[GH-ISSUE #3322] Get provider of current session? #9569

Closed
opened 2026-04-13 05:05:22 -05:00 by GiteaMirror · 10 comments
Owner

Originally created by @piscopancer on GitHub (Jul 9, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/3322

Cannot read it from useSession, are there any methods except for when somehow passing a search param with param that i will then read on the server, will I be able to? why? idk

Originally created by @piscopancer on GitHub (Jul 9, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/3322 Cannot read it from `useSession`, are there any methods except for when somehow passing a search param with param that i will then read on the server, will I be able to? why? idk
GiteaMirror added the locked label 2026-04-13 05:05:22 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jul 9, 2025):

You can't get the provider (like Google, GitHub, etc.) of the current session directly from useSession, because the session object in better-auth doesn't include provider info by default—it only has fields like id, userId, and timestamps. Provider info is stored in the account object, not the session, so useSession won't expose it out of the box [code reference].

The standard way to get the provider for the current user is to look up the account(s) for the userId from the session on the server side. This is intentional: a user can have multiple linked accounts/providers, and the session is kept minimal for security and flexibility reasons.

If you really need the provider in the session, you can add it yourself using the additionalFields config and hooks at login, but this is not the default or recommended pattern [docs]. Passing the provider as a search param and reading it on the server is not a standard or secure workaround.

In short: useSession won't give you the provider, and the best practice is to fetch the user's accounts on the server using their userId from the session. If you need provider info on the client, you'll need to explicitly wire it into the session yourself.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

<!-- gh-comment-id:3054061602 --> @dosubot[bot] commented on GitHub (Jul 9, 2025): <!-- Answer --> You can't get the provider (like Google, GitHub, etc.) of the current session directly from useSession, because the session object in better-auth doesn't include provider info by default—it only has fields like id, userId, and timestamps. Provider info is stored in the account object, not the session, so useSession won't expose it out of the box [[code reference]](https://github.com/better-auth/better-auth/blob/b766b22865895f5cdd7dc575b0cc2e023e795740/packages/better-auth/src/db/schema.ts). The standard way to get the provider for the current user is to look up the account(s) for the userId from the session on the server side. This is intentional: a user can have multiple linked accounts/providers, and the session is kept minimal for security and flexibility reasons. If you really need the provider in the session, you can add it yourself using the additionalFields config and hooks at login, but this is not the default or recommended pattern [[docs]](https://github.com/better-auth/better-auth/blob/b766b22865895f5cdd7dc575b0cc2e023e795740/packages/better-auth/src/types/options.ts). Passing the provider as a search param and reading it on the server is not a standard or secure workaround. In short: useSession won't give you the provider, and the best practice is to fetch the user's accounts on the server using their userId from the session. If you need provider info on the client, you'll need to explicitly wire it into the session yourself. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/b077f63a-7146-4afd-b58e-be2b4d757768?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/b077f63a-7146-4afd-b58e-be2b4d757768?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/b077f63a-7146-4afd-b58e-be2b4d757768?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/b077f63a-7146-4afd-b58e-be2b4d757768?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/b077f63a-7146-4afd-b58e-be2b4d757768?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/b077f63a-7146-4afd-b58e-be2b4d757768?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/b077f63a-7146-4afd-b58e-be2b4d757768?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/3322)
Author
Owner

@piscopancer commented on GitHub (Jul 9, 2025):

@dosu alr i found a way to get accounts, but what must the circumstance for 2 accounts to be in the array? I am having oauth only authorization using google and github and I am not planning to use account linking. I guess in my case a user will never have two accounts per their session

<!-- gh-comment-id:3054235558 --> @piscopancer commented on GitHub (Jul 9, 2025): @dosu alr i found a way to get accounts, but what must the circumstance for 2 accounts to be in the array? I am having oauth only authorization using google and github and I am not planning to use account linking. I guess in my case a user will never have two accounts per their session
Author
Owner

@dosubot[bot] commented on GitHub (Jul 9, 2025):

You're correct: with only OAuth (Google and GitHub) and no account linking enabled, a user will never have more than one account per provider, so you won't see multiple accounts in the array for a single userId. Multiple accounts only appear if account linking is explicitly enabled and used—there are no implicit or edge-case scenarios where this happens in your setup. This is by design in better-auth to avoid accidental account merging and keep things predictable for OAuth-only flows [code reference].

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

<!-- gh-comment-id:3054242567 --> @dosubot[bot] commented on GitHub (Jul 9, 2025): <!-- Answer --> You're correct: with only OAuth (Google and GitHub) and no account linking enabled, a user will never have more than one account per provider, so you won't see multiple accounts in the array for a single userId. Multiple accounts only appear if account linking is explicitly enabled and used—there are no implicit or edge-case scenarios where this happens in your setup. This is by design in better-auth to avoid accidental account merging and keep things predictable for OAuth-only flows [[code reference]](https://github.com/better-auth/better-auth/blob/b766b22865895f5cdd7dc575b0cc2e023e795740/packages/better-auth/src/oauth2/link-account.ts). <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/2f396d7d-14e7-4e74-8747-59eb1b49ed9e?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/2f396d7d-14e7-4e74-8747-59eb1b49ed9e?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/2f396d7d-14e7-4e74-8747-59eb1b49ed9e?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/2f396d7d-14e7-4e74-8747-59eb1b49ed9e?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/2f396d7d-14e7-4e74-8747-59eb1b49ed9e?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/2f396d7d-14e7-4e74-8747-59eb1b49ed9e?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/2f396d7d-14e7-4e74-8747-59eb1b49ed9e?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/3322)
Author
Owner

@dagmawibabi commented on GitHub (Jul 10, 2025):

Hi @piscopancer does the answer from @dosubot answer your question?

I've reviewed it and it's accurate!

<!-- gh-comment-id:3058179174 --> @dagmawibabi commented on GitHub (Jul 10, 2025): Hi @piscopancer does the answer from @dosubot answer your question? I've reviewed it and it's accurate!
Author
Owner

@piscopancer commented on GitHub (Jul 10, 2025):

@dagmawibabi yeah i followed dosu's explanation, worked for me where i read oauth data to upsert to the db (i prefer doing it manually)

<!-- gh-comment-id:3058228003 --> @piscopancer commented on GitHub (Jul 10, 2025): @dagmawibabi yeah i followed dosu's explanation, worked for me where i read oauth data to upsert to the db (i prefer doing it manually)
Author
Owner

@dosubot[bot] commented on GitHub (Jul 10, 2025):

Thanks for reviewing and confirming! If you have any other questions or need more details about session providers or account handling, just let me know.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

<!-- gh-comment-id:3058230688 --> @dosubot[bot] commented on GitHub (Jul 10, 2025): <!-- Answer --> Thanks for reviewing and confirming! If you have any other questions or need more details about session providers or account handling, just let me know. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/e6100599-89e7-4995-8654-e16570b06c34?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/e6100599-89e7-4995-8654-e16570b06c34?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/e6100599-89e7-4995-8654-e16570b06c34?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/e6100599-89e7-4995-8654-e16570b06c34?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/e6100599-89e7-4995-8654-e16570b06c34?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/e6100599-89e7-4995-8654-e16570b06c34?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/e6100599-89e7-4995-8654-e16570b06c34?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/3322)
Author
Owner

@bauchdj commented on GitHub (Jul 15, 2025):

@dosu how do I make it so additionalFields show up when I use authclient.useSession()?

<!-- gh-comment-id:3075076677 --> @bauchdj commented on GitHub (Jul 15, 2025): @dosu how do I make it so additionalFields show up when I use authclient.useSession()?
Author
Owner

@dagmawibabi commented on GitHub (Jul 15, 2025):

@bauchdj we're currently building an option to do that for version 1.3, but until then you'd have to create your own database query to fetch the remaining data. But v1.3 will hopefully be released by the end of this week.

<!-- gh-comment-id:3075275455 --> @dagmawibabi commented on GitHub (Jul 15, 2025): @bauchdj we're currently building an option to do that for version 1.3, but until then you'd have to create your own database query to fetch the remaining data. But v1.3 will hopefully be released by the end of this week.
Author
Owner

@matt-roz commented on GitHub (Sep 1, 2025):

For anyone that follows after me; I am using the following workaround with a hook on ctx.path.startsWith('/callback/:id'):

// _zod.ts
export const AUTH_PROVIDERS = {
  TWITCH: 'twitch',
  DISCORD: 'discord'
} as const

export type AuthProvider = (typeof AUTH_PROVIDERS)[keyof typeof AUTH_PROVIDERS]

export const ZBetterAuthProviderResponse = z.object({
  id: z.enum(AUTH_PROVIDERS)
})

export const ZSession = z.object({
  id: z.string(),
  expiresAt: z.date(),
  token: z.string(),
  createdAt: z.date(),
  updatedAt: z.date(),
  ipAddress: z.string().nullable(),
  userAgent: z.string().nullable(),
  userId: z.string(),
  provider_id: z.string().nullable()
})
// auth.ts
export const auth = betterAuth({
  /* ... */
  session: { additionalFields: { provider_id: { type: 'string', required: false, input: false } } },

  /* ... */
  socialProviders: {
    twitch: { clientId: TWITCH_CLIENT_ID, clientSecret: TWITCH_CLIENT_SECRET },
    discord: { clientId: DISCORD_CLIENT_ID, clientSecret: DISCORD_CLIENT_SECRET }
  },

  /* ... */
  hooks: {
    after: createAuthMiddleware(async (ctx) => {
      if (ctx.path.startsWith('/callback/:id')) {
        let prov: z.infer<typeof ZBetterAuthProviderResponse>
        let sess: z.infer<typeof ZSession>
        let accs: Account[] = []

        try {
          prov = ZBetterAuthProviderResponse.parse(ctx.params)
        } catch (error: unknown) {
          const errMsg = stringifyUnknownError(error).unwrapOr('unknown error')
          const statusMsg = `failed to parse provider from params ${JSON.stringify(ctx.params)} as {"id": "providerId"}`
          throw new APIError('BAD_REQUEST', { message: `${statusMsg}: ${errMsg}` })
        }

        try {
          sess = ZSession.parse(ctx.context.newSession?.session)
        } catch (error: unknown) {
          const errMsg = stringifyUnknownError(error).unwrapOr('unknown error')
          const statusMsg = `could not parse session: ${JSON.stringify(ctx.context.newSession?.session)}`
          throw new APIError('EXPECTATION_FAILED', { message: `${statusMsg}: ${errMsg}` })
        }

        if (sess.provider_id !== null) {
          const message = `provider_id already set: ${sess.provider_id}`
          throw new APIError('CONFLICT', { message })
        }

        try {
          accs = await drizzle.select().from(accounts).where(eq(accounts.userId, sess.userId))
        } catch (error: unknown) {
          const errMsg = stringifyUnknownError(error).unwrapOr('unknown error')
          const statusMsg = `could not retrieve accounts: ${JSON.stringify(sess.userId)}`
          throw new APIError('INTERNAL_SERVER_ERROR', { message: `${statusMsg}: ${errMsg}` })
        }

        // user potentially intercepted redirect and manually set params - check if account type exists
        if (!accs.some((acc) => acc.providerId === prov.id)) {
          const message = `account with providerId ${prov.id} not found`
          throw new APIError('NOT_FOUND', { message })
        }

        try {
          // the user has an account with the providerId - update session to reflect
          await drizzle.update(sessions).set({ provider_id: prov.id }).where(eq(sessions.id, sess.id))
        } catch (error: unknown) {
          const errMsg = stringifyUnknownError(error).unwrapOr('unknown error')
          const statusMsg = `Could Not Update Session: ${JSON.stringify(sess.id)}`
          throw new APIError('INTERNAL_SERVER_ERROR', { message: `${statusMsg}: ${errMsg}` })
        }
      }
    })
  }
})
<!-- gh-comment-id:3243211983 --> @matt-roz commented on GitHub (Sep 1, 2025): For anyone that follows after me; I am using the following workaround with a hook on `ctx.path.startsWith('/callback/:id')`: ```typescript // _zod.ts export const AUTH_PROVIDERS = { TWITCH: 'twitch', DISCORD: 'discord' } as const export type AuthProvider = (typeof AUTH_PROVIDERS)[keyof typeof AUTH_PROVIDERS] export const ZBetterAuthProviderResponse = z.object({ id: z.enum(AUTH_PROVIDERS) }) export const ZSession = z.object({ id: z.string(), expiresAt: z.date(), token: z.string(), createdAt: z.date(), updatedAt: z.date(), ipAddress: z.string().nullable(), userAgent: z.string().nullable(), userId: z.string(), provider_id: z.string().nullable() }) ``` ```typescript // auth.ts export const auth = betterAuth({ /* ... */ session: { additionalFields: { provider_id: { type: 'string', required: false, input: false } } }, /* ... */ socialProviders: { twitch: { clientId: TWITCH_CLIENT_ID, clientSecret: TWITCH_CLIENT_SECRET }, discord: { clientId: DISCORD_CLIENT_ID, clientSecret: DISCORD_CLIENT_SECRET } }, /* ... */ hooks: { after: createAuthMiddleware(async (ctx) => { if (ctx.path.startsWith('/callback/:id')) { let prov: z.infer<typeof ZBetterAuthProviderResponse> let sess: z.infer<typeof ZSession> let accs: Account[] = [] try { prov = ZBetterAuthProviderResponse.parse(ctx.params) } catch (error: unknown) { const errMsg = stringifyUnknownError(error).unwrapOr('unknown error') const statusMsg = `failed to parse provider from params ${JSON.stringify(ctx.params)} as {"id": "providerId"}` throw new APIError('BAD_REQUEST', { message: `${statusMsg}: ${errMsg}` }) } try { sess = ZSession.parse(ctx.context.newSession?.session) } catch (error: unknown) { const errMsg = stringifyUnknownError(error).unwrapOr('unknown error') const statusMsg = `could not parse session: ${JSON.stringify(ctx.context.newSession?.session)}` throw new APIError('EXPECTATION_FAILED', { message: `${statusMsg}: ${errMsg}` }) } if (sess.provider_id !== null) { const message = `provider_id already set: ${sess.provider_id}` throw new APIError('CONFLICT', { message }) } try { accs = await drizzle.select().from(accounts).where(eq(accounts.userId, sess.userId)) } catch (error: unknown) { const errMsg = stringifyUnknownError(error).unwrapOr('unknown error') const statusMsg = `could not retrieve accounts: ${JSON.stringify(sess.userId)}` throw new APIError('INTERNAL_SERVER_ERROR', { message: `${statusMsg}: ${errMsg}` }) } // user potentially intercepted redirect and manually set params - check if account type exists if (!accs.some((acc) => acc.providerId === prov.id)) { const message = `account with providerId ${prov.id} not found` throw new APIError('NOT_FOUND', { message }) } try { // the user has an account with the providerId - update session to reflect await drizzle.update(sessions).set({ provider_id: prov.id }).where(eq(sessions.id, sess.id)) } catch (error: unknown) { const errMsg = stringifyUnknownError(error).unwrapOr('unknown error') const statusMsg = `Could Not Update Session: ${JSON.stringify(sess.id)}` throw new APIError('INTERNAL_SERVER_ERROR', { message: `${statusMsg}: ${errMsg}` }) } } }) } }) ```
Author
Owner

@Jackinatox commented on GitHub (Sep 8, 2025):

The answer from @dosu was already useful, but i couldnt figure it out. My Account model only references the user and because i dont have account linking on, i have multiple accounts per user and now way to tell which session they belong to.

model Account {
  id                    String    @id
  accountId             String
  providerId            String
  userId                String
  user                  User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  accessToken           String?
  refreshToken          String?
  idToken               String?
  accessTokenExpiresAt  DateTime?
  refreshTokenExpiresAt DateTime?
  scope                 String?
  password              String?
  createdAt             DateTime  @default(now())
  updatedAt             DateTime  @updatedAt

  @@map("account")
}

Edit: So either I set it up wrong, or i need to modify the session and create a database before hook to save the account in the session.

<!-- gh-comment-id:3267810276 --> @Jackinatox commented on GitHub (Sep 8, 2025): The answer from @dosu was already useful, but i couldnt figure it out. My Account model only references the user and because i dont have account linking on, i have multiple accounts per user and now way to tell which session they belong to. ``` model Account { id String @id accountId String providerId String userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) accessToken String? refreshToken String? idToken String? accessTokenExpiresAt DateTime? refreshTokenExpiresAt DateTime? scope String? password String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@map("account") } ``` Edit: So either I set it up wrong, or i need to modify the session and create a database before hook to save the account in the session.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#9569