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:
Giancarlo Buomprisco
2025-06-14 14:31:30 +07:00
committed by GitHub
parent f95da27b8f
commit 1143a01727
6 changed files with 43 additions and 20 deletions

View File

@@ -76,6 +76,7 @@
"methodMagicLink": "email link",
"methodOauth": "social sign-in",
"methodOauthWithProvider": "<provider>{{provider}}</provider>",
"methodDefault": "another method",
"existingAccountHint": "You previously signed in with <method>{{method}}</method>. <signInLink>Already have an account?</signInLink>",
"errors": {
"Invalid login credentials": "The credentials entered are invalid",

View File

@@ -4,6 +4,7 @@ import { useMemo } from 'react';
import dynamic from 'next/dynamic';
import Link from 'next/link';
import { useSearchParams } from 'next/navigation';
import { UserCheck } from 'lucide-react';
@@ -33,6 +34,14 @@ export function ExistingAccountHintImpl({
const { hasLastMethod, methodType, providerName, isOAuth } =
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
// This must be called before any conditional returns to follow Rules of Hooks
const methodDescription = useMemo(() => {
@@ -42,13 +51,13 @@ export function ExistingAccountHintImpl({
switch (methodType) {
case 'password':
return 'email and password';
return 'auth:methodPassword';
case 'otp':
return 'email verification';
return 'auth:methodOtp';
case 'magic_link':
return 'email link';
return 'auth:methodMagicLink';
default:
return 'another method';
return 'auth:methodDefault';
}
}, [methodType, isOAuth, providerName]);

View File

@@ -34,17 +34,16 @@ import { AuthErrorAlert } from './auth-error-alert';
const EmailSchema = z.object({ email: z.string().email() });
const OtpSchema = z.object({ token: z.string().min(6).max(6) });
export function OtpSignInContainer({
onSignIn,
shouldCreateUser,
}: {
onSignIn?: (userId?: string) => void;
type OtpSignInContainerProps = {
shouldCreateUser: boolean;
}) {
inviteToken?: string;
};
export function OtpSignInContainer(props: OtpSignInContainerProps) {
const verifyMutation = useVerifyOtp();
const router = useRouter();
const params = useSearchParams();
const { recordAuthMethod } = useLastAuthMethod();
const params = useSearchParams();
const otpForm = useForm({
resolver: zodResolver(OtpSchema.merge(EmailSchema)),
@@ -61,6 +60,9 @@ export function OtpSignInContainer({
const isEmailStep = !email;
const shouldCreateUser =
'shouldCreateUser' in props && props.shouldCreateUser;
const handleVerifyOtp = async ({
token,
email,
@@ -68,7 +70,7 @@ export function OtpSignInContainer({
token: string;
email: string;
}) => {
const result = await verifyMutation.mutateAsync({
await verifyMutation.mutateAsync({
type: 'email',
email,
token,
@@ -77,14 +79,18 @@ export function OtpSignInContainer({
// Record successful OTP sign-in
recordAuthMethod('otp', { email });
if (onSignIn) {
return onSignIn(result?.user?.id);
}
// on sign ups we redirect to the app home
const inviteToken = props.inviteToken;
const next = params.get('next') ?? '/home';
// on sign ups we redirect to the app home
if (shouldCreateUser) {
const next = params.get('next') ?? '/home';
if (inviteToken) {
const params = new URLSearchParams({
invite_token: inviteToken,
next,
});
router.replace(`/join?${params.toString()}`);
} else {
router.replace(next);
}
};

View File

@@ -74,7 +74,10 @@ export function SignInMethodsContainer(props: {
</If>
<If condition={props.providers.otp}>
<OtpSignInContainer shouldCreateUser={false} onSignIn={onSignIn} />
<OtpSignInContainer
inviteToken={props.inviteToken}
shouldCreateUser={false}
/>
</If>
<If condition={props.providers.oAuth.length}>

View File

@@ -51,7 +51,10 @@ export function SignUpMethodsContainer(props: {
</If>
<If condition={props.providers.otp}>
<OtpSignInContainer shouldCreateUser={true} />
<OtpSignInContainer
inviteToken={props.inviteToken}
shouldCreateUser={true}
/>
</If>
<If condition={props.providers.magicLink}>

View File

@@ -87,6 +87,7 @@ export function usePasswordSignUpFlow({
router,
onSignUp,
resetCaptchaToken,
recordAuthMethod,
],
);