fix: enforce override user info on oauth signin (#2294)

This commit is contained in:
Bereket Engida
2025-04-15 12:43:25 +03:00
committed by GitHub
parent be807a8de1
commit de50dfcb6f
3 changed files with 138 additions and 2 deletions

View File

@@ -189,6 +189,7 @@ export const callbackOAuth = createAuthEndpoint(
disableSignUp:
(provider.disableImplicitSignUp && !requestSignUp) ||
provider.options?.disableSignUp,
overrideUserInfo: provider.options?.overrideUserInfoOnSignIn,
});
if (result.error) {
c.context.logger.error(result.error.split(" ").join("_"));

View File

@@ -105,9 +105,10 @@ export async function handleOAuthUserInfo(
}
}
if (overrideUserInfo) {
const { id: _, ...restUserInfo } = userInfo;
// update user info from the provider if overrideUserInfo is true
await c.context.internalAdapter.updateUser(dbUser.user.id, {
...userInfo,
...restUserInfo,
email: userInfo.email.toLowerCase(),
emailVerified:
userInfo.email.toLocaleLowerCase() === dbUser.user.email

View File

@@ -7,6 +7,8 @@ import { getOAuth2Tokens, refreshAccessToken } from "../oauth2";
import { signJWT } from "../crypto/jwt";
import { OAuth2Server } from "oauth2-mock-server";
import { betterFetch } from "@better-fetch/fetch";
import Database from "better-sqlite3";
import { getMigrations } from "../db";
let server = new OAuth2Server();
@@ -16,7 +18,33 @@ vi.mock("../oauth2", async (importOriginal) => {
...original,
validateAuthorizationCode: vi
.fn()
.mockImplementation(async (...args: any) => {
.mockImplementation(async (option: any) => {
if (option.options.overrideUserInfoOnSignIn) {
const data: GoogleProfile = {
email: "user@email.com",
email_verified: true,
name: "Updated User",
picture: "https://test.com/picture.png",
exp: 1234567890,
sub: "1234567890",
iat: 1234567890,
aud: "test",
azp: "test",
nbf: 1234567890,
iss: "test",
locale: "en",
jti: "test",
given_name: "Updated",
family_name: "User",
};
const testIdToken = await signJWT(data, DEFAULT_SECRET);
const tokens = getOAuth2Tokens({
access_token: "test",
refresh_token: "test",
id_token: testIdToken,
});
return tokens;
}
const data: GoogleProfile = {
email: "user@email.com",
email_verified: true,
@@ -549,3 +577,109 @@ describe("Disable signup", async () => {
});
});
});
describe("signin", async () => {
const database = new Database(":memory:");
beforeAll(async () => {
const migrations = await getMigrations({
database,
});
await migrations.runMigrations();
});
it("should allow user info override during sign in", async () => {
let state = "";
const { client, cookieSetter } = await getTestInstance({
database,
socialProviders: {
google: {
clientId: "test",
clientSecret: "test",
enabled: true,
},
},
});
const signInRes = await client.signIn.social({
provider: "google",
callbackURL: "/callback",
});
expect(signInRes.data).toMatchObject({
url: expect.stringContaining("google.com"),
redirect: true,
});
state = new URL(signInRes.data!.url!).searchParams.get("state") || "";
const headers = new Headers();
await client.$fetch("/callback/google", {
query: {
state,
code: "test",
},
method: "GET",
onError: (c) => {
cookieSetter(headers)(c as any);
},
});
const session = await client.getSession({
fetchOptions: {
headers,
},
});
expect(session.data?.user).toMatchObject({
name: "First Last",
});
});
it("should allow user info override during sign in", async () => {
let state = "";
const { client, cookieSetter } = await getTestInstance(
{
database,
socialProviders: {
google: {
clientId: "test",
clientSecret: "test",
enabled: true,
overrideUserInfoOnSignIn: true,
},
},
},
{
disableTestUser: true,
},
);
const signInRes = await client.signIn.social({
provider: "google",
callbackURL: "/callback",
});
expect(signInRes.data).toMatchObject({
url: expect.stringContaining("google.com"),
redirect: true,
});
state = new URL(signInRes.data!.url!).searchParams.get("state") || "";
const headers = new Headers();
await client.$fetch("/callback/google", {
query: {
state,
code: "test",
},
method: "GET",
onError: (c) => {
cookieSetter(headers)(c as any);
},
});
const session = await client.getSession({
fetchOptions: {
headers,
},
});
expect(session.data?.user).toMatchObject({
name: "Updated User",
});
});
});