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

Custom provider with pkce and state checks but without client_secret possible? #3540

Closed
virth opened this issue Dec 30, 2021 · 14 comments
Closed
Labels
question Ask how to do something or how something works

Comments

@virth
Copy link

virth commented Dec 30, 2021

Question 💬

First: thanks a lot for your help! Really appreciate it. 🙏

My question: Is it possible to use a next-auth customprovider with pkce and state checks but without a client_secret? I couln't figure it out. next-auth always asks for a secret. 🤔

background information

I try to use https://zitadel.ch and auth-next with a custom provider. I use the OIDC-flow with checks: ['pkce', 'state'] without a client_secret (as specified in: https://www.oauth.com/oauth2-servers/pkce/authorization-code-exchange/)

Even though a client_secret is not required by specification, next-auth wants one. (see error message in "How to reproduce").

I'm not entirely sure but maybe the option I'm looking for is checks: ['pkce', 'state', 'nonce'] as discussed in #1565 (comment) but this seems not to be implemented.

All existing providers I looked at seem to want a client_secret even though it's not best practice for SPA's.

How to reproduce ☕️

  1. Create your own organisation at: https://accounts.zitadel.ch/register/org
  2. Create a project and an app with type web and the pkce flow
  3. Use the provided client_id together with this configuration:
export default NextAuth({
  providers: [
    {
      id: 'zitadel',
      name: 'zitadel',
      type: 'oauth',
      wellKnown: 'https://issuer.zitadel.ch',
      authorization: { params: { scope: 'openid email profile' } },
      idToken: true,
      checks: ['pkce', 'state'],
      profile(profile) {
        console.log(profile);
        return {
          id: profile.sub,
          name: profile.name,
          email: profile.email,
          image: profile.picture,
        };
      },
      clientId: process.env.ZITADEL_CLIENT_ID,
    },
  ],
});

Alltough a client_secret is officially not required, next-auth would like to have one:

[next-auth][error][OAUTH_CALLBACK_ERROR]
https://next-auth.js.org/errors#oauth_callback_error client_secret_basic client authentication method requires a client_secret {
  error: {
    message: 'client_secret_basic client authentication method requires a client_secret',
    stack: 'TypeError: client_secret_basic client authentication method requires a client_secret\n' +
      '    at Client.authFor (/Users/virth/git/smartive/customers/bin-eigenverlag/learnfox-backend/node_modules/openid-client/lib/helpers/client.js:135:15)\n' +
      '    at Client.authenticatedPost (/Users/virth/git/smartive/customers/bin-eigenverlag/learnfox-backend/node_modules/openid-client/lib/helpers/client.js:174:30)\n' +
      '    at Client.grant (/Users/virth/git/smartive/customers/bin-eigenverlag/learnfox-backend/node_modules/openid-client/lib/client.js:1327:46)\n' +
      '    at Client.callback (/Users/virth/git/smartive/customers/bin-eigenverlag/learnfox-backend/node_modules/openid-client/lib/client.js:472:35)\n' +
      '    at oAuthCallback (/Users/virth/git/smartive/customers/bin-eigenverlag/learnfox-backend/node_modules/next-auth/core/lib/oauth/callback.js:112:29)\n' +
      '    at async Object.callback (/Users/virth/git/smartive/customers/bin-eigenverlag/learnfox-backend/node_modules/next-auth/core/routes/callback.js:50:11)\n' +
      '    at async NextAuthHandler (/Users/virth/git/smartive/customers/bin-eigenverlag/learnfox-backend/node_modules/next-auth/core/index.js:133:28)\n' +
      '    at async NextAuthNextHandler (/Users/virth/git/smartive/customers/bin-eigenverlag/learnfox-backend/node_modules/next-auth/next/index.js:20:19)\n' +
      '    at async /Users/virth/git/smartive/customers/bin-eigenverlag/learnfox-backend/node_modules/next-auth/next/index.js:56:32\n' +
      '    at async Object.apiResolver (/Users/virth/git/smartive/customers/bin-eigenverlag/learnfox-backend/node_modules/next/dist/server/api-utils.js:102:9)',
    name: 'TypeError'
  },
  providerId: 'zitadel',
  message: 'client_secret_basic client authentication method requires a client_secret'
}

Contributing 🙌🏽

Yes, I am willing to help answer this question in a PR

