Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): make next-auth optionally opt-into Web-compatible mode #4299

Closed
wants to merge 13 commits into from

Conversation

ThangHuuVu
Copy link
Member

@ThangHuuVu ThangHuuVu commented Apr 1, 2022

Reasoning 💡

🚧 WIP

This PR removes openid-client dependency and adds oauth4webapi. It would decouple NextAuth.js from Node.js and enable us to run NextAuth.js on other JS runtimes like the edge runtime.

Notes on using PKCE and state/nonce

PKCE is always used. See: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-2.1.1
If PKCE support is missing (either missing from AS metadata or we are not sure), use state in addition. Nonce support for OpenID is WIP at #4100

Checklist 🧢

  • Replace openid-client with oauth4webapi
  • Handle auth on middleware
  • Documentation
  • Tests
  • Ready to be merged

Affected issues 🎟

References

https://github.com/panva/oauth4webapi/blob/main/examples/code.ts
https://github.com/panva/oauth4webapi/blob/main/examples/oauth.ts

@vercel
Copy link

vercel bot commented Apr 1, 2022

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: https://vercel.com/nextauthjs/next-auth/DUoR4N6DybecdR4evEHQzWcVYeR1
✅ Preview: Canceled

[Deployment for 2267292 canceled]

@github-actions github-actions bot added core Refers to `@auth/core` providers TypeScript Issues relating to TypeScript labels Apr 1, 2022
@@ -112,8 +98,8 @@ export interface OAuthConfig<P> extends CommonProviderOptions, PartialIssuer {
version?: string
profile?: (profile: P, tokens: TokenSet) => Awaitable<User & { id: string }>
checks?: ChecksType | ChecksType[]
client?: Partial<ClientMetadata>
jwks?: { keys: JWK[] }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might not need this anymore, as the new Client doesn't require it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exists to be able to override the usage of openid-client. I wonder if we can truly get rid of it and still be flexible. We should discuss.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example: #4286

How could we solve it otherwise?

@@ -130,9 +116,6 @@ export interface OAuthConfig<P> extends CommonProviderOptions, PartialIssuer {
region?: string
// TODO: only allow for some
issuer?: string
/** Read more at: https://github.com/panva/node-openid-client/tree/main/docs#customizing-http-requests */
httpOptions?: HttpOptions
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is specific to Node.js

*/
checks: OAuthChecks
Copy link
Member Author

@ThangHuuVu ThangHuuVu Apr 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this because I think it's unnecessary. I couldn't find the equivalent in oauth4webapi

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's basically the way to tell if the provider wants to use state, pkce, both or none. (nonce support is WIP #4100, but I haven't had the time to have a look yet).

If omitted, our default is to use state currently.

@@ -18,13 +18,13 @@ export default function Facebook<
url: "https://graph.facebook.com/me",
// https://developers.facebook.com/docs/graph-api/reference/user/#fields
params: { fields: "id,name,email,picture" },
async request({ tokens, client, provider }) {
Copy link
Member Author

@ThangHuuVu ThangHuuVu Apr 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm leaving this out because I'm not sure about the API. Can't we abstract this away?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, the request method was added (see #1846) to support providers that do not follow the spec properly... It's a shame that Facebook doesn't do it. (Note, it might have changed since, but I haven't checked in a while). If we can get rid of it, I'm all for though. But there might be other providers still using it.

We should discuss if we want to start discouraging (read: drop support/request) providers that do not implement the spec 100% correctly...

What I absolutely want to avoid is to go back to handling provider-specific stuff in our core. (eg if (provider.id === "facebook"), etc.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See also #4299 (comment)

@ThangHuuVu ThangHuuVu self-assigned this Apr 4, 2022
@ThangHuuVu ThangHuuVu changed the title replace openid-client with oauth4webapi breaking: replace openid-client with oauth4webapi Apr 6, 2022
- handle pkce discovery
- update `validateAuthResponse` to use `expectState`
@@ -5,11 +5,10 @@ import { usePKCECodeVerifier } from "./pkce-handler"
import { OAuthCallbackError } from "../../errors"
import {
authorizationCodeGrantRequest,
clientCredentialsGrantRequest,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
clientCredentialsGrantRequest,

if (!provider?.checks?.includes("pkce") || !codeVerifier) {
if (
!provider?.checks?.includes("pkce") ||
!codeVerifier ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
!codeVerifier ||

if (
!provider?.checks?.includes("pkce") ||
!codeVerifier ||
code_challenge_methods_supported?.length === 0
Copy link
Contributor

@panva panva Apr 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
code_challenge_methods_supported?.length === 0
code_challenge_methods_supported?.includes('S256') === false

client,
callbackParameters,
provider.callbackUrl,
pkce?.codeVerifier as string
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to ask Filip about this, as I'm not sure how to call this method without the codeVerifier.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't, that's the whole point. The API is designed so that you always use S256 PKCE. The discussion we had about AS not supporting PKCE means that you'd use nonce (if openid), or state (if not openid) in addition to PKCE since you don't know if PKCE is supported or not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a look, @panva! 🙏

I thought that this API would also handle requesting the access token in the cases PKCE is not supported, like https://www.rfc-editor.org/rfc/rfc6749.html#section-4.1 and https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth. How would you suggest we make the token request in those cases?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The client always uses PKCE. And in cases where you're not sure if PKCE is supported on the AS you still use PKCE but add nonce (when using OIDC) or state (when not using OIDC).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, I just updated the PKCE handler to always return a value. 🙏

@vercel
Copy link

vercel bot commented Jun 10, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
next-auth ✅ Ready (Inspect) Visit Preview Oct 7, 2022 at 0:21AM (UTC)

@vercel vercel bot temporarily deployed to Preview June 20, 2022 05:04 Inactive
@balazsorban44 balazsorban44 changed the title breaking: replace openid-client with oauth4webapi feat(core): make core optionally opt-into Web-compatible runtime mode Jun 27, 2022
@balazsorban44 balazsorban44 changed the title feat(core): make core optionally opt-into Web-compatible runtime mode feat(core): make next-authoptionally opt-into Web-compatible mode Jun 27, 2022
@balazsorban44 balazsorban44 changed the title feat(core): make next-authoptionally opt-into Web-compatible mode feat(core): make next-auth optionally opt-into Web-compatible mode Jun 27, 2022
@david58
Copy link

david58 commented Oct 5, 2022

With the upcoming end of the active phase of node 16, is there a plan to move this forward as it seems to be the main blocker of official support of higher versions?

@ThangHuuVu
Copy link
Member Author

ThangHuuVu commented Oct 6, 2022

@david58 there is another blocker prior to this PR: #4769 👀 Hope to ship this soon, too 🙌

@balazsorban44
Copy link
Member

Superseded by #5536

@ThangHuuVu ThangHuuVu deleted the feat/oauth4webapi branch October 15, 2022 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Refers to `@auth/core` providers TypeScript Issues relating to TypeScript
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants