kopi-id
Version:
Simple OIDC Library
356 lines (277 loc) • 13.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _constants = _interopRequireDefault(require("../constants"));
var _auth = _interopRequireDefault(require("../services/auth"));
var _jwt = _interopRequireDefault(require("../services/jwt"));
var _token = _interopRequireDefault(require("../services/token"));
var _hash = _interopRequireDefault(require("../services/hash"));
var _logger = _interopRequireDefault(require("../services/logger"));
var _flowUtils = _interopRequireDefault(require("../services/flowUtils"));
var _redirect = _interopRequireDefault(require("../services/redirect"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
var _default = function _default(oidcConfig) {
var _loggerService = (0, _logger["default"])('Controller'),
L = _loggerService.L;
var authService = (0, _auth["default"])(oidcConfig);
var jwtService = (0, _jwt["default"])(oidcConfig);
var tokenService = (0, _token["default"])(oidcConfig, jwtService);
var handleAuthenticationRequest = /*#__PURE__*/function () {
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(authenticationRequest, req, res, next) {
var responseTypes, clientId, redirectUri, idTokenHint, prompt, state, maxAge, client, redirectUriValid, authenticationRequestId, idToken, sub, authTime, currentTime, loginPath, loginUrl;
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.prev = 0;
responseTypes = authenticationRequest.response_type, clientId = authenticationRequest.client_id, redirectUri = authenticationRequest.redirect_uri, idTokenHint = authenticationRequest.id_token_hint, prompt = authenticationRequest.prompt, state = authenticationRequest.state, maxAge = authenticationRequest.max_age; // Validate Client
_context.next = 4;
return oidcConfig.onGetClient(clientId);
case 4:
client = _context.sent;
if (!(client == null)) {
_context.next = 7;
break;
}
return _context.abrupt("return", _redirect["default"].redirectInvalidRequest(res, redirectUri, state));
case 7:
// Validate Redirect URI
redirectUriValid = client.redirectUri.includes(redirectUri);
if (redirectUriValid) {
_context.next = 10;
break;
}
return _context.abrupt("return", _redirect["default"].redirectInvalidRequestUri(res, redirectUri, state));
case 10:
// Create new Authentication Request
authenticationRequest.auth_time = Math.floor(new Date().getTime() / 1000);
_context.next = 13;
return oidcConfig.onSaveAuthenticationRequest(authenticationRequest);
case 13:
authenticationRequestId = _context.sent;
if (!(prompt && prompt.includes(_constants["default"].authenticationRequest.prompt.NONE))) {
_context.next = 36;
break;
}
if (!(prompt.length > 1)) {
_context.next = 17;
break;
}
return _context.abrupt("return", _redirect["default"].redirectInvalidRequest(res, redirectUri, state));
case 17:
if (!(idTokenHint == null)) {
_context.next = 21;
break;
}
return _context.abrupt("return", _redirect["default"].redirectInternalServiceError(res, redirectUri, state));
case 21:
_context.prev = 21;
_context.next = 24;
return jwtService.verifyIdToken(idTokenHint, client);
case 24:
idToken = _context.sent;
sub = idToken.sub, authTime = idToken.auth_time; // Check Max Age
currentTime = Math.floor(new Date().getTime() / 1000);
if (!(maxAge && currentTime > authTime + maxAge)) {
_context.next = 29;
break;
}
throw new Error('Maximum Authentication Age has reached.');
case 29:
return _context.abrupt("return", authService.handleAuthenticated(res, authenticationRequestId, sub, true, true));
case 32:
_context.prev = 32;
_context.t0 = _context["catch"](21);
L.warn("Error caught when verifying ID Token: ".concat(_context.t0.message)); // Send Redirect with Error (login_required)
return _context.abrupt("return", _redirect["default"].redirectLoginRequired(res, redirectUri, state));
case 36:
// Redirect to Login Page
loginPath = oidcConfig.loginUrl || "".concat(oidcConfig.host, "/").concat(oidcConfig.loginPage);
loginUrl = "".concat(loginPath, "?authenticationRequestId=").concat(authenticationRequestId);
res.redirect(loginUrl);
_context.next = 44;
break;
case 41:
_context.prev = 41;
_context.t1 = _context["catch"](0);
next(_context.t1);
case 44:
case "end":
return _context.stop();
}
}
}, _callee, null, [[0, 41], [21, 32]]);
}));
return function handleAuthenticationRequest(_x, _x2, _x3, _x4) {
return _ref.apply(this, arguments);
};
}();
var handleTokenRequest = /*#__PURE__*/function () {
var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(tokenRequest, req, res, next) {
var _req$body, code, redirectUri, clientId, client, authorizationRequestId, authorizationRequest, sub, authenticationRequestId, authenticationRequest, scope, requestClientId, requestRedirectUri, authTime, nonce, cHashBuffer, cHash, accessToken, atHashBuffer, atHash, idToken, accessTokenExpiresIn, response;
return regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
_context2.prev = 0;
_req$body = req.body, code = _req$body.code, redirectUri = _req$body.redirect_uri;
clientId = req.clientId;
_context2.next = 5;
return oidcConfig.onGetClient(clientId);
case 5:
client = _context2.sent;
_context2.next = 8;
return oidcConfig.onLoadAuthorization(code);
case 8:
authorizationRequestId = _context2.sent;
if (!(authorizationRequestId == null)) {
_context2.next = 11;
break;
}
throw new Error('Invalid Code');
case 11:
_context2.next = 13;
return oidcConfig.onLoadAuthorizationRequest(authorizationRequestId);
case 13:
authorizationRequest = _context2.sent;
sub = authorizationRequest.sub, authenticationRequestId = authorizationRequest.authenticationRequestId;
_context2.next = 17;
return oidcConfig.onLoadAuthenticationRequest(authenticationRequestId);
case 17:
authenticationRequest = _context2.sent;
scope = authenticationRequest.scope, requestClientId = authenticationRequest.client_id, requestRedirectUri = authenticationRequest.redirect_uri, authTime = authenticationRequest.auth_time, nonce = authenticationRequest.nonce;
if (!(clientId !== requestClientId)) {
_context2.next = 21;
break;
}
throw new Error('Client ID does not match requested authentication');
case 21:
if (!(redirectUri != null && redirectUri !== requestRedirectUri)) {
_context2.next = 23;
break;
}
throw new Error('Redirect URI does not match requested authentication');
case 23:
if (scope.includes('openid')) {
_context2.next = 25;
break;
}
throw new Error('Scope did not include openid');
case 25:
_context2.next = 27;
return oidcConfig.onRevokeAuthorization(code);
case 27:
// Generate Access Token (should have c_hash)
cHashBuffer = _hash["default"].hash(code, oidcConfig.hashAlgorithm);
cHash = cHashBuffer.slice(0, cHashBuffer.length / 2).toString('base64');
_context2.next = 31;
return tokenService.generateToken(client, sub, scope, authTime, nonce, cHash);
case 31:
accessToken = _context2.sent;
// Generate ID Token (should have at_hash)
atHashBuffer = _hash["default"].hash(accessToken, oidcConfig.hashAlgorithm);
atHash = atHashBuffer.slice(0, atHashBuffer.length / 2).toString('base64');
_context2.next = 36;
return tokenService.generateIdToken(client, sub, authTime, nonce, cHash, atHash);
case 36:
idToken = _context2.sent;
accessTokenExpiresIn = oidcConfig.accessTokenExpiresIn;
response = {
access_token: accessToken,
token_type: 'Bearer',
expires_in: accessTokenExpiresIn,
id_token: idToken
};
if (res.headers == null) {
res.headers = {};
}
res.headers['Cache-Control'] = 'no-store';
res.headers['Pragma'] = 'no-cache';
res.status(200).json(response);
_context2.next = 53;
break;
case 45:
_context2.prev = 45;
_context2.t0 = _context2["catch"](0);
L.error(_context2.t0.message);
L.debug(_context2.t0);
if (res.headers == null) {
res.headers = {};
}
res.headers['Cache-Control'] = 'no-store';
res.headers['Pragma'] = 'no-cache';
res.status(400).json({
error: 'invalid_request'
});
case 53:
case "end":
return _context2.stop();
}
}
}, _callee2, null, [[0, 45]]);
}));
return function handleTokenRequest(_x5, _x6, _x7, _x8) {
return _ref2.apply(this, arguments);
};
}();
var handleUserInfo = /*#__PURE__*/function () {
var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3(req, res, next) {
var tokenPayload, sub, scope, user;
return regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
_context3.prev = 0;
tokenPayload = req.tokenPayload;
sub = tokenPayload.sub, scope = tokenPayload.scope;
_context3.next = 5;
return oidcConfig.onGetUserInfo(sub, scope);
case 5:
user = _context3.sent;
user.sub = user.sub || user.id;
res.status(200).json(user);
_context3.next = 13;
break;
case 10:
_context3.prev = 10;
_context3.t0 = _context3["catch"](0);
next(_context3.t0);
case 13:
case "end":
return _context3.stop();
}
}
}, _callee3, null, [[0, 10]]);
}));
return function handleUserInfo(_x9, _x10, _x11) {
return _ref3.apply(this, arguments);
};
}();
var authenticationRequestGet = function authenticationRequestGet(req, res, next) {
return handleAuthenticationRequest(req.query, req, res, next);
};
var authenticationRequestPost = function authenticationRequestPost(req, res, next) {
return handleAuthenticationRequest(req.body, req, res, next);
};
var tokenRequestPost = function tokenRequestPost(req, res, next) {
return handleTokenRequest(req.body, req, res, next);
};
var userInfoGet = function userInfoGet(req, res, next) {
return handleUserInfo(req, res, next);
};
var userInfoPost = function userInfoPost(req, res, next) {
return handleUserInfo(req, res, next);
};
return {
authenticationRequestGet: authenticationRequestGet,
authenticationRequestPost: authenticationRequestPost,
tokenRequestPost: tokenRequestPost,
userInfoGet: userInfoGet,
userInfoPost: userInfoPost
};
};
exports["default"] = _default;