keycloak-verify
Version:
Javascript backend library to verify keycloak tokens and get user info
103 lines (88 loc) • 2.27 kB
JavaScript
import axios from "axios";
import { prop, path, find, compose, flip, curryN } from "ramda";
import jwt from "jsonwebtoken";
import jwkToPem from "jwk-to-pem";
const cache = {};
const verifyOnline = ({ realm, authServerUrl }) => (
accessToken,
options = {}
) =>
axios
.get(
`${authServerUrl}/auth/realms/${options.realm ||
realm}/protocol/openid-connect/userinfo`,
{
headers: { Authorization: `Bearer ${accessToken}` }
}
)
.then(prop("data"))
.then(makeUser);
const makeUser = ({
sub,
preferred_username,
email_verified,
resource_access,
email,
name,
...others
}) => ({
id: sub,
userName: preferred_username,
emailVerified: email_verified,
resourceAccess: resource_access,
email,
name,
...others,
});
const verify = curryN(2)(jwt.verify);
const isTheRightKid = kid => publicKey => publicKey.kid === kid;
const findPublicKeyFromKid = publicKey => kid =>
find(isTheRightKid(kid))(publicKey);
const getKid = path(["header", "kid"]);
const decode = compose(
curryN(2),
flip
)(jwt.decode);
const getUserFromPublicKey = token =>
compose(
makeUser,
verify(token)
);
const getUserFromJWK = token => jwk =>
compose(
getUserFromPublicKey(token),
jwkToPem,
findPublicKeyFromKid(jwk),
getKid,
decode({ complete: true })
)(token);
const fetchPublicKeys = ({ realm, authServerUrl, useCache }) => {
const url = `${authServerUrl}/auth/realms/${realm}/protocol/openid-connect/certs`;
const key = url;
if (useCache) {
return cache[key]
? Promise.resolve(cache[key])
: axios
.get(url)
.then(path(["data", "keys"]))
.then(publicKey => {
cache[key] = publicKey;
return publicKey;
});
} else {
return axios.get(url).then(path(["data", "keys"]));
}
};
const verifyOffline = config => async (accessToken, options = {}) => {
const { publicKey } = config;
return publicKey
? getUserFromPublicKey(accessToken)(publicKey)
: fetchPublicKeys({ ...config, ...options }).then(
getUserFromJWK(accessToken)
);
};
const Keycloak = config => ({
verifyOnline: verifyOnline(config),
verifyOffline: verifyOffline(config)
});
export default Keycloak;