Skip to content

Commit b56b928

Browse files
feat(core): re-introduce idToken: boolean (#11161)
* feat(core): support JWT signed userinfo endpoint response * chore(deps): bump `oauth4webapi` * use `idToken: boolean` instead * docs: mention `idToken` changed behavior * move helpers
1 parent 89b64c5 commit b56b928

File tree

7 files changed

+120
-74
lines changed

7 files changed

+120
-74
lines changed

docs/pages/getting-started/migrating-to-v5.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ npm install next-auth@beta
4343
- The minimum required Next.js version is now [14.0](https://nextjs.org/14).
4444
- The import `next-auth/next` is replaced. See [Authenticating server-side](#authenticating-server-side) for more details.
4545
- The import `next-auth/middleware` is replaced. See [Authenticating server-side](#authenticating-server-side) for more details.
46+
- When the `idToken: boolean` option is set to `false`, it won't entirely disable the ID token. Instead, it signals `next-auth` to also visit the `userinfo_endpoint` for the final user data. Previously, `idToken: false` opted out to check the `id_token` validity at all.
4647

4748
## Migration
4849

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"@types/cookie": "0.6.0",
6767
"cookie": "0.6.0",
6868
"jose": "^5.1.3",
69-
"oauth4webapi": "^2.9.0",
69+
"oauth4webapi": "^2.10.4",
7070
"preact": "10.11.3",
7171
"preact-render-to-string": "5.2.3"
7272
},

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

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import type {
1414
TokenSet,
1515
User,
1616
} from "../../../../types.js"
17-
import type { OAuthConfigInternal } from "../../../../providers/index.js"
17+
import { type OAuthConfigInternal } from "../../../../providers/index.js"
1818
import type { Cookie } from "../../../utils/cookie.js"
19+
import { isOIDCProvider } from "../../../utils/providers.js"
1920

2021
/**
2122
* Handles the following OAuth steps.
@@ -140,30 +141,49 @@ export async function handleOAuth(
140141
let profile: Profile = {}
141142
let tokens: TokenSet & Pick<Account, "expires_at">
142143

143-
if (provider.type === "oidc") {
144+
if (isOIDCProvider(provider)) {
144145
const nonce = await checks.nonce.use(cookies, resCookies, options)
145-
const result = await o.processAuthorizationCodeOpenIDResponse(
146-
as,
147-
client,
148-
codeGrantResponse,
149-
nonce ?? o.expectNoNonce
150-
)
146+
const processedCodeResponse =
147+
await o.processAuthorizationCodeOpenIDResponse(
148+
as,
149+
client,
150+
codeGrantResponse,
151+
nonce ?? o.expectNoNonce
152+
)
151153

152-
if (o.isOAuth2Error(result)) {
153-
console.log("error", result)
154+
if (o.isOAuth2Error(processedCodeResponse)) {
155+
console.log("error", processedCodeResponse)
154156
throw new Error("TODO: Handle OIDC response body error")
155157
}
156158

157-
profile = o.getValidatedIdTokenClaims(result)
158-
tokens = result
159+
const idTokenClaims = o.getValidatedIdTokenClaims(processedCodeResponse)
160+
profile = idTokenClaims
161+
162+
if (provider.idToken === false) {
163+
const userinfoResponse = await o.userInfoRequest(
164+
as,
165+
client,
166+
processedCodeResponse.access_token
167+
)
168+
169+
profile = await o.processUserInfoResponse(
170+
as,
171+
client,
172+
idTokenClaims.sub,
173+
userinfoResponse
174+
)
175+
}
176+
tokens = processedCodeResponse
159177
} else {
160-
tokens = await o.processAuthorizationCodeOAuth2Response(
161-
as,
162-
client,
163-
codeGrantResponse
164-
)
165-
if (o.isOAuth2Error(tokens as any)) {
166-
console.log("error", tokens)
178+
const processedCodeResponse =
179+
await o.processAuthorizationCodeOAuth2Response(
180+
as,
181+
client,
182+
codeGrantResponse
183+
)
184+
tokens = processedCodeResponse
185+
if (o.isOAuth2Error(processedCodeResponse)) {
186+
console.log("error", processedCodeResponse)
167187
throw new Error("TODO: Handle OAuth 2.0 response body error")
168188
}
169189

@@ -174,7 +194,7 @@ export async function handleOAuth(
174194
const userinfoResponse = await o.userInfoRequest(
175195
as,
176196
client,
177-
(tokens as any).access_token
197+
processedCodeResponse.access_token
178198
)
179199
profile = await userinfoResponse.json()
180200
} else {

packages/core/src/lib/utils/providers.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,22 @@ function normalizeEndpoint(
153153
}
154154
return { url, request: e?.request, conform: e?.conform }
155155
}
156+
157+
export function isOIDCProvider(
158+
provider: InternalProvider<"oidc" | "oauth">
159+
): provider is InternalProvider<"oidc"> {
160+
return provider.type === "oidc"
161+
}
162+
163+
export function isOAuth2Provider(
164+
provider: InternalProvider<"oidc" | "oauth">
165+
): provider is InternalProvider<"oauth"> {
166+
return provider.type === "oauth"
167+
}
168+
169+
/** Either OAuth 2 or OIDC */
170+
export function isOAuthProvider(
171+
provider: InternalProvider<any>
172+
): provider is InternalProvider<"oauth" | "oidc"> {
173+
return provider.type === "oauth" || provider.type === "oidc"
174+
}

packages/core/src/providers/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Profile } from "../types.js"
1+
import type { Profile } from "../types.js"
22
import CredentialsProvider from "./credentials.js"
33
import type {
44
CredentialsConfig,
@@ -12,7 +12,7 @@ import type {
1212
OAuthProviderType,
1313
OIDCConfig,
1414
} from "./oauth.js"
15-
import { WebAuthnConfig, WebAuthnProviderType } from "./webauthn.js"
15+
import type { WebAuthnConfig, WebAuthnProviderType } from "./webauthn.js"
1616

1717
export * from "./credentials.js"
1818
export * from "./email.js"

packages/core/src/providers/oauth.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,11 @@ export interface OIDCConfig<Profile>
239239
extends Omit<OAuth2Config<Profile>, "type" | "checks"> {
240240
type: "oidc"
241241
checks?: Array<NonNullable<OAuth2Config<Profile>["checks"]>[number] | "nonce">
242+
/**
243+
* If set to `false`, the `userinfo_endpoint` will be fetched for the user data.
244+
* @note An `id_token` is still required to be returned during the authorization flow.
245+
*/
246+
idToken?: boolean
242247
}
243248

244249
export type OAuthConfig<Profile> = OIDCConfig<Profile> | OAuth2Config<Profile>
@@ -281,6 +286,7 @@ export type OAuthConfigInternal<Profile> = Omit<
281286

282287
export type OIDCConfigInternal<Profile> = OAuthConfigInternal<Profile> & {
283288
checks: OIDCConfig<Profile>["checks"]
289+
idToken: OIDCConfig<Profile>["idToken"]
284290
}
285291

286292
export type OAuthUserConfig<Profile> = Omit<

0 commit comments

Comments
 (0)