feat(auth): add invite token handling to OTP sign-in and update metho… (#280)
* feat(auth): add invite token handling to OTP sign-in and update method descriptions
This commit is contained in:
committed by
GitHub
parent
f95da27b8f
commit
1143a01727
@@ -76,6 +76,7 @@
|
|||||||
"methodMagicLink": "email link",
|
"methodMagicLink": "email link",
|
||||||
"methodOauth": "social sign-in",
|
"methodOauth": "social sign-in",
|
||||||
"methodOauthWithProvider": "<provider>{{provider}}</provider>",
|
"methodOauthWithProvider": "<provider>{{provider}}</provider>",
|
||||||
|
"methodDefault": "another method",
|
||||||
"existingAccountHint": "You previously signed in with <method>{{method}}</method>. <signInLink>Already have an account?</signInLink>",
|
"existingAccountHint": "You previously signed in with <method>{{method}}</method>. <signInLink>Already have an account?</signInLink>",
|
||||||
"errors": {
|
"errors": {
|
||||||
"Invalid login credentials": "The credentials entered are invalid",
|
"Invalid login credentials": "The credentials entered are invalid",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useMemo } from 'react';
|
|||||||
|
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
import { UserCheck } from 'lucide-react';
|
import { UserCheck } from 'lucide-react';
|
||||||
|
|
||||||
@@ -33,6 +34,14 @@ export function ExistingAccountHintImpl({
|
|||||||
const { hasLastMethod, methodType, providerName, isOAuth } =
|
const { hasLastMethod, methodType, providerName, isOAuth } =
|
||||||
useLastAuthMethod();
|
useLastAuthMethod();
|
||||||
|
|
||||||
|
const params = useSearchParams();
|
||||||
|
|
||||||
|
const isInvite = params.get('invite_token');
|
||||||
|
|
||||||
|
if (isInvite) {
|
||||||
|
signInPath = signInPath + '?invite_token=' + isInvite;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the appropriate method description for the hint
|
// Get the appropriate method description for the hint
|
||||||
// This must be called before any conditional returns to follow Rules of Hooks
|
// This must be called before any conditional returns to follow Rules of Hooks
|
||||||
const methodDescription = useMemo(() => {
|
const methodDescription = useMemo(() => {
|
||||||
@@ -42,13 +51,13 @@ export function ExistingAccountHintImpl({
|
|||||||
|
|
||||||
switch (methodType) {
|
switch (methodType) {
|
||||||
case 'password':
|
case 'password':
|
||||||
return 'email and password';
|
return 'auth:methodPassword';
|
||||||
case 'otp':
|
case 'otp':
|
||||||
return 'email verification';
|
return 'auth:methodOtp';
|
||||||
case 'magic_link':
|
case 'magic_link':
|
||||||
return 'email link';
|
return 'auth:methodMagicLink';
|
||||||
default:
|
default:
|
||||||
return 'another method';
|
return 'auth:methodDefault';
|
||||||
}
|
}
|
||||||
}, [methodType, isOAuth, providerName]);
|
}, [methodType, isOAuth, providerName]);
|
||||||
|
|
||||||
|
|||||||
@@ -34,17 +34,16 @@ import { AuthErrorAlert } from './auth-error-alert';
|
|||||||
const EmailSchema = z.object({ email: z.string().email() });
|
const EmailSchema = z.object({ email: z.string().email() });
|
||||||
const OtpSchema = z.object({ token: z.string().min(6).max(6) });
|
const OtpSchema = z.object({ token: z.string().min(6).max(6) });
|
||||||
|
|
||||||
export function OtpSignInContainer({
|
type OtpSignInContainerProps = {
|
||||||
onSignIn,
|
|
||||||
shouldCreateUser,
|
|
||||||
}: {
|
|
||||||
onSignIn?: (userId?: string) => void;
|
|
||||||
shouldCreateUser: boolean;
|
shouldCreateUser: boolean;
|
||||||
}) {
|
inviteToken?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function OtpSignInContainer(props: OtpSignInContainerProps) {
|
||||||
const verifyMutation = useVerifyOtp();
|
const verifyMutation = useVerifyOtp();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useSearchParams();
|
|
||||||
const { recordAuthMethod } = useLastAuthMethod();
|
const { recordAuthMethod } = useLastAuthMethod();
|
||||||
|
const params = useSearchParams();
|
||||||
|
|
||||||
const otpForm = useForm({
|
const otpForm = useForm({
|
||||||
resolver: zodResolver(OtpSchema.merge(EmailSchema)),
|
resolver: zodResolver(OtpSchema.merge(EmailSchema)),
|
||||||
@@ -61,6 +60,9 @@ export function OtpSignInContainer({
|
|||||||
|
|
||||||
const isEmailStep = !email;
|
const isEmailStep = !email;
|
||||||
|
|
||||||
|
const shouldCreateUser =
|
||||||
|
'shouldCreateUser' in props && props.shouldCreateUser;
|
||||||
|
|
||||||
const handleVerifyOtp = async ({
|
const handleVerifyOtp = async ({
|
||||||
token,
|
token,
|
||||||
email,
|
email,
|
||||||
@@ -68,7 +70,7 @@ export function OtpSignInContainer({
|
|||||||
token: string;
|
token: string;
|
||||||
email: string;
|
email: string;
|
||||||
}) => {
|
}) => {
|
||||||
const result = await verifyMutation.mutateAsync({
|
await verifyMutation.mutateAsync({
|
||||||
type: 'email',
|
type: 'email',
|
||||||
email,
|
email,
|
||||||
token,
|
token,
|
||||||
@@ -77,14 +79,18 @@ export function OtpSignInContainer({
|
|||||||
// Record successful OTP sign-in
|
// Record successful OTP sign-in
|
||||||
recordAuthMethod('otp', { email });
|
recordAuthMethod('otp', { email });
|
||||||
|
|
||||||
if (onSignIn) {
|
// on sign ups we redirect to the app home
|
||||||
return onSignIn(result?.user?.id);
|
const inviteToken = props.inviteToken;
|
||||||
}
|
const next = params.get('next') ?? '/home';
|
||||||
|
|
||||||
// on sign ups we redirect to the app home
|
if (inviteToken) {
|
||||||
if (shouldCreateUser) {
|
const params = new URLSearchParams({
|
||||||
const next = params.get('next') ?? '/home';
|
invite_token: inviteToken,
|
||||||
|
next,
|
||||||
|
});
|
||||||
|
|
||||||
|
router.replace(`/join?${params.toString()}`);
|
||||||
|
} else {
|
||||||
router.replace(next);
|
router.replace(next);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -74,7 +74,10 @@ export function SignInMethodsContainer(props: {
|
|||||||
</If>
|
</If>
|
||||||
|
|
||||||
<If condition={props.providers.otp}>
|
<If condition={props.providers.otp}>
|
||||||
<OtpSignInContainer shouldCreateUser={false} onSignIn={onSignIn} />
|
<OtpSignInContainer
|
||||||
|
inviteToken={props.inviteToken}
|
||||||
|
shouldCreateUser={false}
|
||||||
|
/>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
<If condition={props.providers.oAuth.length}>
|
<If condition={props.providers.oAuth.length}>
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ export function SignUpMethodsContainer(props: {
|
|||||||
</If>
|
</If>
|
||||||
|
|
||||||
<If condition={props.providers.otp}>
|
<If condition={props.providers.otp}>
|
||||||
<OtpSignInContainer shouldCreateUser={true} />
|
<OtpSignInContainer
|
||||||
|
inviteToken={props.inviteToken}
|
||||||
|
shouldCreateUser={true}
|
||||||
|
/>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
<If condition={props.providers.magicLink}>
|
<If condition={props.providers.magicLink}>
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export function usePasswordSignUpFlow({
|
|||||||
router,
|
router,
|
||||||
onSignUp,
|
onSignUp,
|
||||||
resetCaptchaToken,
|
resetCaptchaToken,
|
||||||
|
recordAuthMethod,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user