UNPKG

keycloak-lambda-authorizer

Version:
224 lines 12.8 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultClientAuthorization = void 0; const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const jws_1 = __importDefault(require("jws")); const uuid_1 = require("uuid"); const Options_1 = require("../Options"); const KeycloakUtils_1 = require("../utils/KeycloakUtils"); const TokenUtils_1 = require("../utils/TokenUtils"); class DefaultClientAuthorization { constructor(options) { this.options = options; } clientAuthentication0(requestContent, token) { return __awaiter(this, void 0, void 0, function* () { const parsedToken = token ? JSON.parse(token) : null; const authorization = yield this.clientIdAuthorization(requestContent); let data = `grant_type=client_credentials&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&${authorization}`; if (token && parsedToken && parsedToken.decodedRefreshToken && !(0, TokenUtils_1.isExpired)(parsedToken.decodedRefreshToken)) { data = `refresh_token=${JSON.parse(token).refresh_token}&grant_type=refresh_token&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&${authorization}`; } const umaConfig = yield this.options.umaConfiguration.getUma2Configuration(requestContent); const res = yield this.options.restClient.sendData(`${umaConfig.token_endpoint}`, 'POST', data); const newToken = JSON.parse(res); newToken.decodedAccessToken = jsonwebtoken_1.default.decode(newToken.access_token); newToken.decodedRefreshToken = jsonwebtoken_1.default.decode(newToken.refresh_token); return newToken; }); } clientAuthentication(requestContent) { return __awaiter(this, void 0, void 0, function* () { const keycloakJson = yield this.options.keycloakJson(this.options, requestContent); const key = `${keycloakJson.realm}:${keycloakJson.resource}`; const { cache } = this.options; let token = yield cache.get('client_credentials', key); if (!token || (0, TokenUtils_1.isExpired)(JSON.parse(token).decodedAccessToken)) { const newToken = yield this.clientAuthentication0(requestContent, token); token = JSON.stringify(newToken); yield cache.put('client_credentials', key, token); } return JSON.parse(token); }); } clientIdAuthorization(requestContent) { return __awaiter(this, void 0, void 0, function* () { const keycloakJson = yield this.options.keycloakJson(this.options, requestContent); let authorization = `client_id=${keycloakJson.resource}`; if (keycloakJson.credentials && keycloakJson.credentials.secret) { authorization += `&client_secret=${keycloakJson.credentials.secret}`; } else if (this.options.keys && this.options.keys.privateKey) { authorization += `&client_assertion=${yield this.clientJWT(yield this.createJWS(requestContent), this.options.keys.privateKey)}`; } else { throw new Error('Unsupported Credential Type'); } return authorization; }); } clientJWT(payload, privateKey) { return new Promise((resolve, reject) => { jws_1.default.createSign({ header: { alg: 'RS256', typ: 'RSA' }, privateKey, payload, }).on('done', (signature) => { resolve(signature); }).on('error', (e) => { this.options.logger.log(`error:${e}`); reject(e); }); }); } createJWS(requestContent) { return __awaiter(this, void 0, void 0, function* () { const timeLocal = new Date().getTime(); const timeSec = Math.floor(timeLocal / 1000); const keycloakJson = yield this.options.keycloakJson(this.options, requestContent); return { jti: (0, uuid_1.v4)(), sub: keycloakJson.resource, iss: keycloakJson.resource, aud: `${(0, KeycloakUtils_1.getKeycloakUrl)(keycloakJson)}/realms/${keycloakJson.realm}`, exp: timeSec + 30, iat: timeSec, }; }); } exchangeRPT(requestContent, accessToken, clientId) { return __awaiter(this, void 0, void 0, function* () { const umaConfig = yield this.options.umaConfiguration.getUma2Configuration(requestContent); const url = `${umaConfig.token_endpoint}`; const data = `grant_type=urn:ietf:params:oauth:grant-type:uma-ticket&response_include_resource_name=false&audience=${clientId}`; try { const response = yield this.options.restClient.sendData(url, 'POST', data, { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/x-www-form-urlencoded', }); return JSON.parse(response); } catch (e) { throw new Error(e); } }); } getRPT(requestContent, enforcerFunction) { return __awaiter(this, void 0, void 0, function* () { const keycloakJson = yield this.options.keycloakJson(this.options, requestContent); const enforceFunc = (0, Options_1.updateEnforce)(enforcerFunction); const enforcer = yield (enforceFunc(this.options, requestContent)); const clientId = enforcer.clientId || keycloakJson.resource; const key = `${keycloakJson.realm}:${clientId}:${requestContent.token.payload.jti}`; const { cache } = this.options; const tkn = yield cache.get('rpt', key); let tkn0; // eslint-disable-next-line no-negated-condition if (!tkn) { tkn0 = yield this.exchangeRPT(requestContent, requestContent.tokenString, clientId); tkn0.decodedAccessToken = jsonwebtoken_1.default.decode(tkn0.access_token); tkn0.decodedRefreshToken = jsonwebtoken_1.default.decode(tkn0.refresh_token); yield cache.put('rpt', key, JSON.stringify(tkn0), tkn0.refresh_expires_in); } else { const parseToken = JSON.parse(tkn); if ((0, TokenUtils_1.isExpired)(parseToken.decodedAccessToken)) { if (parseToken.refresh_token) { const refreshContext = yield this.keycloakRefreshToken((0, TokenUtils_1.transformRequestToRefresh)(parseToken, requestContent), enforceFunc); tkn0 = refreshContext ? refreshContext.token : null; } else { tkn0 = yield this.exchangeRPT(requestContent, requestContent.tokenString, clientId); } if (!tkn0) { throw new Error('Not able to refresh token'); } tkn0.decodedAccessToken = jsonwebtoken_1.default.decode(tkn0.access_token); tkn0.decodedRefreshToken = jsonwebtoken_1.default.decode(tkn0.refresh_token); yield cache.put('rpt', key, JSON.stringify(tkn0), tkn0.refresh_expires_in); } else { tkn0 = parseToken; } } return tkn0; }); } getTokenByCode(requestContent, code, redirectUri) { return __awaiter(this, void 0, void 0, function* () { const keycloakJson = yield this.options.keycloakJson(this.options, requestContent); const umaConfig = yield this.options.umaConfiguration.getUma2Configuration(requestContent); const url = `${umaConfig.token_endpoint}`; const authorization = yield this.clientIdAuthorization(requestContent); const data = `code=${code}&grant_type=authorization_code&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&${authorization}&redirect_uri=${encodeURIComponent(redirectUri)}`; const tokenResponse = yield this.options.restClient.sendData(url, 'POST', data, { 'Content-Type': 'application/x-www-form-urlencoded' }); const tokenJson = JSON.parse(tokenResponse); tokenJson.decodedAccessToken = jsonwebtoken_1.default.decode(tokenJson.access_token); tokenJson.decodedRefreshToken = jsonwebtoken_1.default.decode(tokenJson.refresh_token); return tokenJson; }); } keycloakRefreshToken(refreshContext, enforcerFunc) { return __awaiter(this, void 0, void 0, function* () { let tokenJson = refreshContext.token; if (!tokenJson) { return null; } const decodedAccessToken = jsonwebtoken_1.default.decode(tokenJson.access_token); const decodedRefreshToken = jsonwebtoken_1.default.decode(tokenJson.refresh_token); if ((0, TokenUtils_1.isExpired)(decodedRefreshToken)) { return null; } const requestContent = (0, TokenUtils_1.transformResfreshToRequest)(refreshContext); if (!decodedAccessToken || (0, TokenUtils_1.isExpired)(decodedAccessToken)) { const keycloakJson = yield this.options.keycloakJson(this.options, requestContent); const realmName = keycloakJson.realm; const umaConfig = yield this.options.umaConfiguration.getUma2Configuration(requestContent); const url = `${umaConfig.token_endpoint}`; this.options.logger.debug(`Token Request Url: ${url}`); const authorization = yield this.clientIdAuthorization(requestContent); const data = `refresh_token=${tokenJson.refresh_token}&grant_type=refresh_token&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&${authorization}`; try { const tokenResponse = yield this.options.restClient.sendData(url, 'POST', data, { 'Content-Type': 'application/x-www-form-urlencoded' }); tokenJson = JSON.parse(tokenResponse); tokenJson.decodedAccessToken = jsonwebtoken_1.default.decode(tokenJson.access_token); tokenJson.decodedRefreshToken = jsonwebtoken_1.default.decode(tokenJson.refresh_token); } catch (e) { this.options.logger.error(`wrong refresh token for ${realmName}`, e); return null; } } const enforcer = enforcerFunc ? yield enforcerFunc(this.options, requestContent) : null; if (enforcer) { yield this.options.enforcer.enforce(requestContent, (0, Options_1.updateEnforce)(enforcer)); } return Object.assign(Object.assign({}, refreshContext), { token: tokenJson }); }); } logout(requestContent, refreshToken) { return __awaiter(this, void 0, void 0, function* () { const authorization = yield this.clientIdAuthorization(requestContent); const data = `refresh_token=${refreshToken}&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&${authorization}`; const umaConfig = yield this.options.umaConfiguration.getUma2Configuration(requestContent); const url = `${umaConfig.end_session_endpoint}`; yield this.options.restClient.sendData(url, 'POST', data, { 'Content-Type': 'application/x-www-form-urlencoded' }); }); } } exports.DefaultClientAuthorization = DefaultClientAuthorization; //# sourceMappingURL=ClientAuthorization.js.map