keycloak-verify
Version:
Javascript backend library to verify keycloak tokens and get user info
117 lines (106 loc) • 4.28 kB
JavaScript
import { pick } from "ramda";
import axios from "axios";
import jwkToPem from "jwk-to-pem";
import { sign } from "jsonwebtoken";
import Keycloak from "..";
const kid = "2f51904a-442f-4549-af93-0d0a2ab34129";
const keyPair = {
publicJWK: {
keys: [
{
alg: "RS256",
kty: "RSA",
e: "AQAB",
kid,
n:
"f40P5FlkgD1_6BLAHF40dqGef3hskekkjbdLggxcXNZiWkJI6XQ4n-mQut5DcjADzTojCzT8CNIfje4lXoBDmLAUNtaOhh8FbWfUuXHhjqGMUMAWUXW36TC8RMbFQ8S-NxUKchJP-DDnC5hzMahDjtHOa8Qz9I_uEOoezdzPOBE"
}
]
},
privatePEM:
"-----BEGIN RSA PRIVATE KEY-----\n\
MIICWgIBAAKBgH+ND+RZZIA9f+gSwBxeNHahnn94bJHpJI23S4IMXFzWYlpCSOl0\n\
OJ/pkLreQ3IwA806Iws0/AjSH43uJV6AQ5iwFDbWjoYfBW1n1Llx4Y6hjFDAFlF1\n\
t+kwvETGxUPEvjcVCnIST/gw5wuYczGoQ47RzmvEM/SP7hDqHs3czzgRAgMBAAEC\n\
fytYxQ3VU1JBlDZKKP97BFlMk5C+XCc6FDIVGJQZn0ntkX8bB6xO0u+FcKoQ3trv\n\
dltIZqBoYT6eKhsR06FJ9a5xCQfgAqIhAE2qfkLESuzIPJvL7fUl4WJxecf79EiG\n\
a0nwk2j00y02FizgSPPYjrJWMSCiQfg0GVDU57vzZxECQQDbPQJZAI30uoNIt98N\n\
NvM/xe6sDzUUJuW+dli1B/WXlx0DqD5DxzNYt9mcS7vsBs0YtyPCXa8Ix4O+53iT\n\
7KI9AkEAlPBMS2kpx1J4V56KLhKArc4PltuyONkCfnRXlu6KjyHDrbxkQ4tR+rhb\n\
Sj+BHshm0PWMMroDb2izoERheoFuZQJBAKNh7AX369KdzIi8vnVSpiS4lQ2Up7HE\n\
6yHtgF4o+FVoQC8hioVoRlOvb3SS3BEhYGcy1Gtc9bxNM1lplupmRuECQDy4EoKT\n\
5wJprsIZ0j+iL2+sGFLqUig24HtpNuRDb52WqE3GBiI7RDqwuhb0+NDx5mi+EmAD\n\
0a6zwrdN6WemKLkCQQDKhT1ayQCLVbMmO0qfIHMSTPBHIfzHclGURxRk2pjl3LaY\n\
oIcNiLaNvGP9o8/ftYYLr0B7HnGYx7nnz5oiCnln\n\
-----END RSA PRIVATE KEY-----"
};
const payload = {
sub: "user_id",
preferred_username: "user_name",
email: "email@email.com",
name: "name",
nonUserField: "test"
};
const options = { algorithm: "RS256", header: { kid }, expiresIn: "1h" };
const token = sign(payload, keyPair.privatePEM, options);
jest.mock("axios");
axios.get.mockResolvedValue({ data: keyPair.publicJWK });
describe("Keycloak Verify", () => {
describe("verify offline without public key", () => {
it("should get UserInfo", async () => {
const expectedUser = {
id: payload.sub,
userName: payload.preferred_username,
emailVerified: undefined,
resourceAccess: undefined,
email: payload.email,
name: payload.name
};
const config = { realm: "realm", authServerUrl: "url" };
const keycloak = Keycloak(config);
const user = await keycloak.verifyOffline(token);
expect(pick(['id', 'userName', 'emailVerified', 'resourceAccess', 'email', 'name'], user)).toEqual(expectedUser);
});
});
describe("verify offline with public key without cache", () => {
it("should get UserInfo", async () => {
const expectedUser = {
id: payload.sub,
userName: payload.preferred_username,
emailVerified: undefined,
resourceAccess: undefined,
email: payload.email,
name: payload.name
};
const config = { publicKey: jwkToPem(keyPair.publicJWK.keys[0]) };
const keycloak = Keycloak(config);
const user = await keycloak.verifyOffline(token);
expect(pick(['id', 'userName', 'emailVerified', 'resourceAccess', 'email', 'name'], user)).toEqual(expectedUser);
});
it("should not get UserInfo", () => {
const config = { publicKey: "FAKE" };
const keycloak = Keycloak(config);
return expect(keycloak.verifyOffline(token)).rejects.toThrow();
});
it("should get public key twice", async () => {
axios.get.mockClear();
const config = { useCache: false, realm: "realm", authServerUrl: "url" };
const keycloak = Keycloak(config);
const user1 = await keycloak.verifyOffline(token);
const user2 = await keycloak.verifyOffline(token);
expect(user1).toEqual(user2);
expect(axios.get.mock.calls.length).toEqual(2);
});
});
describe("verify offline without public key with cache", () => {
it("should get public key once", async () => {
axios.get.mockClear();
const config = { useCache: true, realm: "realm", authServerUrl: "url" };
const keycloak = Keycloak(config);
const user1 = await keycloak.verifyOffline(token);
const user2 = await keycloak.verifyOffline(token);
expect(user1).toEqual(user2);
expect(axios.get.mock.calls.length).toEqual(1);
});
});
});