Set default oAuth scopes for azure and keycloak. Allow passing custom… (#207)
* Set default oAuth scopes for azure and keycloak. Allow passing custom query parameters from the OauthProviders component. * Pass return path if a next query parameter is provided. Use home path otherwise.
This commit is contained in:
committed by
GitHub
parent
08cd6983f4
commit
b265f596da
@@ -13,6 +13,7 @@ import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
interface SignInPageProps {
|
||||
searchParams: Promise<{
|
||||
invite_token?: string;
|
||||
next?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -24,19 +25,19 @@ export const generateMetadata = async () => {
|
||||
};
|
||||
};
|
||||
|
||||
const paths = {
|
||||
callback: pathsConfig.auth.callback,
|
||||
home: pathsConfig.app.home,
|
||||
joinTeam: pathsConfig.app.joinTeam,
|
||||
};
|
||||
|
||||
async function SignInPage({ searchParams }: SignInPageProps) {
|
||||
const inviteToken = (await searchParams).invite_token;
|
||||
const { invite_token: inviteToken, next = '' } = await searchParams;
|
||||
|
||||
const signUpPath =
|
||||
pathsConfig.auth.signUp +
|
||||
(inviteToken ? `?invite_token=${inviteToken}` : '');
|
||||
|
||||
const paths = {
|
||||
callback: pathsConfig.auth.callback,
|
||||
returnPath: next ?? pathsConfig.app.home,
|
||||
joinTeam: pathsConfig.app.joinTeam,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'flex flex-col items-center gap-1'}>
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import type { Provider } from '@supabase/supabase-js';
|
||||
import type {
|
||||
Provider,
|
||||
SignInWithOAuthCredentials,
|
||||
} from '@supabase/supabase-js';
|
||||
|
||||
import { useSignInWithProvider } from '@kit/supabase/hooks/use-sign-in-with-provider';
|
||||
import { If } from '@kit/ui/if';
|
||||
@@ -23,19 +26,21 @@ import { AuthProviderButton } from './auth-provider-button';
|
||||
*/
|
||||
const OAUTH_SCOPES: Partial<Record<Provider, string>> = {
|
||||
azure: 'email',
|
||||
keycloak: 'openid',
|
||||
// add your OAuth providers here
|
||||
};
|
||||
|
||||
export function OauthProviders(props: {
|
||||
export const OauthProviders: React.FC<{
|
||||
inviteToken?: string;
|
||||
shouldCreateUser: boolean;
|
||||
enabledProviders: Provider[];
|
||||
queryParams?: Record<string, string>;
|
||||
|
||||
paths: {
|
||||
callback: string;
|
||||
returnPath: string;
|
||||
};
|
||||
}) {
|
||||
}> = (props) => {
|
||||
const signInWithProviderMutation = useSignInWithProvider();
|
||||
|
||||
// we make the UI "busy" until the next page is fully loaded
|
||||
@@ -46,7 +51,7 @@ export function OauthProviders(props: {
|
||||
const credential = await signInRequest();
|
||||
|
||||
if (!credential) {
|
||||
return Promise.reject(new Error('Failed to sign in with provider'));
|
||||
return Promise.reject(new Error(`No credential returned`));
|
||||
}
|
||||
},
|
||||
[],
|
||||
@@ -89,16 +94,16 @@ export function OauthProviders(props: {
|
||||
].join('?');
|
||||
|
||||
const redirectTo = [origin, redirectPath].join('');
|
||||
const scopesOpts = OAUTH_SCOPES[provider] ?? {};
|
||||
const scopes = OAUTH_SCOPES[provider] ?? undefined;
|
||||
|
||||
const credentials = {
|
||||
provider,
|
||||
options: {
|
||||
shouldCreateUser: props.shouldCreateUser,
|
||||
redirectTo,
|
||||
...scopesOpts,
|
||||
queryParams: props.queryParams,
|
||||
scopes,
|
||||
},
|
||||
};
|
||||
} satisfies SignInWithOAuthCredentials;
|
||||
|
||||
return onSignInWithProvider(() =>
|
||||
signInWithProviderMutation.mutateAsync(credentials),
|
||||
@@ -120,7 +125,7 @@ export function OauthProviders(props: {
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
function getProviderName(providerId: string) {
|
||||
const capitalize = (value: string) =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import type { Provider } from '@supabase/supabase-js';
|
||||
|
||||
@@ -18,8 +18,8 @@ export function SignInMethodsContainer(props: {
|
||||
|
||||
paths: {
|
||||
callback: string;
|
||||
home: string;
|
||||
joinTeam: string;
|
||||
returnPath: string;
|
||||
};
|
||||
|
||||
providers: {
|
||||
@@ -29,13 +29,13 @@ export function SignInMethodsContainer(props: {
|
||||
};
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const nextPath = useSearchParams().get('next') ?? props.paths.home;
|
||||
|
||||
const redirectUrl = isBrowser()
|
||||
? new URL(props.paths.callback, window?.location.origin).toString()
|
||||
: '';
|
||||
|
||||
const onSignIn = () => {
|
||||
// if the user has an invite token, we should join the team
|
||||
if (props.inviteToken) {
|
||||
const searchParams = new URLSearchParams({
|
||||
invite_token: props.inviteToken,
|
||||
@@ -45,7 +45,8 @@ export function SignInMethodsContainer(props: {
|
||||
|
||||
router.replace(joinTeamPath);
|
||||
} else {
|
||||
router.replace(nextPath);
|
||||
// otherwise, we should redirect to the return path
|
||||
router.replace(props.paths.returnPath);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,7 +83,7 @@ export function SignInMethodsContainer(props: {
|
||||
shouldCreateUser={false}
|
||||
paths={{
|
||||
callback: props.paths.callback,
|
||||
returnPath: props.paths.home,
|
||||
returnPath: props.paths.returnPath,
|
||||
}}
|
||||
/>
|
||||
</If>
|
||||
|
||||
Reference in New Issue
Block a user