[GH-ISSUE #8741] authorizationUrlParams not being applied in genericOAuth plugin. It breaks user info. #19808

Open
opened 2026-04-15 19:09:37 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @reutersharry-sketch on GitHub (Mar 23, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8741

Originally assigned to: @gustavovalverde on GitHub.

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

export const auth = betterAuth({
  baseURL: 'http://localhost:5001',
  cookies: { secure: true, sameSite: 'lax' },
  trustedOrigins: ['http://localhost:5001', 'http://127.0.0.1:5001'],
  pages: {
    signIn: process.env.LOGIN_URL!,
    error: process.env.ERROR_URL!,
  },
  user: {
    additionalFields: {
      customerOrg: {
        type: 'string',
        required: false,
        defaultValue: null,
      },
    },
  },
  session: {
    expiresIn: commonWorkingHours,
    updateAge: halfWorkDay,
    additionalFields: {
      customerOrg: {
        type: 'string',
        get: async ({ account }: { account: { customerOrg: string } }) => {
          return account?.customerOrg ?? null
        },
      },
      idToken: {
        type: 'string',
        get: async ({ account }: { account: { idToken: string } }) => {
          return account?.idToken ?? null
        },
      },
      accessToken: {
        type: 'string',
        get: async ({ account }: { account: { accessToken: string } }) => {
          return account?.accessToken ?? null
        },
      },
    },
  },

  plugins: [
    genericOAuth({
      config: [
        {
          providerId: 'MY ID',
          name: 'My IDENTITY',
          type: 'oidc',
          discoveryUrl: `${process.env.AUTH_IDP_AUTHORITY!}/.well-known/openid-configuration`,
          clientId: process.env.AUTH_IDP_ID!,
          clientSecret: process.env.AUTH_IDP_SECRET!,
          issuer: process.env.AUTH_IDP_AUTHORITY!,
          redirectURI: 'http://localhost:5001/api/auth/oauth2/callback/idp',
          pkce: true,
          responseType: 'code',
          authorizationUrlParams: async (ctx) => {
            return {
              ...ctx.params,

              resource: process.env.AUTH_IDP_RESOURCE,
            }
          },
          scopes: ['openid', 'profile', 'email', 'address', 'groups', 'offline_access'],

          refreshAccessToken: async (token: any) => {
            if (new Date().getTime() < (token.expiresAt as number) * 1000) {
              return token.accessToken
            }
            return {
              accessToken: token.accessToken,
              refreshToken: token.refreshToken,
            }
          },
          mapUser: async (res: any) => {
            return {
              ...res.user,
            }
          },
          getUserInfo: async (tokens) => {
            console.log({ tokens })
            const accessToken = tokens?.accessToken
            const res = await fetch(`${process.env.AUTH_IDP_AUTHORITY!}/userinfo`, {
              headers: { Authorization: `Bearer ${accessToken}` },
            })

            // if (!res.ok) return null

            const profile = await res.json()
            return {
              id: profile.sub,
              email: profile.email ?? null,
              name: profile.name ?? null,
              image: profile.picture ?? null,
              customerOrg: profile.customerOrg ?? null, // Map custom field
            }
          },
          hooks: {
            after: [
              {
                matcher: (context: any) => context.path === '/callback/:id',
                handler: createAuthMiddleware(async (ctx) => {
                  if (!ctx.context.newSession?.user?.id) {
                    return
                  }
                  // Update session with fresh user data including roles/rel
                  ctx.context.newSession.user = {
                    ...ctx.context.newSession.user,
                    roles: ctx.context.newSession.user.roles,
                    rel: ctx.context.newSession.user.rel,
                  }
                }),
              },
            ],
          },
        },
      ],
    }),
  ],
})

Current vs. Expected behavior

Normally I should get the session object and the authorizationUrlParams resource property should be provided to the generic IDP, that way, I receive a full-access token in the client.

What version of Better Auth are you using?

1.5.5

System info

Failed to gather account info [Error [APIError]: Account not found] {
  status: 'BAD_REQUEST',
  body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' },
  headers: {},
  statusCode: 400
}

Which area(s) are affected? (Select all that apply)

Client

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
});

