diff --git a/auth/dbc/models.go b/auth/dbc/models.go index ed347d58..2cc65f4b 100644 --- a/auth/dbc/models.go +++ b/auth/dbc/models.go @@ -39,7 +39,7 @@ type OidcLogin struct { Opaque string `json:"opaque"` Provider string `json:"provider"` RedirectUrl string `json:"redirectUrl"` - Tenant *string `json:"tenant"` + Tenant string `json:"tenant"` Code *string `json:"code"` CreatedAt time.Time `json:"createdAt"` } diff --git a/auth/dbc/oidc.sql.go b/auth/dbc/oidc.sql.go index 60045aea..2764282a 100644 --- a/auth/dbc/oidc.sql.go +++ b/auth/dbc/oidc.sql.go @@ -33,9 +33,9 @@ returning ` type ConsumeOidcLoginParams struct { - Opaque string `json:"opaque"` - Provider string `json:"provider"` - Tenant *string `json:"tenant"` + Opaque string `json:"opaque"` + Provider string `json:"provider"` + Tenant string `json:"tenant"` } func (q *Queries) ConsumeOidcLogin(ctx context.Context, arg ConsumeOidcLoginParams) (OidcLogin, error) { @@ -62,10 +62,10 @@ returning ` type CreateOidcLoginParams struct { - Provider string `json:"provider"` - Opaque string `json:"opaque"` - RedirectUrl string `json:"redirectUrl"` - Tenant *string `json:"tenant"` + Provider string `json:"provider"` + Opaque string `json:"opaque"` + RedirectUrl string `json:"redirectUrl"` + Tenant string `json:"tenant"` } func (q *Queries) CreateOidcLogin(ctx context.Context, arg CreateOidcLoginParams) (OidcLogin, error) { diff --git a/auth/oidc.go b/auth/oidc.go index 97c3507e..b27c38ec 100644 --- a/auth/oidc.go +++ b/auth/oidc.go @@ -55,11 +55,6 @@ func (h *Handler) OidcLogin(c *echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, "Missing redirectUrl") } - var tenant *string - if t := c.QueryParam("tenant"); t != "" { - tenant = &t - } - opaque := make([]byte, 64) _, err = rand.Read(opaque) if err != nil { @@ -70,7 +65,7 @@ func (h *Handler) OidcLogin(c *echo.Context) error { Provider: provider.Id, Opaque: base64.RawURLEncoding.EncodeToString(opaque), RedirectUrl: redirectURL, - Tenant: tenant, + Tenant: c.QueryParam("tenant"), }) if err != nil { return err @@ -147,6 +142,7 @@ func (h *Handler) OidcLogged(c *echo.Context) error { return echo.NewHTTPError(http.StatusInternalServerError, "Invalid OIDC redirect URL") } params := ret.Query() + params.Set("provider", provider.Id) params.Set("token", login.Opaque) params.Set("error", providerErr) ret.RawQuery = params.Encode() @@ -171,15 +167,11 @@ func (h *Handler) OidcCallback(c *echo.Context) error { if err != nil { return err } - var tenant *string - if t := c.QueryParam("tenant"); t != "" { - tenant = &t - } login, err := h.db.ConsumeOidcLogin(ctx, dbc.ConsumeOidcLoginParams{ Opaque: c.QueryParam("token"), Provider: provider.Id, - Tenant: tenant, + Tenant: c.QueryParam("tenant"), }) if err == pgx.ErrNoRows { return echo.NewHTTPError(http.StatusGone, "Login token expired or already used") @@ -400,8 +392,13 @@ func (h *Handler) CreateUserByOidc( return err } + username := strings.ReplaceAll(profile.Username, "@", "-") + if len(username) > 256 { + username = username[:256] + } + user, err = h.db.CreateUser(ctx, dbc.CreateUserParams{ - Username: strings.TrimSpace(strings.ReplaceAll(profile.Username, "@", "-"))[:256], + Username: username, Email: profile.Email, Password: nil, Claims: h.config.DefaultClaims, diff --git a/auth/sql/migrations/000004_oidc.up.sql b/auth/sql/migrations/000004_oidc.up.sql index 25b68165..38ab641a 100644 --- a/auth/sql/migrations/000004_oidc.up.sql +++ b/auth/sql/migrations/000004_oidc.up.sql @@ -6,7 +6,7 @@ create table keibi.oidc_login( opaque varchar(128) not null unique, provider varchar(256) not null, redirect_url text not null, - tenant text, + tenant varchar(256) not null, code text, created_at timestamptz not null default now()::timestamptz ); diff --git a/auth/users.go b/auth/users.go index a308e3a6..ca20151e 100644 --- a/auth/users.go +++ b/auth/users.go @@ -74,7 +74,7 @@ func MapDbUser(user *dbc.User) User { CreatedDate: user.CreatedDate, LastSeen: user.LastSeen, Claims: user.Claims, - Oidc: nil, + Oidc: make(map[string]OidcHandle), } } diff --git a/front/src/primitives/button.tsx b/front/src/primitives/button.tsx index 5d5b3251..6d1df9e1 100644 --- a/front/src/primitives/button.tsx +++ b/front/src/primitives/button.tsx @@ -1,4 +1,10 @@ -import type { ComponentProps, ComponentType, ReactElement, Ref } from "react"; +import type { + ComponentProps, + ComponentType, + ReactElement, + ReactNode, + Ref, +} from "react"; import { type Falsy, type PressableProps, View } from "react-native"; import { cn } from "~/utils"; import { Icon } from "./icons"; @@ -14,6 +20,7 @@ export const Button = ({ disabled, as, ref, + children, className, ...props }: { @@ -23,6 +30,7 @@ export const Button = ({ left?: ReactElement | Falsy; ricon?: ComponentProps["icon"] | Falsy; right?: ReactElement | Falsy; + children?: ReactNode; ref?: Ref; className?: string; as?: ComponentType; @@ -34,34 +42,33 @@ export const Button = ({ disabled={disabled} className={cn( "flex-row items-center justify-center overflow-hidden", - "rounded-4xl border-3 border-accent p-1 outline-0", + "rounded-4xl border-3 border-accent p-1 px-6 outline-0", disabled && "border-slate-600", "group focus-within:bg-accent hover:bg-accent", className, )} {...(props as AsProps)} > - - {icon && ( - - )} - {left} - {text && ( -

- {text} -

- )} - {right} - {ricon && ( - - )} -
+ {icon && ( + + )} + {left} + {text && ( +

+ {text} +

+ )} + {children} + {right} + {ricon && ( + + )} ); }; diff --git a/front/src/primitives/links.tsx b/front/src/primitives/links.tsx index f91fb3bf..a017a4aa 100644 --- a/front/src/primitives/links.tsx +++ b/front/src/primitives/links.tsx @@ -34,7 +34,7 @@ export function useLinkTo({ e?.preventDefault(); if (href.startsWith("http")) { Platform.OS === "web" - ? window.open(href, "_blank") + ? window.open(href, replace ? "_self" : "_blank") : Linking.openURL(href); } else { replace ? router.replace(href) : router.push(href); diff --git a/front/src/query/fetch.tsx b/front/src/query/fetch.tsx index 1e8feeac..0cddd6a5 100644 --- a/front/src/query/fetch.tsx +++ b/front/src/query/fetch.tsx @@ -13,5 +13,5 @@ export const Fetch = ({ const { data } = useFetch(query); if (!data) return ; - return ; + return Render(data); }; diff --git a/front/src/ui/login/logic.tsx b/front/src/ui/login/logic.tsx index b5bb8a39..872fc0cb 100644 --- a/front/src/ui/login/logic.tsx +++ b/front/src/ui/login/logic.tsx @@ -60,7 +60,7 @@ export const oidcLogin = async ( apiUrl ??= defaultApiUrl; try { const { token } = await queryFn({ - method: "POST", + method: "GET", url: `${apiUrl}/auth/oidc/callback/${provider}?token=${code}`, authToken: null, parser: z.object({ token: z.string() }), diff --git a/front/src/ui/login/login.tsx b/front/src/ui/login/login.tsx index eb0ad04e..8dfcbfd3 100644 --- a/front/src/ui/login/login.tsx +++ b/front/src/ui/login/login.tsx @@ -16,7 +16,7 @@ export const LoginPage = () => { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const params = useLocalSearchParams(); - const [error, setError] = useState(params.error as string | null); + const [error, setError] = useState(params.error as string | undefined); const { t } = useTranslation(); const router = useRouter(); @@ -26,7 +26,7 @@ export const LoginPage = () => { return (

{t("login.login")}

- +

{t("login.username")}

{ const [apiUrl] = useQueryState("apiUrl", undefined!); diff --git a/front/src/ui/login/oidc.tsx b/front/src/ui/login/oidc.tsx index cd5f8675..c8333b1c 100644 --- a/front/src/ui/login/oidc.tsx +++ b/front/src/ui/login/oidc.tsx @@ -8,9 +8,11 @@ import { Fetch, type QueryIdentifier } from "~/query"; export const OidcLogin = ({ apiUrl, children, + error, }: { apiUrl: string; children: ReactNode; + error?: string; }) => { const { t } = useTranslation(); @@ -36,6 +38,7 @@ export const OidcLogin = ({ as={Link} key={id} href={provider.link} + replace className="w-full sm:w-3/4" left={ provider.logo ? ( @@ -50,19 +53,20 @@ export const OidcLogin = ({ /> ))}
- {info.allowRegister && or} + {info.allowRegister + ? or + : error && ( +

{error}

+ )} )} Loader={() => ( <> {[...Array(3)].map((_, i) => ( - - - + ))} {or} @@ -99,7 +103,7 @@ const AuthInfo = z provider, { ...info, - link: `${x.publicUrl}/auth/oidc/login/${provider}?redirectUrl=${baseUrl}/login/callback`, + link: `${x.publicUrl}/auth/oidc/login/${provider}?redirectUrl=${baseUrl}/oidc-callback?apiUrl=${x.publicUrl}`, }, ]), ),