Skip to content

Commit

Permalink
bruk oasis for obo tokenx
Browse files Browse the repository at this point in the history
  • Loading branch information
kenglxn committed Sep 20, 2024
1 parent 20a4c3a commit 1993a98
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 98 deletions.
101 changes: 57 additions & 44 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@
"type": "module",
"main": "server.js",
"dependencies": {
"casual": "^1.6.2",
"@navikt/oasis": "3.4.0",
"express": "^4.20.0",
"express-http-proxy": "2.0.0",
"http-proxy-middleware": "3.0.0",
"http-terminator": "3.2.0",
"jose": "5.6.2",
"jsdom": "^24.1.0",
"mustache": "^4.2.0",
"openid-client": "5.6.5",
"prom-client": "^14.2.0",
"prometheus-api-metrics": "^3.2.2",
"winston": "3.13.0"
Expand Down
66 changes: 15 additions & 51 deletions server/tokenx.js
Original file line number Diff line number Diff line change
@@ -1,77 +1,41 @@
import { Issuer, errors } from 'openid-client';
import { decodeJwt } from 'jose';

const { TOKEN_X_WELL_KNOWN_URL, TOKEN_X_CLIENT_ID, TOKEN_X_PRIVATE_JWK } = process.env;

const exchangeToken = async (tokenxClient, { subject_token, audience }) => {
return await tokenxClient.grant(
{
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
client_assertion_type: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
subject_token_type: 'urn:ietf:params:oauth:token-type:jwt',
audience,
subject_token,
},
{
clientAssertionPayload: {
nbf: Math.floor(Date.now() / 1000),
// TokenX only allows a single audience
aud: [tokenxClient?.issuer.metadata.token_endpoint],
},
}
);
};

const createTokenXClient = async () => {
const issuer = await Issuer.discover(TOKEN_X_WELL_KNOWN_URL);
return new issuer.Client(
{
client_id: TOKEN_X_CLIENT_ID,
token_endpoint_auth_method: 'private_key_jwt',
},
{ keys: [JSON.parse(TOKEN_X_PRIVATE_JWK)] }
);
};
import { getToken, requestOboToken, validateToken } from '@navikt/oasis';

/**
* onProxyReq does not support async, so using middleware for tokenx instead
* ref: https://github.com/chimurai/http-proxy-middleware/issues/318
*/
export const tokenXMiddleware =
({ audience, tokenXClientPromise = audience ? createTokenXClient() : null, log }) =>
({ audience, log }) =>
async (req, res, next) => {
try {
if (!audience) {
next();
return;
}

const subject_token = (req.headers.authorization || '').replace('Bearer', '').trim();
const subject_token = getToken(req);
if (subject_token === '') {
log.info('no authorization header found, skipping tokenx.');
log.info('no subject_token found, skipping tokenx.');
next();
return;
}

const { exp } = decodeJwt(subject_token);
if (!exp || exp * 1000 <= Date.now()) {
log.info('unauthorized request. subject_token is expired.');
const validation = await validateToken(subject_token);
if (!validation.ok) {
log.info('unauthorized request. subject_token is invalid.');
res.status(401).send();
return;
}

const { access_token } = await exchangeToken(await tokenXClientPromise, {
subject_token,
audience,
});
req.headers.authorization = `Bearer ${access_token}`;
next();
} catch (err) {
if (err instanceof errors.OPError) {
log.info(`token exchange feilet ${err.message}`, err);
const obo = await requestOboToken(subject_token, audience);
if (!obo.ok) {
log.error('token exchange failed. could not obtain obo token.');
res.status(401).send();
} else {
next(err);
return;
}
req.headers.authorization = `Bearer ${obo.token}`;
next();
} catch (err) {
next(err);
}
};

0 comments on commit 1993a98

Please sign in to comment.