nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.56k stars 3.45k forks source link

Salesforce Provider sends incorrect request to token endpoint #4200

Closed arx111 closed 2 years ago

arx111 commented 2 years ago

Provider type

Salesforce

Environment

  System:
    OS: macOS 12.2.1
    CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
    Memory: 394.78 MB / 16.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 16.14.1 - ~/.nvm/versions/node/v16.14.1/bin/node
    npm: 8.5.0 - ~/.nvm/versions/node/v16.14.1/bin/npm
  Browsers:
    Brave Browser: 98.1.35.101
    Firefox: 98.0.1
    Safari: 15.3
  npmPackages:
    next: ^12.0.4 => 12.0.4 
    next-auth: ^4.3.0 => 4.3.0 
    react: ^17.0.1 => 17.0.2 

Reproduction URL

https://github.com/arx111/next-auth-example/blob/main/pages/api/auth/%5B...nextauth%5D.ts

Describe the issue

for the Sandbox version of salesforce, we would use test.salesforce.com domain instead of login.salesforce.com for all the requests including Oauth2 endpoints. documented here: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_endpoints.htm&type=5

All endpoints require secure HTTP (HTTPS). Instead of using login.salesforce.com, you can also use the My Domain, Experience Cloud site, or test.salesforce.com (sandbox) domain in these endpoints. For hostname, use the My Domain, Experience Cloud site, or custom URL.

however i get this error when salesforce redirects us to the callback:

[next-auth][error][OAUTH_CALLBACK_ERROR] 
https://next-auth.js.org/errors#oauth_callback_error invalid_client_id (client identifier invalid) {
  error: {
    message: 'invalid_client_id (client identifier invalid)',
    stack: 'OPError: invalid_client_id (client identifier invalid)\n' +
      '    at processResponse (/Users/.../node_modules/openid-client/lib/helpers/process_response.js:38:13)\n' +
      '    at Client.grant (/Users/.../node_modules/openid-client/lib/client.js:1340:22)\n' +
      '    at processTicksAndRejections (node:internal/process/task_queues:96:5)\n' +
      '    at async Client.oauthCallback (/Users/.../node_modules/openid-client/lib/client.js:612:24)\n' +
      '    at async oAuthCallback (/Users/.../node_modules/next-auth/core/lib/oauth/callback.js:114:16)\n' +
      '    at async Object.callback (/Users/.../node_modules/next-auth/core/routes/callback.js:50:11)\n' +
      '    at async NextAuthHandler (/Users/.../node_modules/next-auth/core/index.js:139:28)\n' +
      '    at async NextAuthNextHandler (/Users/.../node_modules/next-auth/next/index.js:21:19)\n' +
      '    at async auth (webpack-internal:///./src/pages/api/auth/[...nextauth].tsx:13:12)\n' +
      '    at async Object.apiResolver (/Users/.../node_modules/next/dist/server/api-utils.js:102:9)',
    name: 'OPError'
  },
  providerId: 'salesforce',
  message: 'invalid_client_id (client identifier invalid)'
}
[next-auth][error][CALLBACK_OAUTH_ERROR] 
https://next-auth.js.org/errors#callback_oauth_error invalid_client_id (client identifier invalid) OPError: invalid_client_id (client identifier invalid)
    at processResponse (/Users/.../node_modules/openid-client/lib/helpers/process_response.js:38:13)
    at Client.grant (/Users/.../node_modules/openid-client/lib/client.js:1340:22)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Client.oauthCallback (/Users/.../node_modules/openid-client/lib/client.js:612:24)
    at async oAuthCallback (/Users/.../node_modules/next-auth/core/lib/oauth/callback.js:114:16)
    at async Object.callback (/Users/.../node_modules/next-auth/core/routes/callback.js:50:11)
    at async NextAuthHandler (/Users/.../node_modules/next-auth/core/index.js:139:28)
    at async NextAuthNextHandler (/Users/.../node_modules/next-auth/next/index.js:21:19)
    at async auth (webpack-internal:///./src/pages/api/auth/[...nextauth].tsx:13:12)
    at async Object.apiResolver (/Users/.../node_modules/next/dist/server/api-utils.js:102:9) {
  name: 'OAuthCallbackError',
  code: undefined
}

the following token retreival request to salesforce:

POST /services/oauth2/token HTTP/1.1
Host: mycompany.my.salesforce.com
Content-length: 307
Content-type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=aPrxhgZ2MIpkSy0aOdn07LjKFvsFOis6RGcWXz7p8JQCjcqfed5NQLe7sxWwMY_JQFuLwHRaRA==&
client_id=3MVG9IHf89I1t8hrvswazsWedXWY0iqK20PSFaInvUgLFB6vrcb9bbWFTSIHpO8G2jxBLJA6uZGyPFC5Aejq&
client_secret=*******************&
redirect_uri=https://www.mycustomerorderstatus.com/oauth2/callback

as described at: https://help.salesforce.com/s/articleView?id=remoteaccess_oauth_web_server_flow.htm&type=5&language=en_US

fails and returns that error

i suspect it is due to incorrectly constructructing the Post request above.

when i implement custom request:

     token: {
          url: "https://test.salesforce.com/services/oauth2/token",

          async request(context) {
            try {
              const { data: tokens } = await axios.post<any, AxiosResponse<TokenSet>>("https://test.salesforce.com/services/oauth2/token", qs.stringify({
                grant_type: "authorization_code",
                code: context.params.code,
                client_id: context.provider.clientId,
                client_secret: context.provider.clientSecret,
                redirect_uri: "http://localhost:3000/api/auth/callback/salesforce"
              }), {
                headers: {
                  "Content-Type": "application/x-www-form-urlencoded"
                }
              });
              console.log("received", tokens)
              return { tokens };
            } catch (e) {
              console.log(e)
              throw e
            }

          }
        },

Everything works fine

How to reproduce

  1. attempt to login with default configuration, on test endpoints, this fails

  2. and implement custom request as above. this returns correct response with access token

Expected behavior

we should get access token from salesforce

ndom91 commented 2 years ago

So it looks like the failed POST request you've provided is being sent to mycompany.my.salesforce.com/services/oauth2/token, right? Or is that just copy and pasted from the docs?

Anyway, the working custom request is being sent to test.salesforce.com/services/oauth2/token.

Our salesforce provider by default has the token url defined as login.salesforce.com/services/oauth2/token. In fact, all defined URLs for this provider are hardcoded to login.salesforce.com as the primary host.

It seems that the salesforce provider could use a small refactor to add an additional option to customise the host (or maybe just the subdomain?) of the configured URLs, in order to support this "MyDomain" feature.

niicojs commented 2 years ago

For the record, I had some troubles making the Salesforce provider work. At the end, this did it:

    SalesforceProvider({
      idToken: true,
      wellKnown: `https://${process.env.SF_DOMAIN}.my.salesforce.com/.well-known/openid-configuration`,
      clientId: process.env.SF_CLIENT_ID,
      clientSecret: process.env.SF_CLIENT_SECRET,
      authorization: { params: { scope: 'openid api refresh_token' } },
      userinfo: {
        async request({ provider, tokens, client }) {
          return await client.userinfo(tokens, {
            params: provider.userinfo?.params,
          });
        },
      },
      profile(profile) {
        return { id: profile.email, ...profile };
      },
    }),
ndom91 commented 2 years ago

Yeah it looks like there is an additional feature for salesforce OAuth which allows users to use their own domain, like the SF_DOMAIN override yuo've shown in your example code here ^^.

We'd need someone to refactor the existing Salesforce Provider a bit to add this functionality. I'll keep this issue open for now then :+1:

balazsorban44 commented 2 years ago

This should be easy to fix, we just have to make the issuer configurable in: https://github.com/nextauthjs/next-auth/blob/main/packages/next-auth/src/providers/salesforce.js

I.e: https://login.salesforce.com should come from options.issuer but still default to https://login.salesforce.com.

Feel free to open a PR!

tarifalhasan commented 1 year ago

not working

aakash14goplani commented 1 year ago

@balazsorban44 - is there any way we can tap the token and user info request made by Nextauth?

I am trying to integrate SvelteKit Auth and facing same issue as above. Even after following recommended solution, I am still not able to resolve error!

So I was wondering if there is any way in which I could visually see what I the payload or endpoint that is hit by SveltkitAuth (or Nextauth)