@virth virth added the question Ask how to do something or how something works label Dec 30, 2021
@virth virth changed the title OAuth Provider with pkce and state but without client_secret not working Custom provider with 'pkce' and 'state' checks but without client_secret possible? Dec 30, 2021
@virth virth changed the title Custom provider with 'pkce' and 'state' checks but without client_secret possible? Custom provider with pkce and state checks but without client_secret possible? Dec 30, 2021
@balazsorban44
Copy link
Member

Should be possible, check the client provider config option: https://next-auth.js.org/configuration/providers/oauth#client-option

@hboylan
Copy link
Contributor

hboylan commented Jan 26, 2022

I'm encountering the same issue while trying to use PKCE. The OktaProvider options type requires clientSecret and the custom provider throws the error client_secret_basic client authentication method requires a client_secret.

@balazsorban44 It would be great for next-auth to support this natively since PKCE flow does not require a client secret. I would also be willing to contribute the code for this.

@hboylan
Copy link
Contributor

hboylan commented Jan 26, 2022

So after messing around with the client provider config option, I was able to get this working with the following provider config in the test app.

Okta({
  clientId: process.env.OKTA_ID,
  clientSecret: process.env.OKTA_SECRET, // not set
  issuer: process.env.OKTA_ISSUER,
  checks: ["pkce", "state"],
  client: {
    token_endpoint_auth_method: "none",
  },
})

What do you all think about updating the config types so they no longer require clientSecret and merging the client options { token_endpoint_auth_method: 'none' } if not set? Or maybe this is something the underlying openid-client should handle?

@kramer99
Copy link

kramer99 commented Jan 26, 2022

@hboylan Thank you! I was stuck with the same issue using PKCE with Okta complaining about client_secret. I had resigned myself to using v3, but your config above worked for me with v4.1.2

Agree, this should be the default behaviour for PKCE providers, clientSecret should not be required.

@atissedredecathlon
Copy link

Just lost ~2 hours on this issue, I'm using a custom provider with PKCE and state, without any client secret.

The documentation is not currently referencing this use case and I would have been unable to resolve this issue without @hboylan last reply (thank you for that).

Could we merge this default PKCE behaviour or at least update the documentation ?

@dryhurst
Copy link

dryhurst commented Jul 26, 2022

Okta({
  clientId: process.env.OKTA_ID,
  clientSecret: process.env.OKTA_SECRET, // not set
  issuer: process.env.OKTA_ISSUER,
  checks: ["pkce", "state"],
  client: {
    token_endpoint_auth_method: "none",
  },
})

This is the magic sauce right here. Three hours of debugging okta/next-auth and I finally come across this thread. Thank you.

@LLCampos
Copy link

I don't think this should be closed? 🤔 The underlying issue is still there - client_secret shouldn't be mandatory when using pkce flow? The workaround looks like an hack.

@skarensmoll
Copy link

Hey there! I spent quite some time trying to fix this, this is not even on the documentation. Thanks @hboylan btw your solution worked like a charm

@m-weslati
Copy link

Hello! after using the solution from @hboylan, the signOut() function seems to not be able to reset the session and i would get logged in once i click the login button (which calls signIn()) i get authenticated without redirecting to the provider (in my case keycloak).

@rawkode
Copy link

rawkode commented Oct 9, 2024

This appears to now be broken due to yesterday's commit:

0244513

@rawkode
Copy link

rawkode commented Oct 9, 2024

@balazsorban44 This really needs re-opened 👍🏻

@balazsorban44
Copy link
Member

balazsorban44 commented Oct 9, 2024

@rawkode please open a new issue with a reproduction. This is a 3y old issue marked as a question. I only saw this by accident, we don't monitor closed issues. 🙏

@balazsorban44
Copy link
Member

@rawkode if you can consistently reproduce an issue, please open a new issue. I just tried this locally using a Keycloak instance that supports public clients (no client_secret) passed, and I was logged in fine. I can't commit to check back on this issue, but if you open a new one, I would like to get to the bottom of this! 💚

@franck-grenier
Copy link

franck-grenier commented Feb 12, 2025

Hello,
null, undefined or not setting clientSecret does not work.

All throwing #oauth_callback_error client_secret_basic client authentication method requires a client_secret.

The only value working with PKCE is an empty string : clientSecret: ''

(next-auth 4.21.1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Ask how to do something or how something works
Projects
None yet
Development

No branches or pull requests