Should better-auth invalidate tokens on unlinkAccount? #2929

Open
opened 2026-03-13 10:28:10 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @rosano on GitHub (Feb 27, 2026).

Is this suited for github?

  • Yes, this is suited for github

It seems like the github accessToken for a linked account is still valid after unlinking the account. Considering that github oauth tokens never expire, should they be invalidated or is that fine as is?

Describe the solution you'd like

Invalidate the token so that it can't be reused somehow.

Describe alternatives you've considered

I might encrypt the tokens to minimize leaks.

Additional context

No response

Originally created by @rosano on GitHub (Feb 27, 2026). ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. It seems like the github `accessToken` for a linked account is still valid after unlinking the account. Considering that github oauth tokens never expire, should they be invalidated or is that fine as is? ### Describe the solution you'd like Invalidate the token so that it can't be reused somehow. ### Describe alternatives you've considered I might encrypt the tokens to minimize leaks. ### Additional context _No response_
GiteaMirror added the enhancement label 2026-03-13 10:28:10 -05:00
Author
Owner

@rosano commented on GitHub (Feb 27, 2026):

Currently just using a before hook like this:

import { createAuthMiddleware } from 'better-auth/api';

export const auth = betterAuth({

  // ...

  hooks: {
    before: createAuthMiddleware(async ctx => {
      if (ctx.path === '/unlink-account') {
        const { accessToken } = await auth.api.getAccessToken({
          body: {
            accountId: ctx.body.id,
            providerId: ctx.body.providerId,
          },
          headers: ctx.headers,
        });

        const clientId = process.env.GITHUB_CLIENT_ID;
        const clientSecret = process.env.GITHUB_CLIENT_SECRET;

        const credentials = btoa(`${ clientId }:${ clientSecret }`);
        fetch({
          url: `https://api.github.com/applications/${ clientId }/token`,
          method: 'DELETE',
          headers: {
            'Authorization': `Basic ${ credentials }`,
          },
          body: JSON.stringify({
            access_token: accessToken,
          }),
        })
      }
    }),
  },
})

I guess this gets more annoying and error-prone with more providers so probably nicer for better-auth to 'tidy up' on unlink.

@rosano commented on GitHub (Feb 27, 2026): Currently just using a before hook like this: ```javascript import { createAuthMiddleware } from 'better-auth/api'; export const auth = betterAuth({ // ... hooks: { before: createAuthMiddleware(async ctx => { if (ctx.path === '/unlink-account') { const { accessToken } = await auth.api.getAccessToken({ body: { accountId: ctx.body.id, providerId: ctx.body.providerId, }, headers: ctx.headers, }); const clientId = process.env.GITHUB_CLIENT_ID; const clientSecret = process.env.GITHUB_CLIENT_SECRET; const credentials = btoa(`${ clientId }:${ clientSecret }`); fetch({ url: `https://api.github.com/applications/${ clientId }/token`, method: 'DELETE', headers: { 'Authorization': `Basic ${ credentials }`, }, body: JSON.stringify({ access_token: accessToken, }), }) } }), }, }) ``` I guess this gets more annoying and error-prone with more providers so probably nicer for better-auth to 'tidy up' on unlink.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2929