feat(auth): show last used auth provider on sign-in page (#12737)

This commit is contained in:
YuTengjing
2026-03-06 03:29:31 +08:00
committed by GitHub
parent 07f44e2ba2
commit 92cb759c37
6 changed files with 67 additions and 23 deletions

View File

@@ -86,6 +86,7 @@
"betterAuth.signin.forgotPasswordSent": "Password reset link sent, please check your email",
"betterAuth.signin.invalidReferralCodeContent": "The referral code \"{{code}}\" you used is invalid or expired. Do you want to continue signing in?",
"betterAuth.signin.invalidReferralCodeTitle": "Invalid Referral Code",
"betterAuth.signin.lastUsed": "Last used",
"betterAuth.signin.magicLinkButton": "Send sign-in link",
"betterAuth.signin.magicLinkError": "Failed to send sign-in link, please try again later",
"betterAuth.signin.magicLinkSent": "Sign-in link sent, please check your email",

View File

@@ -86,6 +86,7 @@
"betterAuth.signin.forgotPasswordSent": "已发送重置链接,请检查邮箱",
"betterAuth.signin.invalidReferralCodeContent": "您使用的邀请码 \"{{code}}\" 无效或已过期。是否继续登录?",
"betterAuth.signin.invalidReferralCodeTitle": "无效的邀请码",
"betterAuth.signin.lastUsed": "上次使用",
"betterAuth.signin.magicLinkButton": "发送登录链接",
"betterAuth.signin.magicLinkError": "发送遇到了问题。你可以稍后再试",
"betterAuth.signin.magicLinkSent": "登录链接已发送,请查收邮箱",

View File

@@ -1,7 +1,7 @@
import { BRANDING_NAME } from '@lobechat/business-const';
import { Alert, Button, Flexbox, Icon, Input, Skeleton, Text } from '@lobehub/ui';
import { type FormInstance, type InputRef } from 'antd';
import { Divider, Form } from 'antd';
import { Badge, Divider, Form } from 'antd';
import { createStaticStyles } from 'antd-style';
import { ChevronRight, Mail } from 'lucide-react';
import { useEffect, useRef } from 'react';
@@ -27,6 +27,7 @@ export interface SignInEmailStepProps {
disableEmailPassword?: boolean;
form: FormInstance<{ email: string }>;
isSocialOnly: boolean;
lastAuthProvider?: string | null;
loading: boolean;
oAuthSSOProviders: string[];
onCheckUser: (values: { email: string }) => Promise<void>;
@@ -40,6 +41,7 @@ export const SignInEmailStep = ({
disableEmailPassword,
form,
isSocialOnly,
lastAuthProvider,
loading,
oAuthSSOProviders,
serverConfigInit,
@@ -114,27 +116,41 @@ export const SignInEmailStep = ({
)}
{serverConfigInit && oAuthSSOProviders.length > 0 && (
<Flexbox gap={12}>
{oAuthSSOProviders.map((provider) => (
<Button
block
key={provider}
loading={socialLoading === provider}
size="large"
icon={
<Icon
icon={AuthIcons(provider, 18)}
style={{
left: 12,
position: 'absolute',
top: 13,
}}
/>
}
onClick={() => onSocialSignIn(provider)}
>
{getProviderLabel(provider)}
</Button>
))}
{oAuthSSOProviders.map((provider) => {
const button = (
<Button
block
key={provider}
loading={socialLoading === provider}
size="large"
icon={
<Icon
icon={AuthIcons(provider, 18)}
style={{
left: 12,
position: 'absolute',
top: 13,
}}
/>
}
onClick={() => onSocialSignIn(provider)}
>
{getProviderLabel(provider)}
</Button>
);
return provider === lastAuthProvider ? (
<Badge.Ribbon
color="var(--ant-color-info-fill-tertiary)"
key={provider}
styles={{ content: { color: 'var(--ant-color-info)' } }}
text={t('betterAuth.signin.lastUsed')}
>
{button}
</Badge.Ribbon>
) : (
button
);
})}
{!disableEmailPassword && divider}
</Flexbox>
)}

View File

@@ -19,6 +19,7 @@ const SignInPage = () => {
handleSignIn,
handleSocialSignIn,
isSocialOnly,
lastAuthProvider,
loading,
oAuthSSOProviders,
serverConfigInit,
@@ -33,6 +34,7 @@ const SignInPage = () => {
disableEmailPassword={disableEmailPassword}
form={form as any}
isSocialOnly={isSocialOnly}
lastAuthProvider={lastAuthProvider}
loading={loading}
oAuthSSOProviders={oAuthSSOProviders}
serverConfigInit={serverConfigInit}

View File

@@ -14,6 +14,8 @@ import { isBuiltinProvider, normalizeProviderId } from '@/libs/better-auth/utils
import { useAuthServerConfigStore } from '../_layout/AuthServerConfigProvider';
import { EMAIL_REGEX, USERNAME_REGEX } from './SignInEmailStep';
const LAST_AUTH_PROVIDER_KEY = 'lobehub:auth:last-provider:v1';
type Step = 'email' | 'password';
interface SignInFormValues {
@@ -40,6 +42,13 @@ export const useSignIn = () => {
const [step, setStep] = useState<Step>('email');
const [email, setEmail] = useState('');
const [isSocialOnly, setIsSocialOnly] = useState(false);
const [lastAuthProvider] = useState(() => {
try {
return localStorage.getItem(LAST_AUTH_PROVIDER_KEY);
} catch {
return null;
}
});
const serverConfigInit = useAuthServerConfigStore((s) => s.serverConfigInit);
const oAuthSSOProviders = useAuthServerConfigStore((s) => s.serverConfig.oAuthSSOProviders) || [];
const { ssoProviders, preSocialSigninCheck, getAdditionalData } = useBusinessSignin();
@@ -194,6 +203,10 @@ export const useSignIn = () => {
return;
}
try {
localStorage.setItem(LAST_AUTH_PROVIDER_KEY, provider);
} catch {}
const callbackUrl = searchParams.get('callbackUrl') || '/';
const additionalData = await getAdditionalData();
const result = isBuiltinProvider(normalizedProvider)
@@ -243,6 +256,15 @@ export const useSignIn = () => {
}
};
const resolvedProviders = ENABLE_BUSINESS_FEATURES ? ssoProviders : oAuthSSOProviders;
const sortedProviders = lastAuthProvider
? [...resolvedProviders].sort((a, b) => {
if (a === lastAuthProvider) return -1;
if (b === lastAuthProvider) return 1;
return 0;
})
: resolvedProviders;
return {
disableEmailPassword,
email,
@@ -254,8 +276,9 @@ export const useSignIn = () => {
handleSignIn,
handleSocialSignIn,
isSocialOnly,
lastAuthProvider,
loading,
oAuthSSOProviders: ENABLE_BUSINESS_FEATURES ? ssoProviders : oAuthSSOProviders,
oAuthSSOProviders: sortedProviders,
serverConfigInit: ENABLE_BUSINESS_FEATURES ? true : serverConfigInit,
socialLoading,
step,

View File

@@ -92,6 +92,7 @@ export default {
'betterAuth.signin.magicLinkButton': 'Send sign-in link',
'betterAuth.signin.magicLinkError': 'Failed to send sign-in link, please try again later',
'betterAuth.signin.magicLinkSent': 'Sign-in link sent, please check your email',
'betterAuth.signin.lastUsed': 'Last used',
'betterAuth.signin.nextStep': 'Next',
'betterAuth.signin.noAccount': "Don't have an account?",
'betterAuth.signin.orContinueWith': 'OR',