keycloak-lambda-authorizer
Version:
224 lines • 12.8 kB
JavaScript
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
;