Extract rfc8693 tokenexchange logic to a helper function

Signed-off-by: Ruben Vallejo <rvallejo@vmware.com>
Co-authored-by: Jamie Klassen <jklassen@vmware.com>
This commit is contained in:
Ruben Vallejo
2023-09-12 15:05:37 -04:00
parent ae34255836
commit b497b6e9f3
5 changed files with 61 additions and 44 deletions
@@ -15,7 +15,7 @@
*/
import { createBackend } from '@backstage/backend-defaults';
import { authPlugin } from '@backstage/plugin-auth-backend';
import authPlugin from '@backstage/plugin-auth-backend';
import { authModulePinnipedProvider } from '../src';
const backend = createBackend();
@@ -415,7 +415,7 @@ describe('pinnipedAuthenticator', () => {
await expect(
pinnipedAuthenticator.authenticate(handlerRequest, implementation),
).rejects.toThrow(
`Failed to get cluster specific ID token for "test_cluster", RFC8693 token exchange failed with error: NetworkError: Connection timed out`,
`Failed to get cluster specific ID token for "test_cluster": Error: RFC8693 token exchange failed with error: NetworkError: Connection timed out`,
);
});
@@ -19,7 +19,39 @@ import {
decodeOAuthState,
encodeOAuthState,
} from '@backstage/plugin-auth-node';
import { Issuer, TokenSet, Strategy as OidcStrategy } from 'openid-client';
import {
Client,
Issuer,
TokenSet,
Strategy as OidcStrategy,
} from 'openid-client';
const rfc8693TokenExchange = async ({
subject_token,
target_audience,
ctx,
}: {
subject_token: string;
target_audience: string;
ctx: Promise<{
providerStrategy: OidcStrategy<{}>;
client: Client;
}>;
}): Promise<string | undefined> => {
const { client } = await ctx;
return client
.grant({
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
subject_token,
audience: target_audience,
subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',
requested_token_type: 'urn:ietf:params:oauth:token-type:jwt',
})
.then(tokenset => tokenset.access_token)
.catch(err => {
throw new Error(`RFC8693 token exchange failed with error: ${err}`);
});
};
/** @public */
export const pinnipedAuthenticator = createOAuthAuthenticator({
@@ -39,7 +71,7 @@ export const pinnipedAuthenticator = createOAuthAuthenticator({
scope: config.getOptionalString('scope') || '',
id_token_signed_response_alg: 'ES256',
});
const strategy = new OidcStrategy(
const providerStrategy = new OidcStrategy(
{
client,
passReqToCallback: false,
@@ -57,11 +89,11 @@ export const pinnipedAuthenticator = createOAuthAuthenticator({
},
);
return { strategy, client };
return { providerStrategy, client };
},
async start(input, implementation) {
const { strategy } = await implementation;
async start(input, ctx) {
const { providerStrategy } = await ctx;
const stringifiedAudience = input.req.query?.audience as string;
const decodedState = decodeOAuthState(input.state);
const state = { ...decodedState, audience: stringifiedAudience };
@@ -73,6 +105,7 @@ export const pinnipedAuthenticator = createOAuthAuthenticator({
};
return new Promise((resolve, reject) => {
const strategy = Object.create(providerStrategy);
strategy.redirect = (url: string) => {
resolve({ url });
};
@@ -83,8 +116,8 @@ export const pinnipedAuthenticator = createOAuthAuthenticator({
});
},
async authenticate(input, implementation) {
const { strategy, client } = await implementation;
async authenticate(input, ctx) {
const { providerStrategy } = await ctx;
const { req } = input;
const { searchParams } = new URL(req.url, 'https://pinniped.com');
const stateParam = searchParams.get('state');
@@ -93,25 +126,20 @@ export const pinnipedAuthenticator = createOAuthAuthenticator({
: undefined;
return new Promise((resolve, reject) => {
strategy.success = user => {
const strategy = Object.create(providerStrategy);
strategy.success = (user: any) => {
(audience
? client
.grant({
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
subject_token: user.tokenset.access_token,
audience,
subject_token_type:
'urn:ietf:params:oauth:token-type:access_token',
requested_token_type: 'urn:ietf:params:oauth:token-type:jwt',
})
.then(tokenset => tokenset.access_token)
.catch(err =>
reject(
new Error(
`Failed to get cluster specific ID token for "${audience}", RFC8693 token exchange failed with error: ${err}`,
),
? rfc8693TokenExchange({
subject_token: user.tokenset.access_token,
target_audience: audience,
ctx,
}).catch(err =>
reject(
new Error(
`Failed to get cluster specific ID token for "${audience}": ${err}`,
),
)
),
)
: Promise.resolve(user.tokenset.id_token)
).then(idToken => {
resolve({
@@ -127,7 +155,7 @@ export const pinnipedAuthenticator = createOAuthAuthenticator({
});
};
strategy.fail = info => {
strategy.fail = (info: any) => {
reject(new Error(`Authentication rejected, ${info.message || ''}`));
};
@@ -143,8 +171,8 @@ export const pinnipedAuthenticator = createOAuthAuthenticator({
});
},
async refresh(input, implementation) {
const { client } = await implementation;
async refresh(input, ctx) {
const { client } = await ctx;
const tokenset = await client.refresh(input.refreshToken);
return new Promise((resolve, reject) => {
-2
View File
@@ -54,9 +54,7 @@
"@types/passport": "^1.0.3",
"compression": "^1.7.4",
"connect-session-knex": "^3.0.1",
"cookie": "^0.5.0",
"cookie-parser": "^1.4.5",
"cookie-signature": "^1.2.1",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
+4 -13
View File
@@ -5036,9 +5036,7 @@ __metadata:
"@types/xml2js": ^0.4.7
compression: ^1.7.4
connect-session-knex: ^3.0.1
cookie: ^0.5.0
cookie-parser: ^1.4.5
cookie-signature: ^1.2.1
cors: ^2.8.5
express: ^4.17.1
express-promise-router: ^4.1.0
@@ -22775,13 +22773,6 @@ __metadata:
languageName: node
linkType: hard
"cookie-signature@npm:^1.2.1":
version: 1.2.1
resolution: "cookie-signature@npm:1.2.1"
checksum: bb464aacac390b5d7d8ead2d6fff7c1c3b7378c7d0250921f48923fe889688e081ab33950448929db5f24d4f9f1506589a7ee1c685de8f12a3fdb30c49667ec5
languageName: node
linkType: hard
"cookie@npm:0.4.1":
version: 0.4.1
resolution: "cookie@npm:0.4.1"
@@ -22796,7 +22787,7 @@ __metadata:
languageName: node
linkType: hard
"cookie@npm:0.5.0, cookie@npm:^0.5.0, cookie@npm:~0.5.0":
"cookie@npm:0.5.0, cookie@npm:~0.5.0":
version: 0.5.0
resolution: "cookie@npm:0.5.0"
checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180
@@ -30257,9 +30248,9 @@ __metadata:
linkType: hard
"jose@npm:^4.14.6":
version: 4.14.6
resolution: "jose@npm:4.14.6"
checksum: eae81a234e7bf1446b1bd80722b3462b014e3835b155c3a7799c1c5043163a53a0dc28d347004151b031e6b7b863403aabf8814d9cc217ce21f8c2f3ebd4b335
version: 4.15.2
resolution: "jose@npm:4.15.2"
checksum: 8f0cab1eef31243abe14a935b2b330cd95f10f9b69808fd642088ae5000e50e566664934537d2c6413ab2f6b54acd8265a5033da05157aa1260c5f1d7e57fab0
languageName: node
linkType: hard