[GH-ISSUE #1889] OAuth2 Account Linking doesn't pass parameters #8963

Closed
opened 2026-04-13 04:12:35 -05:00 by GiteaMirror · 0 comments
Owner

Originally created by @waleedlatif1 on GitHub (Mar 19, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/1889

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Have a user with an existing account
  2. Create a genericOAuth provider with prompt: 'consent' and accessType: 'offline'
  3. Use client.oauth2.link to link some new credentials to that existing user
  4. Notice that the refresh token was not given in the response back

Current vs. Expected behavior

I expect the response from the OAuth provider to include a refresh token, but it doesn't.

What version of Better Auth are you using?

1.2.4

Provide environment information

- OS: MacOS
- Browser: Brave

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

Backend

Auth config (if applicable)

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: 'pg',
    schema,
  }),
  session: {
    cookieCache: {
      enabled: true,
      maxAge: 5 * 60, // Cache duration (5 minutes)
    },
  },
  account: {
    accountLinking: {
      enabled: true,
      allowDifferentEmails: true,
      trustedProviders: ['google', 'email-password'],
    },
  },
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
      scopes: [
        'https://www.googleapis.com/auth/userinfo.email',
        'https://www.googleapis.com/auth/userinfo.profile',
      ],
    },
  },
  plugins: [
    nextCookies(),
    genericOAuth({
      config: [
        // Google providers for different purposes
        {
          providerId: 'google-email',
          clientId: process.env.GOOGLE_CLIENT_ID as string,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
          discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration',
          accessType: 'offline',
          scopes: [
            'https://www.googleapis.com/auth/userinfo.email',
            'https://www.googleapis.com/auth/userinfo.profile',
            'https://www.googleapis.com/auth/gmail.send',
            'https://www.googleapis.com/auth/gmail.readonly',
            'https://www.googleapis.com/auth/gmail.labels',
          ],
          prompt: 'consent',
          redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/google-email`,
        },
        {
          providerId: 'google-calendar',
          clientId: process.env.GOOGLE_CLIENT_ID as string,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
          discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration',
          accessType: 'offline',
          scopes: [
            'https://www.googleapis.com/auth/userinfo.email',
            'https://www.googleapis.com/auth/userinfo.profile',
            'https://www.googleapis.com/auth/calendar',
          ],
          prompt: 'consent',
          redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/google-calendar`,
        },
        {
          providerId: 'google-drive',
          clientId: process.env.GOOGLE_CLIENT_ID as string,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
          discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration',
          accessType: 'offline',
          scopes: [
            'https://www.googleapis.com/auth/userinfo.email',
            'https://www.googleapis.com/auth/userinfo.profile',
            'https://www.googleapis.com/auth/drive',
            'https://www.googleapis.com/auth/drive.file',
          ],
          prompt: 'consent',
          redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/google-drive`,
        },
        {
          providerId: 'google-docs',
          clientId: process.env.GOOGLE_CLIENT_ID as string,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
          discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration',
          accessType: 'offline',
          scopes: [
            'https://www.googleapis.com/auth/userinfo.email',
            'https://www.googleapis.com/auth/userinfo.profile',
            'https://www.googleapis.com/auth/documents',
            'https://www.googleapis.com/auth/drive.file',
          ],
          prompt: 'consent',
          redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/google-docs`,
        },
        {
          providerId: 'google-sheets',
          clientId: process.env.GOOGLE_CLIENT_ID as string,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
          discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration',
          accessType: 'offline',
          scopes: [
            'https://www.googleapis.com/auth/userinfo.email',
            'https://www.googleapis.com/auth/userinfo.profile',
            'https://www.googleapis.com/auth/spreadsheets',
            'https://www.googleapis.com/auth/drive.file',
          ],
          prompt: 'consent',
          redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/google-sheets`,
        },
      ],
    }),
  ],
  pages: {
    signIn: '/login',
    signUp: '/signup',
    error: '/error',
    verify: '/verify',
    verifyRequest: '/verify-request',
  },
})

// Server-side auth helpers
export async function getSession() {
  return await auth.api.getSession({
    headers: await headers(),
  })
}

export const signIn = auth.api.signInEmail
export const signUp = auth.api.signUpEmail

Additional context

The reason I need this fixed is because my application relies heavily on the account linking functionality, and if we don't get a refresh token when we link accounts then we always need to detach/reattach the credentials every time it expires. This creates a terrible UX for my users.

Originally created by @waleedlatif1 on GitHub (Mar 19, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/1889 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Have a user with an existing account 2. Create a genericOAuth provider with `prompt: 'consent'` and `accessType: 'offline'` 3. Use `client.oauth2.link` to link some new credentials to that existing user 4. Notice that the refresh token was not given in the response back ### Current vs. Expected behavior I expect the response from the OAuth provider to include a refresh token, but it doesn't. ### What version of Better Auth are you using? 1.2.4 ### Provide environment information ```bash - OS: MacOS - Browser: Brave ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript export const auth = betterAuth({ database: drizzleAdapter(db, { provider: 'pg', schema, }), session: { cookieCache: { enabled: true, maxAge: 5 * 60, // Cache duration (5 minutes) }, }, account: { accountLinking: { enabled: true, allowDifferentEmails: true, trustedProviders: ['google', 'email-password'], }, }, socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, scopes: [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', ], }, }, plugins: [ nextCookies(), genericOAuth({ config: [ // Google providers for different purposes { providerId: 'google-email', clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration', accessType: 'offline', scopes: [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/gmail.send', 'https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/gmail.labels', ], prompt: 'consent', redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/google-email`, }, { providerId: 'google-calendar', clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration', accessType: 'offline', scopes: [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/calendar', ], prompt: 'consent', redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/google-calendar`, }, { providerId: 'google-drive', clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration', accessType: 'offline', scopes: [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.file', ], prompt: 'consent', redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/google-drive`, }, { providerId: 'google-docs', clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration', accessType: 'offline', scopes: [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/documents', 'https://www.googleapis.com/auth/drive.file', ], prompt: 'consent', redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/google-docs`, }, { providerId: 'google-sheets', clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, discoveryUrl: 'https://accounts.google.com/.well-known/openid-configuration', accessType: 'offline', scopes: [ 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/spreadsheets', 'https://www.googleapis.com/auth/drive.file', ], prompt: 'consent', redirectURI: `${process.env.NEXT_PUBLIC_APP_URL}/api/auth/oauth2/callback/google-sheets`, }, ], }), ], pages: { signIn: '/login', signUp: '/signup', error: '/error', verify: '/verify', verifyRequest: '/verify-request', }, }) // Server-side auth helpers export async function getSession() { return await auth.api.getSession({ headers: await headers(), }) } export const signIn = auth.api.signInEmail export const signUp = auth.api.signUpEmail ``` ### Additional context The reason I need this fixed is because my application relies heavily on the account linking functionality, and if we don't get a refresh token when we link accounts then we always need to detach/reattach the credentials every time it expires. This creates a terrible UX for my users.
GiteaMirror added the lockedbug labels 2026-04-13 04:12:35 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#8963