@davidzemon/passport-okta-oauth
Version:
Passport strategy for authenticating with Okta using OAuth 2.0.
178 lines (177 loc) • 7.51 kB
JavaScript
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;