auth-node: add scopeAlreadyGranted field
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-auth-node': patch
|
||||
---
|
||||
|
||||
Added `scopeAlreadyGranted` property to `OAuthAuthenticatorRefreshInput`, signaling to the provider whether the requested scope has already been granted when persisting session scope.
|
||||
@@ -326,6 +326,7 @@ export interface OAuthAuthenticatorRefreshInput {
|
||||
req: Request_2;
|
||||
// (undocumented)
|
||||
scope: string;
|
||||
scopeAlreadyGranted?: boolean;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -193,6 +193,55 @@ describe('CookieScopeManager', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should signal whether persisted scopes have already been granted when refreshing', async () => {
|
||||
const getGrantedScopes = jest.fn();
|
||||
const manager = CookieScopeManager.create({
|
||||
authenticator: {
|
||||
scopes: {
|
||||
persist: true,
|
||||
} as OAuthAuthenticatorScopeOptions,
|
||||
} as OAuthAuthenticator<any, any>,
|
||||
cookieManager: {
|
||||
getGrantedScopes,
|
||||
} as unknown as OAuthCookieManager,
|
||||
});
|
||||
|
||||
getGrantedScopes.mockReturnValue('x y');
|
||||
await expect(manager.refresh(makeReq('x,y'))).resolves.toEqual({
|
||||
scope: 'x y',
|
||||
scopeAlreadyGranted: true,
|
||||
commit: expect.any(Function),
|
||||
});
|
||||
|
||||
getGrantedScopes.mockReturnValueOnce('x y');
|
||||
await expect(manager.refresh(makeReq('x'))).resolves.toEqual({
|
||||
scope: 'x y',
|
||||
scopeAlreadyGranted: true,
|
||||
commit: expect.any(Function),
|
||||
});
|
||||
|
||||
getGrantedScopes.mockReturnValueOnce('x y');
|
||||
await expect(manager.refresh(makeReq('x,y,z'))).resolves.toEqual({
|
||||
scope: 'x y z',
|
||||
scopeAlreadyGranted: false,
|
||||
commit: expect.any(Function),
|
||||
});
|
||||
|
||||
getGrantedScopes.mockReturnValueOnce('');
|
||||
await expect(manager.refresh(makeReq('x,y'))).resolves.toEqual({
|
||||
scope: 'x y',
|
||||
scopeAlreadyGranted: false,
|
||||
commit: expect.any(Function),
|
||||
});
|
||||
|
||||
getGrantedScopes.mockReturnValueOnce(undefined);
|
||||
await expect(manager.refresh(makeReq('x,y'))).resolves.toEqual({
|
||||
scope: 'x y',
|
||||
scopeAlreadyGranted: false,
|
||||
commit: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
it('should use custom scope transform', async () => {
|
||||
const manager = CookieScopeManager.create({
|
||||
additionalScopes: ['b'],
|
||||
|
||||
@@ -138,6 +138,7 @@ export class CookieScopeManager {
|
||||
|
||||
async refresh(req: express.Request): Promise<{
|
||||
scope: string;
|
||||
scopeAlreadyGranted?: boolean;
|
||||
commit(result: OAuthAuthenticatorResult<any>): Promise<string>;
|
||||
}> {
|
||||
const requestScope = splitScope(req.query.scope?.toString());
|
||||
@@ -147,6 +148,9 @@ export class CookieScopeManager {
|
||||
|
||||
return {
|
||||
scope,
|
||||
scopeAlreadyGranted: this.cookieManager
|
||||
? hasScopeBeenGranted(grantedScope, scope)
|
||||
: undefined,
|
||||
commit: async result => {
|
||||
if (this.cookieManager) {
|
||||
this.cookieManager.setGrantedScopes(
|
||||
@@ -162,3 +166,16 @@ export class CookieScopeManager {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function hasScopeBeenGranted(
|
||||
grantedScope: Iterable<string>,
|
||||
requestedScope: string,
|
||||
): boolean {
|
||||
const granted = new Set(grantedScope);
|
||||
for (const requested of splitScope(requestedScope)) {
|
||||
if (!granted.has(requested)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -720,6 +720,7 @@ describe('createOAuthRouteHandlers', () => {
|
||||
req: expect.anything(),
|
||||
refreshToken: 'refresh-token',
|
||||
scope: 'persisted-scope',
|
||||
scopeAlreadyGranted: true,
|
||||
},
|
||||
{ ctx: 'authenticator' },
|
||||
);
|
||||
|
||||
@@ -315,7 +315,12 @@ export function createOAuthRouteHandlers<TProfile>(
|
||||
const scopeRefresh = await scopeManager.refresh(req);
|
||||
|
||||
const result = await authenticator.refresh(
|
||||
{ req, scope: scopeRefresh.scope, refreshToken },
|
||||
{
|
||||
req,
|
||||
scope: scopeRefresh.scope,
|
||||
scopeAlreadyGranted: scopeRefresh.scopeAlreadyGranted,
|
||||
refreshToken,
|
||||
},
|
||||
authenticatorCtx,
|
||||
);
|
||||
|
||||
|
||||
@@ -59,6 +59,11 @@ export interface OAuthAuthenticatorAuthenticateInput {
|
||||
|
||||
/** @public */
|
||||
export interface OAuthAuthenticatorRefreshInput {
|
||||
/**
|
||||
* Signals whether the requested scope has already been granted for the session. Will only be set if the `scopes.persist` option is enabled.
|
||||
*/
|
||||
scopeAlreadyGranted?: boolean;
|
||||
|
||||
scope: string;
|
||||
refreshToken: string;
|
||||
req: Request;
|
||||
|
||||
Reference in New Issue
Block a user