Additional context

This is happening in next.js environment.

Originally created by @reutersharry-sketch on GitHub (Mar 23, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8741 Originally assigned to: @gustavovalverde on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce ``` export const auth = betterAuth({ baseURL: 'http://localhost:5001', cookies: { secure: true, sameSite: 'lax' }, trustedOrigins: ['http://localhost:5001', 'http://127.0.0.1:5001'], pages: { signIn: process.env.LOGIN_URL!, error: process.env.ERROR_URL!, }, user: { additionalFields: { customerOrg: { type: 'string', required: false, defaultValue: null, }, }, }, session: { expiresIn: commonWorkingHours, updateAge: halfWorkDay, additionalFields: { customerOrg: { type: 'string', get: async ({ account }: { account: { customerOrg: string } }) => { return account?.customerOrg ?? null }, }, idToken: { type: 'string', get: async ({ account }: { account: { idToken: string } }) => { return account?.idToken ?? null }, }, accessToken: { type: 'string', get: async ({ account }: { account: { accessToken: string } }) => { return account?.accessToken ?? null }, }, }, }, plugins: [ genericOAuth({ config: [ { providerId: 'MY ID', name: 'My IDENTITY', type: 'oidc', discoveryUrl: `${process.env.AUTH_IDP_AUTHORITY!}/.well-known/openid-configuration`, clientId: process.env.AUTH_IDP_ID!, clientSecret: process.env.AUTH_IDP_SECRET!, issuer: process.env.AUTH_IDP_AUTHORITY!, redirectURI: 'http://localhost:5001/api/auth/oauth2/callback/idp', pkce: true, responseType: 'code', authorizationUrlParams: async (ctx) => { return { ...ctx.params, resource: process.env.AUTH_IDP_RESOURCE, } }, scopes: ['openid', 'profile', 'email', 'address', 'groups', 'offline_access'], refreshAccessToken: async (token: any) => { if (new Date().getTime() < (token.expiresAt as number) * 1000) { return token.accessToken } return { accessToken: token.accessToken, refreshToken: token.refreshToken, } }, mapUser: async (res: any) => { return { ...res.user, } }, getUserInfo: async (tokens) => { console.log({ tokens }) const accessToken = tokens?.accessToken const res = await fetch(`${process.env.AUTH_IDP_AUTHORITY!}/userinfo`, { headers: { Authorization: `Bearer ${accessToken}` }, }) // if (!res.ok) return null const profile = await res.json() return { id: profile.sub, email: profile.email ?? null, name: profile.name ?? null, image: profile.picture ?? null, customerOrg: profile.customerOrg ?? null, // Map custom field } }, hooks: { after: [ { matcher: (context: any) => context.path === '/callback/:id', handler: createAuthMiddleware(async (ctx) => { if (!ctx.context.newSession?.user?.id) { return } // Update session with fresh user data including roles/rel ctx.context.newSession.user = { ...ctx.context.newSession.user, roles: ctx.context.newSession.user.roles, rel: ctx.context.newSession.user.rel, } }), }, ], }, }, ], }), ], }) ``` ### Current vs. Expected behavior Normally I should get the session object and the authorizationUrlParams resource property should be provided to the generic IDP, that way, I receive a full-access token in the client. ### What version of Better Auth are you using? 1.5.5 ### System info ```bash Failed to gather account info [Error [APIError]: Account not found] { status: 'BAD_REQUEST', body: { message: 'Account not found', code: 'ACCOUNT_NOT_FOUND' }, headers: {}, statusCode: 400 } ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, }); ``` ### Additional context This is happening in next.js environment.
GiteaMirror added the oauthbug labels 2026-04-15 19:09:37 -05:00
Author
Owner

@reutersharry-sketch commented on GitHub (Mar 31, 2026):

Hello, a gentle reminder please. Thanks 💯

<!-- gh-comment-id:4162281523 --> @reutersharry-sketch commented on GitHub (Mar 31, 2026): Hello, a gentle reminder please. Thanks 💯
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19808