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 {
|
interface SignInPageProps {
|
||||||
searchParams: Promise<{
|
searchParams: Promise<{
|
||||||
invite_token?: string;
|
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) {
|
async function SignInPage({ searchParams }: SignInPageProps) {
|
||||||
const inviteToken = (await searchParams).invite_token;
|
const { invite_token: inviteToken, next = '' } = await searchParams;
|
||||||
|
|
||||||
const signUpPath =
|
const signUpPath =
|
||||||
pathsConfig.auth.signUp +
|
pathsConfig.auth.signUp +
|
||||||
(inviteToken ? `?invite_token=${inviteToken}` : '');
|
(inviteToken ? `?invite_token=${inviteToken}` : '');
|
||||||
|
|
||||||
|
const paths = {
|
||||||
|
callback: pathsConfig.auth.callback,
|
||||||
|
returnPath: next ?? pathsConfig.app.home,
|
||||||
|
joinTeam: pathsConfig.app.joinTeam,
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={'flex flex-col items-center gap-1'}>
|
<div className={'flex flex-col items-center gap-1'}>
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
import { useCallback } from 'react';
|
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 { useSignInWithProvider } from '@kit/supabase/hooks/use-sign-in-with-provider';
|
||||||
import { If } from '@kit/ui/if';
|
import { If } from '@kit/ui/if';
|
||||||
@@ -23,19 +26,21 @@ import { AuthProviderButton } from './auth-provider-button';
|
|||||||
*/
|
*/
|
||||||
const OAUTH_SCOPES: Partial<Record<Provider, string>> = {
|
const OAUTH_SCOPES: Partial<Record<Provider, string>> = {
|
||||||
azure: 'email',
|
azure: 'email',
|
||||||
|
keycloak: 'openid',
|
||||||
// add your OAuth providers here
|
// add your OAuth providers here
|
||||||
};
|
};
|
||||||
|
|
||||||
export function OauthProviders(props: {
|
export const OauthProviders: React.FC<{
|
||||||
inviteToken?: string;
|
inviteToken?: string;
|
||||||
shouldCreateUser: boolean;
|
shouldCreateUser: boolean;
|
||||||
enabledProviders: Provider[];
|
enabledProviders: Provider[];
|
||||||
|
queryParams?: Record<string, string>;
|
||||||
|
|
||||||
paths: {
|
paths: {
|
||||||
callback: string;
|
callback: string;
|
||||||
returnPath: string;
|
returnPath: string;
|
||||||
};
|
};
|
||||||
}) {
|
}> = (props) => {
|
||||||
const signInWithProviderMutation = useSignInWithProvider();
|
const signInWithProviderMutation = useSignInWithProvider();
|
||||||
|
|
||||||
// we make the UI "busy" until the next page is fully loaded
|
// we make the UI "busy" until the next page is fully loaded
|
||||||
@@ -46,7 +51,7 @@ export function OauthProviders(props: {
|
|||||||
const credential = await signInRequest();
|
const credential = await signInRequest();
|
||||||
|
|
||||||
if (!credential) {
|
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('?');
|
].join('?');
|
||||||
|
|
||||||
const redirectTo = [origin, redirectPath].join('');
|
const redirectTo = [origin, redirectPath].join('');
|
||||||
const scopesOpts = OAUTH_SCOPES[provider] ?? {};
|
const scopes = OAUTH_SCOPES[provider] ?? undefined;
|
||||||
|
|
||||||
const credentials = {
|
const credentials = {
|
||||||
provider,
|
provider,
|
||||||
options: {
|
options: {
|
||||||
shouldCreateUser: props.shouldCreateUser,
|
|
||||||
redirectTo,
|
redirectTo,
|
||||||
...scopesOpts,
|
queryParams: props.queryParams,
|
||||||
|
scopes,
|
||||||
},
|
},
|
||||||
};
|
} satisfies SignInWithOAuthCredentials;
|
||||||
|
|
||||||
return onSignInWithProvider(() =>
|
return onSignInWithProvider(() =>
|
||||||
signInWithProviderMutation.mutateAsync(credentials),
|
signInWithProviderMutation.mutateAsync(credentials),
|
||||||
@@ -120,7 +125,7 @@ export function OauthProviders(props: {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
function getProviderName(providerId: string) {
|
function getProviderName(providerId: string) {
|
||||||
const capitalize = (value: string) =>
|
const capitalize = (value: string) =>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useRouter, useSearchParams } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
import type { Provider } from '@supabase/supabase-js';
|
import type { Provider } from '@supabase/supabase-js';
|
||||||
|
|
||||||
@@ -18,8 +18,8 @@ export function SignInMethodsContainer(props: {
|
|||||||
|
|
||||||
paths: {
|
paths: {
|
||||||
callback: string;
|
callback: string;
|
||||||
home: string;
|
|
||||||
joinTeam: string;
|
joinTeam: string;
|
||||||
|
returnPath: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
providers: {
|
providers: {
|
||||||
@@ -29,13 +29,13 @@ export function SignInMethodsContainer(props: {
|
|||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const nextPath = useSearchParams().get('next') ?? props.paths.home;
|
|
||||||
|
|
||||||
const redirectUrl = isBrowser()
|
const redirectUrl = isBrowser()
|
||||||
? new URL(props.paths.callback, window?.location.origin).toString()
|
? new URL(props.paths.callback, window?.location.origin).toString()
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const onSignIn = () => {
|
const onSignIn = () => {
|
||||||
|
// if the user has an invite token, we should join the team
|
||||||
if (props.inviteToken) {
|
if (props.inviteToken) {
|
||||||
const searchParams = new URLSearchParams({
|
const searchParams = new URLSearchParams({
|
||||||
invite_token: props.inviteToken,
|
invite_token: props.inviteToken,
|
||||||
@@ -45,7 +45,8 @@ export function SignInMethodsContainer(props: {
|
|||||||
|
|
||||||
router.replace(joinTeamPath);
|
router.replace(joinTeamPath);
|
||||||
} else {
|
} 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}
|
shouldCreateUser={false}
|
||||||
paths={{
|
paths={{
|
||||||
callback: props.paths.callback,
|
callback: props.paths.callback,
|
||||||
returnPath: props.paths.home,
|
returnPath: props.paths.returnPath,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
|
|||||||
Reference in New Issue
Block a user