auth-{backend,node}: improved error forwarding from passport helpers

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-11-26 19:03:49 +01:00
parent 51bf53cd29
commit c907440f8a
4 changed files with 71 additions and 24 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-auth-backend': patch
'@backstage/plugin-auth-node': patch
---
Improved error forwarding for OAuth refresh endpoints
@@ -272,28 +272,63 @@ describe('PassportStrategyHelper', () => {
);
});
it('should reject with an error if refresh failed', async () => {
class MyCustomOAuth2Error {
getOAuthAccessToken(
_refreshToken: string,
_options: any,
callback: Function,
) {
callback(new Error('Unknown error'));
}
}
it('should forward simple errors', async () => {
class MyCustomRefreshTokenSuccess extends passport.Strategy {
_oauth2 = new MyCustomOAuth2Error();
_oauth2 = new (class {
getOAuthAccessToken(_r: string, _o: any, cb: Function) {
cb(new Error('Unknown error'));
}
})();
}
const mockStrategy = new MyCustomRefreshTokenSuccess();
const refreshTokenPromise = executeRefreshTokenStrategy(
mockStrategy,
'REFRESH_TOKEN',
'a',
await expect(
executeRefreshTokenStrategy(
new MyCustomRefreshTokenSuccess(),
'REFRESH_TOKEN',
'a',
),
).rejects.toThrow(
'Failed to refresh access token; caused by Error: Unknown error',
);
await expect(refreshTokenPromise).rejects.toThrow(
'Failed to refresh access token Error: Unknown error',
});
it('should forward string errors', async () => {
class MyCustomRefreshTokenSuccess extends passport.Strategy {
_oauth2 = new (class {
getOAuthAccessToken(_r: string, _o: any, cb: Function) {
cb('some silly string error');
}
})();
}
await expect(
executeRefreshTokenStrategy(
new MyCustomRefreshTokenSuccess(),
'REFRESH_TOKEN',
'a',
),
).rejects.toThrow(
"Failed to refresh access token; caused by unknown error 'some silly string error'",
);
});
it('should forward object errors', async () => {
class MyCustomRefreshTokenSuccess extends passport.Strategy {
_oauth2 = new (class {
getOAuthAccessToken(_r: string, _o: any, cb: Function) {
cb({ name: 'SomeError', message: 'some message' });
}
})();
}
await expect(
executeRefreshTokenStrategy(
new MyCustomRefreshTokenSuccess(),
'REFRESH_TOKEN',
'a',
),
).rejects.toThrow(
'Failed to refresh access token; caused by SomeError: some message',
);
});
@@ -21,6 +21,7 @@ import { InternalOAuthError } from 'passport-oauth2';
import { ProfileInfo } from '@backstage/plugin-auth-node';
import { PassportProfile } from './types';
import { OAuthStartResponse } from '../../providers/types';
import { ForwardedError } from '@backstage/errors';
export type PassportDoneCallback<Res, Private = never> = (
err?: Error,
@@ -66,7 +67,10 @@ export const makeProfileInfo = (
displayName = decoded.name;
}
} catch (e) {
throw new Error(`Failed to parse id token and get profile info, ${e}`);
throw new ForwardedError(
`Failed to parse id token and get profile info`,
e,
);
}
}
@@ -176,7 +180,7 @@ export const executeRefreshTokenStrategy = async (
params: any,
) => {
if (err) {
reject(new Error(`Failed to refresh access token ${err.toString()}`));
reject(new ForwardedError(`Failed to refresh access token`, err));
}
if (!accessToken) {
reject(
@@ -19,6 +19,7 @@ import { decodeJwt } from 'jose';
import { Strategy } from 'passport';
import { PassportProfile } from './types';
import { ProfileInfo } from '../types';
import { ForwardedError } from '@backstage/errors';
// Re-declared here to avoid direct dependency on passport-oauth2
/** @internal */
@@ -76,7 +77,10 @@ export class PassportHelpers {
displayName = decoded.name;
}
} catch (e) {
throw new Error(`Failed to parse id token and get profile info, ${e}`);
throw new ForwardedError(
`Failed to parse id token and get profile info`,
e,
);
}
}
@@ -191,9 +195,7 @@ export class PassportHelpers {
params: any,
) => {
if (err) {
reject(
new Error(`Failed to refresh access token ${err.toString()}`),
);
reject(new ForwardedError(`Failed to refresh access token`, err));
}
if (!accessToken) {
reject(