blueocean-plugin/blueocean-core-js/src/js/jwt.js

102 lines
3.1 KiB
JavaScript

import es6Promise from 'es6-promise'; es6Promise.polyfill();
import fetch from 'isomorphic-fetch';
import jwt from 'jsonwebtoken';
import { BlueUrl as UrlUtils } from './urlconfig';
import { FetchFunctions } from './fetch';
import { jwk2pem } from 'pem-jwk';
let storedToken = null;
let publicKeyStore = null;
let tokenFetchPromise = null;
const CLOCK_SKEW_SECONDS = 60;
export default {
/**
* Fetches the JWT token. This token is cached for a default of 25mins.
* If it is within 5mins or expiry it will fetch a new one.
*/
fetchJWT() {
if (storedToken && storedToken.exp) {
const diff = storedToken.exp - Math.trunc(new Date().getTime() / 1000);
// refetch token if we are within 60s of it exp
if (diff < CLOCK_SKEW_SECONDS) {
tokenFetchPromise = null;
}
}
if (!tokenFetchPromise) {
tokenFetchPromise = fetch(`${UrlUtils.getJenkinsRootURL()}/jwt-auth/token`, { credentials: 'same-origin' })
.then(this.checkStatus)
.then(response => {
const token = response.headers.get('X-BLUEOCEAN-JWT');
if (token) {
return token;
}
throw new Error('Could not fetch jwt_token');
});
}
return tokenFetchPromise;
},
/**
* Verifies the token using the public key.
*/
verifyToken(token, certObject) {
return new Promise((resolve, reject) =>
jwt.verify(token, jwk2pem(certObject), { algorithms: [certObject.alg], clockTolerance: CLOCK_SKEW_SECONDS }, (err, payload) => {
if (err) {
reject(err);
} else {
resolve(payload);
}
}));
},
/**
* Fetches the public key that is used to verify tokens.
*/
fetchJWTPublicKey(token) {
const decoded = jwt.decode(token, { complete: true });
const url = `${UrlUtils.getJenkinsRootURL()}/jwt-auth/jwks/${decoded.header.kid}/`;
if (!publicKeyStore) {
publicKeyStore = fetch(url, { credentials: 'same-origin' })
.then(FetchFunctions.checkStatus)
.then(FetchFunctions.parseJSON)
.then(cert => this.verifyToken(token, cert)
.then(payload => ({
token,
payload,
})));
}
return publicKeyStore;
},
/**
* Puts the token into global storage for later use.
*/
storeToken(data) {
storedToken = data.payload;
return data;
},
/**
* Use this function if you want the payload from the token.
*/
getTokenWithPayload() {
return this.fetchJWT()
.then(FetchFunctions.checkStatus)
.then(token => this.fetchJWTPublicKey(token))
.then(data => this.storeToken(data));
},
/**
* Gets the token from te server and verifies it.
*/
getToken() {
return this.getTokenWithPayload().then(token => token.token);
},
};