Skip to content

Commit f08f27f

Browse files
authored
fix: mismatched account.providerAccountId (#9932)
* fix: remove unnecessary /signout appending to signout form action URL * fix: use actual user.id from provider as providerAccountId * fix: add regression test * fix: typescript issue, profile.id should always be set * fix: fallback to randomUUID if provider doesn't return a user.id * fix: add internal jsdoc on exported fn * Discard changes to packages/core/src/lib/pages/signout.tsx
1 parent 87e903b commit f08f27f

File tree

3 files changed

+108
-3
lines changed

3 files changed

+108
-3
lines changed

packages/core/src/lib/actions/callback/oauth/callback.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,11 @@ export async function handleOAuth(
186186
return { ...profileResult, profile, cookies: resCookies }
187187
}
188188

189-
/** Returns the user and account that is going to be created in the database. */
190-
async function getUserAndAccount(
189+
/**
190+
* Returns the user and account that is going to be created in the database.
191+
* @internal
192+
*/
193+
export async function getUserAndAccount(
191194
OAuthProfile: Profile,
192195
provider: OAuthConfigInternal<any>,
193196
tokens: TokenSet,
@@ -207,7 +210,7 @@ async function getUserAndAccount(
207210
...tokens,
208211
provider: provider.id,
209212
type: provider.type,
210-
providerAccountId: user.id.toString(),
213+
providerAccountId: userFromProfile.id ?? crypto.randomUUID(),
211214
},
212215
}
213216
} catch (e) {

packages/core/test/authorize.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { describe, expect, it, vi, beforeEach } from "vitest"
22
import { Auth, AuthConfig, skipCSRFCheck } from "../src"
33
import { Adapter } from "../src/adapters"
44
import SendGrid from "../src/providers/sendgrid"
5+
import { getUserAndAccount } from "../src/lib/actions/callback/oauth/callback"
6+
import { getUserAndAccountArgs } from "./fixtures/oauth-callback.ts"
57

68
const mockAdapter: Adapter = {
79
createVerificationToken: vi.fn(),
@@ -67,6 +69,30 @@ describe("auth via callbacks.signIn", () => {
6769
})
6870
})
6971

72+
describe("oauth callback", () => {
73+
it("should build the correct user object", async () => {
74+
const { profile, provider, tokens, logger } = getUserAndAccountArgs
75+
const profileResult = await getUserAndAccount(
76+
profile,
77+
provider,
78+
tokens,
79+
logger
80+
)
81+
82+
expect(profileResult?.account.type).toBe("oauth")
83+
expect(profileResult?.account.provider).toBe("github")
84+
expect(profileResult?.account.providerAccountId).toBe("abc")
85+
expect(profileResult?.user.email).toBe("[email protected]")
86+
87+
// Test 'user.id' is a valid UUIDv4 from `crypto.randomUUID()`
88+
expect(
89+
profileResult?.user.id.match(
90+
/^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
91+
)?.[0]
92+
).toBe(profileResult?.user.id)
93+
})
94+
})
95+
7096
// TODO: We need an OAuth provider to test against
7197
describe.todo("redirect in oauth", () => {})
7298
})
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
const userProfile = {
2+
id: "abc",
3+
name: "Fill Murray",
4+
5+
image: "https://source.boringavatars.com/marble",
6+
}
7+
8+
export const getUserAndAccountArgs = {
9+
profile: {
10+
login: "fmurray",
11+
id: 7415983,
12+
node_id: "aDn61Xa3cjk0MT05gDQ=",
13+
gravatar_id: "",
14+
type: "User",
15+
site_admin: false,
16+
name: "Fill Murray",
17+
company: null,
18+
19+
hireable: null,
20+
bio: "CEO of HTMX",
21+
twitter_username: "",
22+
public_repos: 146,
23+
public_gists: 39,
24+
followers: 314,
25+
following: 86,
26+
created_at: "2014-04-26T19:35:49Z",
27+
updated_at: "2024-01-27T20:46:58Z",
28+
private_gists: 19,
29+
total_private_repos: 20,
30+
owned_private_repos: 20,
31+
disk_usage: 768655,
32+
collaborators: 6,
33+
two_factor_authentication: true,
34+
plan: {
35+
name: "free",
36+
space: 976562499,
37+
collaborators: 0,
38+
private_repos: 10000,
39+
},
40+
},
41+
provider: {
42+
id: "github",
43+
name: "GitHub",
44+
type: "oauth" as const,
45+
authorization: {
46+
url: new URL("https://google.com"),
47+
request: undefined,
48+
conform: undefined,
49+
},
50+
token: { url: new URL("https://google.com") },
51+
userinfo: {
52+
url: new URL("https://google.com"),
53+
// request: async () => {},
54+
conform: undefined,
55+
},
56+
profile: () => userProfile,
57+
style: { logo: "/github.svg", bg: "#24292f", text: "#fff" },
58+
clientId: "abc",
59+
clientSecret: "abc",
60+
signinUrl: "http://localhost:3000/api/auth/signin/github",
61+
callbackUrl: "http://localhost:3000/api/auth/callback/github",
62+
redirectProxyUrl: undefined,
63+
checks: ["pkce" as const],
64+
account: () => {},
65+
},
66+
tokens: {
67+
access_token: "gho_abc",
68+
token_type: "bearer" as const,
69+
scope: "read:user,user:email",
70+
},
71+
logger: {
72+
error: (error: Error) => console.error(error),
73+
warn: (error: string) => console.warn(error),
74+
debug: (error: string) => console.debug(error),
75+
},
76+
}

0 commit comments

Comments
 (0)