mirror of
https://github.com/willchen96/mike.git
synced 2026-06-30 21:59:37 +02:00
feat: implement multi-factor authentication (MFA) setup and verification flow
- Add SecurityPage component for managing MFA settings, including enrollment and verification. - Create MfaLoginGate to handle MFA verification state during login. - Develop MfaVerificationPopup for user input of verification codes. - Implement VerifyMfaPage for the MFA verification process after login. - Introduce reusable VerificationCodeInput component for entering verification codes. - Integrate Supabase MFA API for managing factors and verification. - Add loading states and error handling for a better user experience.
This commit is contained in:
parent
15c96b0dd4
commit
3a10943200
32 changed files with 3704 additions and 311 deletions
|
|
@ -14,7 +14,9 @@ import {
|
|||
type ApiKeyProvider,
|
||||
type UserProfile as ApiUserProfile,
|
||||
getUserProfile,
|
||||
isMfaRequiredError,
|
||||
saveApiKey,
|
||||
updateUserMfaOnLogin,
|
||||
updateUserProfile,
|
||||
} from "@/app/lib/mikeApi";
|
||||
|
||||
|
|
@ -27,6 +29,7 @@ interface UserProfile {
|
|||
tier: string;
|
||||
titleModel: string;
|
||||
tabularModel: string;
|
||||
mfaOnLogin: boolean;
|
||||
apiKeys: ApiKeyState;
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +42,7 @@ interface UserProfileContextType {
|
|||
field: "titleModel" | "tabularModel",
|
||||
value: string,
|
||||
) => Promise<boolean>;
|
||||
updateMfaOnLogin: (enabled: boolean) => Promise<boolean>;
|
||||
updateApiKey: (
|
||||
provider: ApiKeyProvider,
|
||||
value: string | null,
|
||||
|
|
@ -83,6 +87,7 @@ function toProfile(data: ApiUserProfile): UserProfile {
|
|||
|
||||
return {
|
||||
...profile,
|
||||
mfaOnLogin: profile.mfaOnLogin === true,
|
||||
apiKeys,
|
||||
};
|
||||
}
|
||||
|
|
@ -111,6 +116,7 @@ export function UserProfileProvider({ children }: { children: ReactNode }) {
|
|||
tier: "Free",
|
||||
titleModel: "gemini-3.1-flash-lite-preview",
|
||||
tabularModel: "gemini-3-flash-preview",
|
||||
mfaOnLogin: false,
|
||||
apiKeys: emptyApiKeys(),
|
||||
});
|
||||
} finally {
|
||||
|
|
@ -156,7 +162,8 @@ export function UserProfileProvider({ children }: { children: ReactNode }) {
|
|||
prev ? { ...prev, ...toProfile(updated) } : null,
|
||||
);
|
||||
return true;
|
||||
} catch {
|
||||
} catch (error) {
|
||||
if (isMfaRequiredError(error)) throw error;
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
|
@ -184,6 +191,23 @@ export function UserProfileProvider({ children }: { children: ReactNode }) {
|
|||
[user],
|
||||
);
|
||||
|
||||
const updateMfaOnLogin = useCallback(
|
||||
async (enabled: boolean): Promise<boolean> => {
|
||||
if (!user) return false;
|
||||
try {
|
||||
const updated = await updateUserMfaOnLogin(enabled);
|
||||
setProfile((prev) =>
|
||||
prev ? { ...prev, ...toProfile(updated) } : null,
|
||||
);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (isMfaRequiredError(error)) throw error;
|
||||
return false;
|
||||
}
|
||||
},
|
||||
[user],
|
||||
);
|
||||
|
||||
const updateApiKey = useCallback(
|
||||
async (
|
||||
provider: ApiKeyProvider,
|
||||
|
|
@ -208,7 +232,8 @@ export function UserProfileProvider({ children }: { children: ReactNode }) {
|
|||
: null,
|
||||
);
|
||||
return true;
|
||||
} catch {
|
||||
} catch (error) {
|
||||
if (isMfaRequiredError(error)) throw error;
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
|
@ -242,6 +267,7 @@ export function UserProfileProvider({ children }: { children: ReactNode }) {
|
|||
updateDisplayName,
|
||||
updateOrganisation,
|
||||
updateModelPreference,
|
||||
updateMfaOnLogin,
|
||||
updateApiKey,
|
||||
reloadProfile,
|
||||
incrementMessageCredits,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue