docs: imprvoe account linking docs

This commit is contained in:
Bereket Engida
2024-10-20 06:44:16 +03:00
parent 8078b79dcd
commit e7c46e035c
3 changed files with 48 additions and 59 deletions
+17 -8
View File
@@ -122,28 +122,37 @@ The account table stores the authentication data of the user. The account table
### Account linking
Account linking allows users to link multiple authentication methods to the same account. This is useful when users want to sign in using different methods.
Account linking allows users to connect multiple authentication methods to a single account. Better Auth supports linking social sign-on or OAuth providers to an existing account if the provider returns a verified email. If the email isn't verified, the account can't be linked.
By default Better Auth allows account linking for all providers. You can disable account linking by setting `accountLinking.enabled` to `false`.
```ts
const auth = new BetterAuth({
account: {
accountLinking: {
accountLinking: {
enabled: false,
}
}
},
});
```
You can also specify, the list of providers that should be trusted. When a user signs in using a trusted provider, if the provider returns a verfied email, the user is automatically signed in.
You can also specify a list of trusted providers. If a user signs in using a trusted provider, the account is linked, even if the email verification status is not provided.
If account linking is disabled, accounts cannot be linked, regardless of the provider or email verification.
```ts
const auth = new BetterAuth({
account: {
accountLinking: {
accountLinking: {
enabled: true,
trustedProviders: ["email-password", "google"],
}
trustedProviders: ["google", "github"],
}
},
});
```
To link a credential-based account (email and password), users can either go through the "forgot password" flow or use the `setPassword` function if they are already logged in.
```ts
await authClient.user.setPassword({
password,
});
```
+30 -48
View File
@@ -1,4 +1,3 @@
import { APIError } from "better-call";
import { z } from "zod";
import { userSchema } from "../../db/schema";
import { generateId } from "../../utils/id";
@@ -115,7 +114,15 @@ export const callbackOAuth = createAuthEndpoint(
`${c.context.baseURL}/error?error=please_restart_the_process`,
);
}
//find user in db
function redirectOnError(error: string) {
throw c.redirect(
`${
currentURL || callbackURL || `${c.context.baseURL}/error`
}?error=${error}`,
);
}
const dbUser = await c.context.internalAdapter
.findUserByEmail(user.email, {
includeAccounts: true,
@@ -141,17 +148,11 @@ export const callbackOAuth = createAuthEndpoint(
const isTrustedProvider = trustedProviders?.includes(
provider.id as "apple",
);
if (!isTrustedProvider) {
let url: URL;
try {
url = new URL(currentURL || callbackURL);
url.searchParams.set("error", "account_not_linked");
} catch (e) {
throw c.redirect(
`${c.context.baseURL}/error?error=account_not_linked`,
);
}
throw c.redirect(url.toString());
if (
(!isTrustedProvider && !user.emailVerified) ||
!c.context.options.account?.accountLinking?.enabled
) {
redirectOnError("account_not_linked");
}
try {
await c.context.internalAdapter.linkAccount({
@@ -162,10 +163,8 @@ export const callbackOAuth = createAuthEndpoint(
...getAccountTokens(tokens),
});
} catch (e) {
console.log(e);
throw c.redirect(
`${c.context.baseURL}/error?error=failed_linking_account`,
);
logger.error("Unable to link account", e);
redirectOnError("unable_to_link_account");
}
}
} else {
@@ -199,40 +198,23 @@ export const callbackOAuth = createAuthEndpoint(
);
}
} catch (e) {
const url = new URL(currentURL || callbackURL);
url.searchParams.set("error", "unable_to_create_user");
throw c.redirect(url.toString());
logger.error("Unable to create user", e);
redirectOnError("unable_to_create_user");
}
}
//this should never happen
if (!userId && !id)
throw new APIError("INTERNAL_SERVER_ERROR", {
message: "Unable to create user",
});
//create session
try {
const session = await c.context.internalAdapter.createSession(
userId || id,
c.request,
);
if (!session) {
const url = new URL(currentURL || callbackURL);
url.searchParams.set("error", "unable_to_create_session");
throw c.redirect(url.toString());
}
try {
await setSessionCookie(c, session.id);
} catch (e) {
c.context.logger.error("Unable to set session cookie", e);
const url = new URL(currentURL || callbackURL);
url.searchParams.set("error", "unable_to_create_session");
throw c.redirect(url.toString());
}
} catch {
const url = new URL(currentURL || callbackURL || "");
url.searchParams.set("error", "unable_to_create_session");
throw c.redirect(url.toString());
if (!userId && !id) {
redirectOnError("unable_to_create_user");
}
const session = await c.context.internalAdapter.createSession(
userId || id,
c.request,
);
if (!session) {
redirectOnError("unable_to_create_session");
}
await setSessionCookie(c, session.id);
throw c.redirect(callbackURL);
},
);
@@ -1,7 +1,5 @@
import { describe, expect, it, vi } from "vitest";
import { describe, expect, it } from "vitest";
import { getTestInstance } from "../../test-utils/test-instance";
import { createAuthClient } from "../../client";
import { parseSetCookieHeader } from "../../cookies";
describe("updateUser", async () => {
let emailVerificationToken: string;