auth-backend-module-github-provider: reject refresh for sessions without granted scope

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2025-02-12 12:18:22 +01:00
parent ab9a6fb321
commit b40af03894
3 changed files with 34 additions and 4 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-auth-backend-module-github-provider': patch
---
Fixed a bug where the requested scope was ignored when refreshing sessions for a GitHub OAuth App. This would lead to access tokens being returned that didn't have the requested scope, and in turn errors when trying to use these tokens.
As part of this fix all existing sessions are being revoked in order to ensure that they receive the correct scope.
@@ -42,7 +42,7 @@ describe('githubAuthenticator', () => {
accessToken: 'my-token',
scope: 'user:read',
tokenType: 'bearer',
refreshToken: 'access-token.my-token',
refreshToken: 'access-token-v2.my-token',
},
});
});
@@ -105,9 +105,10 @@ describe('githubAuthenticator', () => {
await expect(
githubAuthenticator.refresh(
{
refreshToken: 'access-token.my-token',
refreshToken: 'access-token-v2.my-token',
req: {} as any,
scope: 'user:read',
scopeAlreadyGranted: true,
},
{
fetchProfile: async _input => ({ id: 'id' } as PassportProfile),
@@ -119,11 +120,28 @@ describe('githubAuthenticator', () => {
accessToken: 'my-token',
scope: 'user:read',
tokenType: 'bearer',
refreshToken: 'access-token.my-token',
refreshToken: 'access-token-v2.my-token',
},
});
});
it('should fail refresh if scope has not already been granted', async () => {
await expect(
githubAuthenticator.refresh(
{
refreshToken: 'access-token-v2.my-token',
req: {} as any,
scope: 'user:read',
},
{
fetchProfile: async _input => ({ id: 'id' } as PassportProfile),
} as PassportOAuthAuthenticatorHelper,
),
).rejects.toThrow(
'Refresh failed, session has not been granted the requested scope',
);
});
it('should refresh with refresh token', async () => {
const res = {};
await expect(
@@ -22,7 +22,7 @@ import {
PassportProfile,
} from '@backstage/plugin-auth-node';
const ACCESS_TOKEN_PREFIX = 'access-token.';
const ACCESS_TOKEN_PREFIX = 'access-token-v2.';
/** @public */
export const githubAuthenticator = createOAuthAuthenticator({
@@ -98,6 +98,11 @@ export const githubAuthenticator = createOAuthAuthenticator({
// refresh token cookie. We use that token to fetch the user profile and
// refresh the Backstage session when needed.
if (input.refreshToken?.startsWith(ACCESS_TOKEN_PREFIX)) {
if (!input.scopeAlreadyGranted) {
throw new Error(
'Refresh failed, session has not been granted the requested scope',
);
}
const accessToken = input.refreshToken.slice(ACCESS_TOKEN_PREFIX.length);
const fullProfile = await helper