UNPKG

@davidzemon/passport-okta-oauth

Version:

Passport strategy for authenticating with Okta using OAuth 2.0.

178 lines (177 loc) 7.51 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; /** * Module dependencies. */ var uid = require("uid2"); var querystring = require("querystring"); var OAuth2Strategy = require("passport-oauth2"); var InternalOAuthError = require("passport-oauth2").InternalOAuthError; /** * @typedef {Object} UniqueOktaStrategyOptions * @property {string} audience audience is the Okta Domain, e.g. `https://example.okta.com`, * `https://example.oktapreview.com` * @property {string | undefined} authServerID authServerID is the authorization server ID. If it is defined, the token * URL might be something like `https://example.okta.com/oauth2/authServerID/v1/token` * @property {string | undefined} idp idp is the Identity Provider (id). This is an optional field. it's a 20 character * alphanumeric string, e.g. `qOp8aaJmCEhvep5Il6ZJ` (generated example) * @property {boolean | undefined} passReqToCallback With this option enabled, `req` will be passed as the first argument to the * verify callback. * @property {'code'} response_type Set this to 'code' */ /** * @typedef {UniqueOktaStrategyOptions & Omit<import("passport-oauth2")._StrategyOptionsBase, "authorizationURL" | "tokenURL">} OktaStrategyOptions */ /** * @extends OAuth2Strategy */ var Strategy = /** @class */ (function (_super) { __extends(Strategy, _super); /** * @param {OktaStrategyOptions | undefined} options * @param {import("passport-oauth2").VerifyFunction | import("passport-oauth2").VerifyFunctionWithRequest} verify */ function Strategy(options, verify) { var _this = this; var baseUrl = options.authServerID ? "".concat(options.audience, "/oauth2/").concat(options.authServerID, "/v1") : "".concat(options.audience, "/oauth2/v1"); var configuredOptions = __assign(__assign({}, options), { authorizationURL: "".concat(baseUrl, "/authorize"), tokenURL: "".concat(baseUrl, "/token"), userInfoUrl: "".concat(baseUrl, "/userinfo") }); _this = _super.call(this, configuredOptions, verify) || this; _this.name = "okta"; _this._userInfoUrl = configuredOptions.userInfoUrl; _this._idp = configuredOptions.idp; _this._state = configuredOptions.state; // Authorize Request using Authorization Header _this._oauth2.getOAuthAccessToken = function (code, params, callback) { params = params || {}; var codeParam = params.grant_type === "refresh_token" ? "refresh_token" : "code"; params[codeParam] = code; var post_data = querystring.stringify(params); var post_headers = { "Content-Type": "application/x-www-form-urlencoded", Authorization: "Basic: " + new Buffer(this._clientId + ":" + this._clientSecret).toString("base64") }; this._request("POST", this._getAccessTokenUrl(), post_headers, post_data, null, function (error, data) { if (error) callback(error); else { var results = void 0; try { // As of http://tools.ietf.org/html/draft-ietf-oauth-v2-07 // responses should be in JSON results = JSON.parse(data); } catch (e) { // .... However both Facebook + Github currently use rev05 of the spec // and neither seem to specify a content-type correctly in their response headers :( // clients of these services will suffer a *minor* performance cost of the exception // being thrown results = querystring.parse(data); } var access_token = results["access_token"]; var refresh_token = results["refresh_token"]; delete results["refresh_token"]; callback(null, access_token, refresh_token, results); // callback results =-= } }); }; return _this; } /** * Retrieve user profile from Okta. * Further references at http://developer.okta.com/docs/api/resources/oidc.html#get-user-information * * This function constructs a normalized profile, with the following properties: * * - `provider` always set to `okta` * - `id` * - `username` * - `displayName` * * @param {String} accessToken * @param {Function} done * @api protected */ Strategy.prototype.userProfile = function (accessToken, done) { var post_headers = { Authorization: "Bearer " + accessToken }; // noinspection JSAccessibilityCheck this._oauth2._request("POST", this._userInfoUrl, post_headers, "", null, function (err, body) { if (err) { return done(new InternalOAuthError("failed to fetch user profile", err)); } try { var json = JSON.parse(body); done(null, { provider: "tsokta", id: json.sub, displayName: json.name, username: json.preferred_username, name: { fullName: json.name, familyName: json.family_name, givenName: json.given_name }, emails: [{ value: json.email }], _raw: body, _json: json }); } catch (e) { done(e); } }); }; /** * Return extra Okta-specific parameters to be included in the authorization * request. * * @param {Object} option * @return {Object} * @api protected */ Strategy.prototype.authorizationParams = function (option) { var params = {}; if (this._state) { params.state = true; params.nonce = uid(24); } if (this._idp) { params.idp = this._idp; } if (option.scope) { params.scope = option.scope; } return params; }; return Strategy; }(OAuth2Strategy)); /** * Expose `Strategy`. */ module.exports = Strategy;