Skip to content

Commit f9b9672

Browse files
authored
feat(oauth2): override user info on provider sign-in (#2148)
* feat(oauth2): override user info on provider sign-in * improve email verification handling * resolve mrge * fix(sso): update overrideUserInfo handling to use provider configuration * fix param
1 parent 1c09820 commit f9b9672

File tree

4 files changed

+48
-1
lines changed

4 files changed

+48
-1
lines changed

packages/better-auth/src/oauth2/link-account.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ export async function handleOAuthUserInfo(
1111
account,
1212
callbackURL,
1313
disableSignUp,
14+
overrideUserInfo,
1415
}: {
1516
userInfo: Omit<User, "createdAt" | "updatedAt">;
1617
account: Omit<Account, "id" | "userId" | "createdAt" | "updatedAt">;
1718
callbackURL?: string;
1819
disableSignUp?: boolean;
20+
overrideUserInfo?: boolean;
1921
},
2022
) {
2123
const dbUser = await c.context.internalAdapter
@@ -102,6 +104,17 @@ export async function handleOAuthUserInfo(
102104
);
103105
}
104106
}
107+
if (overrideUserInfo) {
108+
// update user info from the provider if overrideUserInfo is true
109+
await c.context.internalAdapter.updateUser(dbUser.user.id, {
110+
...userInfo,
111+
email: userInfo.email.toLowerCase(),
112+
emailVerified:
113+
userInfo.email.toLocaleLowerCase() === dbUser.user.email
114+
? dbUser.user.emailVerified || userInfo.emailVerified
115+
: userInfo.emailVerified,
116+
});
117+
}
105118
} else {
106119
if (disableSignUp) {
107120
return {

packages/better-auth/src/oauth2/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,11 @@ export type ProviderOptions<Profile extends Record<string, any> = any> = {
176176
* The response mode to use for the authorization code request
177177
*/
178178
responseMode?: "query" | "form_post";
179+
/**
180+
* If enabled, the user info will be overridden with the provider user info
181+
* This is useful if you want to use the provider user info to update the user info
182+
*
183+
* @default false
184+
*/
185+
overrideUserInfoOnSignIn?: boolean;
179186
};

packages/better-auth/src/plugins/generic-oauth/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ interface GenericOAuthConfig {
127127
* @default "post"
128128
*/
129129
authentication?: "basic" | "post";
130+
/**
131+
* Override user info with the provider info.
132+
*
133+
* This will update the user info with the provider info,
134+
* when the user signs in with the provider.
135+
* @default false
136+
*/
137+
overrideUserInfo?: boolean;
130138
}
131139

132140
interface GenericOAuthOptions {
@@ -681,6 +689,7 @@ export const genericOAuth = (options: GenericOAuthOptions) => {
681689
disableSignUp:
682690
(provider.disableImplicitSignUp && !requestSignUp) ||
683691
provider.disableSignUp,
692+
overrideUserInfo: provider.overrideUserInfo,
684693
});
685694

686695
if (result.error) {

packages/better-auth/src/plugins/sso/index.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,14 @@ interface SSOOptions {
6363
};
6464
/**
6565
* Disable implicit sign up for new users. When set to true for the provider,
66-
* sign-in need to be calle dwith with requestSignUp as true to create new users.
66+
* sign-in need to be called with with requestSignUp as true to create new users.
6767
*/
6868
disableImplicitSignUp?: boolean;
69+
/**
70+
* Override user info with the provider info.
71+
* @default false
72+
*/
73+
defaultOverrideUserInfo?: boolean;
6974
}
7075

7176
export const sso = (options?: SSOOptions) => {
@@ -166,6 +171,13 @@ export const sso = (options?: SSOOptions) => {
166171
"If organization plugin is enabled, the organization id to link the provider to",
167172
})
168173
.optional(),
174+
overrideUserInfo: z
175+
.boolean({
176+
description:
177+
"Override user info with the provider info. Defaults to false",
178+
})
179+
.default(false)
180+
.optional(),
169181
}),
170182
use: [sessionMiddleware],
171183
metadata: {
@@ -375,6 +387,10 @@ export const sso = (options?: SSOOptions) => {
375387
mapping: body.mapping,
376388
scopes: body.scopes,
377389
userinfoEndpoint: body.userInfoEndpoint,
390+
overrideUserInfo:
391+
ctx.body.overrideUserInfo ||
392+
options?.defaultOverrideUserInfo ||
393+
false,
378394
}),
379395
organizationId: body.organizationId,
380396
userId: ctx.context.session.user.id,
@@ -848,6 +864,7 @@ export const sso = (options?: SSOOptions) => {
848864
scope: tokenResponse.scopes?.join(","),
849865
},
850866
disableSignUp: options?.disableImplicitSignUp && !requestSignUp,
867+
overrideUserInfo: config.overrideUserInfo,
851868
});
852869
if (linked.error) {
853870
throw ctx.redirect(
@@ -978,6 +995,7 @@ interface OIDCConfig {
978995
discoveryEndpoint: string;
979996
userInfoEndpoint?: string;
980997
scopes?: string[];
998+
overrideUserInfo?: boolean;
981999
tokenEndpoint?: string;
9821000
tokenEndpointAuthentication?: "client_secret_post" | "client_secret_basic";
9831001
jwksEndpoint?: string;

0 commit comments

Comments
 (0)