-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Improved OAuth2 access token provider with caching and support for refresh token; which do not rely on asynchronous cache invalidation.
Code
const DEFAULT_LATENCY_MS = 1500;
export const oauth2AccessTokenProvider = ({newTokensProvider, refreshedTokensProvider}) => withNewSession(provideAccessToken({
startSession: sessionStarter(newTokensProvider),
refreshSession: refreshedTokensProvider && sessionRefresher(refreshedTokensProvider),
}));
const withNewSession = (fn) => fn({});
const provideAccessToken = ({startSession, refreshSession}) => (session) => () => (isValid(session) ? getAccessToken : refreshSession && canRefresh(session) ? refreshSession : startSession)(session);
const updateSession = (session) => (tokens) => Object.assign(session, tokens, expiryTimes(tokens));
const expiryTimes = (tokens) => ({
expires_at: expireAt(tokens.expires_in),
refresh_expires_at: expireAt(tokens.refresh_expires_in),
});
const sToMs = (x) => x * 1000;
const withLatency = (x) => x - DEFAULT_LATENCY_MS;
const expireAt = (durationSeconds) => withLatency(Date.now() + sToMs(durationSeconds));
const isFuture = (timestampMs) => timestampMs && Date.now() < timestampMs;
const isValid = (session) => isFuture(session.expires_at);
const canRefresh = (session) => isFuture(session.refresh_expires_at);
const sessionStarter = (tokensProvider) => (session) => tokensProvider().then(updateSession(session)).then(getAccessToken);
const sessionRefresher = (tokensProvider) => (session) => tokensProvider(getRefreshToken(session)).then(updateSession(session)).then(getAccessToken);
const getAccessToken = (session) => Promise.resolve(session.access_token);
const getRefreshToken = (session) => session.refresh_token;Example
import axios from 'axios';
import {oauth2AccessTokenProvider} from '@quickcase/node-toolkit';
// FIXME use config
const tokenEndpoint = 'http://keycloak:8080/auth/realms/master/protocol/openid-connect/token';
const username = 'quickcase';
const password = 'pass';
const urlSearchParams = (params) => new URLSearchParams(params);
const extractData = (res) => res.data;
const newTokensProvider = () => axios.post(tokenEndpoint, urlSearchParams({
grant_type: 'password',
client_id: 'admin-cli',
username,
password,
})).then(extractData);
const refreshedTokensProvider = (refreshToken) => axios.post(tokenEndpoint, urlSearchParams({
grant_type: 'refresh_token',
client_id: 'admin-cli',
refresh_token: refreshToken,
})).then(extractData);
const tokenProvider = oauth2AccessTokenProvider({
newTokensProvider,
refreshedTokensProvider,
});
let c = '';
setInterval(() => {
tokenProvider().then(token => {
if (c === token) {
console.log(`${new Date().toISOString()} ---`);
} else {
console.log(`${new Date().toISOString()} NEW`);
}
c = token;
});
}, 5000);Metadata
Metadata
Assignees
Labels
type:featureFeature requestFeature request