feat: social auth account linking
This commit is contained in:
+44
-3
@@ -83,10 +83,43 @@ export const auth = {
|
||||
body: JSON.stringify({ email, password, name }),
|
||||
}),
|
||||
|
||||
logIn: (email: string, password: string) =>
|
||||
authFetch<User>("/auth/login", {
|
||||
/**
|
||||
* Submits email/password. Three outcomes:
|
||||
* - kind: "session" → fully signed in; auth-provider sets the user.
|
||||
* - kind: "otp_required" → account has been unverified for >30 days;
|
||||
* a 6-digit code was emailed. Caller must show an OTP form and call
|
||||
* `verifyLoginOTP(email, password, otp)` to complete sign-in.
|
||||
* Throws AuthError on credential / server errors.
|
||||
*/
|
||||
logIn: async (
|
||||
email: string,
|
||||
password: string,
|
||||
): Promise<
|
||||
| { kind: "session"; user: User }
|
||||
| { kind: "otp_required" }
|
||||
> => {
|
||||
const data = await authFetch<User | { otp_required: true }>(
|
||||
"/auth/login",
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify({ email, password }),
|
||||
},
|
||||
);
|
||||
if (data && typeof data === "object" && "otp_required" in data) {
|
||||
return { kind: "otp_required" };
|
||||
}
|
||||
return { kind: "session", user: data as User };
|
||||
},
|
||||
|
||||
/**
|
||||
* Completes a login that was paused by the OTP gate. Re-checks the
|
||||
* password (the OTP alone isn't enough — defence in depth), validates
|
||||
* the code, marks the email verified, and sets the session cookie.
|
||||
*/
|
||||
verifyLoginOTP: (email: string, password: string, otp: string) =>
|
||||
authFetch<User>("/auth/verify-login-otp", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ email, password }),
|
||||
body: JSON.stringify({ email, password, otp }),
|
||||
}),
|
||||
|
||||
logOut: () => authFetch<{ ok: boolean }>("/auth/logout", { method: "POST" }),
|
||||
@@ -133,6 +166,14 @@ export const auth = {
|
||||
body: JSON.stringify({ email }),
|
||||
}),
|
||||
|
||||
/**
|
||||
* Returns the list of OAuth provider names already linked to the
|
||||
* signed-in user (e.g. `["google", "facebook"]`). The settings page
|
||||
* uses this to flip per-provider rows between "Connect" / "Connected".
|
||||
*/
|
||||
linkedProviders: () =>
|
||||
authFetch<{ providers: string[] }>("/auth/linked-providers"),
|
||||
|
||||
/**
|
||||
* Replaces the signed-in user's password. The BFF verifies the current
|
||||
* password, hashes the new one, and re-issues the session cookie so this
|
||||
|
||||
Reference in New Issue
Block a user