@mvx/identity
Version:
identity is oidc for mvc, type-mvc is base on koa. Decorator, Ioc, AOP mvc framework on server.
324 lines (322 loc) • 14.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OAuth2Strategy = void 0;
var tslib_1 = require("tslib");
var components_1 = require("@tsdi/components");
var errors_1 = require("../errors");
var oauth2_1 = require("./oauth2");
var Strategy_1 = require("./Strategy");
var stores_1 = require("../stores");
var url_1 = require("url");
var results_1 = require("./results");
/**
* Creates an instance of `OAuth2Strategy`.
*
* The OAuth 2.0 authentication strategy authenticates requests using the OAuth
* 2.0 framework.
*
* OAuth 2.0 provides a facility for delegated authentication, whereby users can
* authenticate using a third-party service such as Facebook. Delegating in
* this manner involves a sequence of events, including redirecting the user to
* the third-party service for authorization. Once authorization has been
* granted, the user is redirected back to the application and an authorization
* code can be used to obtain credentials.
*
* Applications must supply a `verify` callback, for which the function
* signature is:
*
* function(accessToken, refreshToken, profile, done) { ... }
*
* The verify callback is responsible for finding or creating the user, and
* invoking `done` with the following arguments:
*
* done(err, user, info);
*
* `user` should be set to `false` to indicate an authentication failure.
* Additional `info` can optionally be passed as a third argument, typically
* used to display informational messages. If an exception occured, `err`
* should be set.
*
* Params:
*
* - `authorizationURL` URL used to obtain an authorization grant
* - `tokenURL` URL used to obtain an access token
* - `clientId` identifies client to service provider
* - `clientSecret` secret used to establish ownership of the client identifer
* - `callbackURL` URL to which the service provider will redirect the user after obtaining authorization
* - `passReqToCallback` when `true`, `req` is the first argument to the verify callback (default: `false`)
*
* Examples:
*
* passport.use(new OAuth2Strategy({
* authorizationURL: 'https://www.example.com/oauth2/authorize',
* tokenURL: 'https://www.example.com/oauth2/token',
* clientId: '123-456-789',
* clientSecret: 'shhh-its-a-secret'
* callbackURL: 'https://www.example.net/auth/example/callback'
* },
* function(accessToken, refreshToken, profile, done) {
* User.findOrCreate(..., function (err, user) {
* done(err, user);
* });
* }
* ));
*
*/
var OAuth2Strategy = /** @class */ (function (_super) {
tslib_1.__extends(OAuth2Strategy, _super);
function OAuth2Strategy() {
return _super !== null && _super.apply(this, arguments) || this;
}
OAuth2Strategy.prototype.onAfterInit = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
if (!this.name) {
this.name = 'oauth2';
}
// NOTE: The _oauth2 property is considered "protected". Subclasses are
// allowed to use it when making protected resource requests to retrieve
// the user profile.
this.oauth2 = new oauth2_1.OAuth2(this.clientId, this.clientSecret, '', this.authorizationURL, this.tokenURL, this.customHeaders);
if (!this.sessionKey) {
this.sessionKey = ('oauth2:' + url_1.parse(this.authorizationURL).hostname);
}
if (!this.stateStore) { // if stateStore is `true`, use default session stateStore
this.stateStore = new stores_1.SessionStore(this.sessionKey);
}
if (!this.userProfile) {
this.userProfile = function (accessToken) { return new Object(); };
}
if (!this.tokenParams) {
this.tokenParams = function (options) { return new Object(); };
}
if (!this.authorizationParams) {
this.authorizationParams = function (options) { return new Object(); };
}
return [2 /*return*/];
});
});
};
OAuth2Strategy.prototype.authenticate = function (ctx, options) {
if (options === void 0) { options = {}; }
return tslib_1.__awaiter(this, void 0, void 0, function () {
var callbackURL, parsed, meta, state, _a, verifiedResult, verifiedMsg, code, params, accessToken, refreshToken, accessTokenResult, err_1, profile, _b, user, info, params, scope, state, parsed, location_1;
var _c;
return tslib_1.__generator(this, function (_d) {
switch (_d.label) {
case 0:
if (ctx.query && ctx.query.error) {
if (ctx.query.error === 'access_denied') {
return [2 /*return*/, new results_1.FailResult(ctx.query.error_description, 401)];
}
throw new errors_1.AuthenticationError(ctx.status || 403, ctx.query.error_uri, ctx.query.error_description || ctx.query.error);
}
callbackURL = options.callbackURL || this.callbackURL;
if (callbackURL) {
parsed = url_1.parse(callbackURL);
if (!parsed.protocol) {
// The callback URL is relative, resolve a fully qualified URL from the
// URL of the originating request.
callbackURL = url_1.resolve(ctx.request.origin, callbackURL);
}
}
meta = {
authorizationURL: this.authorizationURL,
tokenURL: this.tokenURL,
clientId: this.clientId,
};
if (!(ctx.query && ctx.query.code)) return [3 /*break*/, 8];
state = ctx.query.state;
return [4 /*yield*/, this.stateStore.verify(ctx, state)];
case 1:
_a = _d.sent(), verifiedResult = _a.result, verifiedMsg = _a.message;
if (!verifiedResult) {
return [2 /*return*/, new results_1.FailResult(verifiedMsg, 403)];
}
code = ctx.query.code;
params = this.tokenParams(options);
params.grant_type = 'authorization_code';
if (callbackURL) {
params.redirect_uri = callbackURL;
}
accessToken = void 0;
refreshToken = void 0;
accessTokenResult = void 0;
_d.label = 2;
case 2:
_d.trys.push([2, 4, , 5]);
return [4 /*yield*/, this.oauth2.getOAuthAccessToken(code, params)];
case 3:
(_c = _d.sent(), accessToken = _c.accessToken, refreshToken = _c.refreshToken, accessTokenResult = _c.result);
return [3 /*break*/, 5];
case 4:
err_1 = _d.sent();
throw this.parseOAuthError(err_1);
case 5: return [4 /*yield*/, this.loadUserProfile(accessToken)];
case 6:
profile = _d.sent();
return [4 /*yield*/, this.verify(accessToken, refreshToken, accessTokenResult, profile)];
case 7:
_b = _d.sent(), user = _b.user, info = _b.info;
if (!user) {
// TODO, not sure 401 is the correct meaning
return [2 /*return*/, new results_1.FailResult(info, 401)];
}
return [2 /*return*/, new results_1.SuccessResult(options, user, info)];
case 8:
params = this.authorizationParams(options);
params.response_type = 'code';
if (callbackURL) {
params.redirect_uri = callbackURL;
}
scope = options.scope || this.scope;
if (scope) {
if (Array.isArray(scope)) {
scope = scope.join(this.scopeSeparator);
}
params.scope = scope;
}
state = options.state;
if (!state) return [3 /*break*/, 9];
params.state = state;
return [3 /*break*/, 11];
case 9: return [4 /*yield*/, this.stateStore.store(ctx, meta)];
case 10:
state = _d.sent();
if (state) {
params.state = state;
}
_d.label = 11;
case 11:
parsed = url_1.parse(this.oauth2.AuthorizeUrl, true);
parsed.query = Object.assign({}, parsed.query, params);
parsed.query.client_id = this.oauth2.ClientId;
delete parsed.search;
location_1 = url_1.format(parsed);
return [2 /*return*/, new results_1.RedirectResult(location_1)];
}
});
});
};
/**
* Parse error response from OAuth 2.0 endpoint.
*
* OAuth 2.0-based authentication strategies can overrride this function in
* order to parse error responses received from the token endpoint, allowing the
* most informative message to be displayed.
*
* If this function is not overridden, the body will be parsed in accordance
* with RFC 6749, section 5.2.
*
*/
OAuth2Strategy.prototype.parseOAuthError = function (err) {
var e;
if (err instanceof oauth2_1.OAuth2Error) {
try {
var json = JSON.parse(err.message);
if (json.error) {
e = new errors_1.InternalOAuthError("Failed to obtain access token:" + json.error_description, json.error);
}
}
catch (_) {
// console.warn('============This error can be ignored==============');
// console.warn(_);
// console.warn('===================================================');
}
}
if (!e) {
err.message = "Failed to obtain access token:" + err.message;
e = err;
}
return e;
};
/**
* Load user profile, contingent upon options.
*
*/
OAuth2Strategy.prototype.loadUserProfile = function (accessToken) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (this.skipUserProfile) {
return [2 /*return*/, Promise.resolve(null)];
}
return [4 /*yield*/, this.userProfile(accessToken)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
OAuth2Strategy.ρAnn = function () {
return { "name": "OAuth2Strategy", "params": { "authenticate": ["ctx", "options"], "parseOAuthError": ["err"], "loadUserProfile": ["accessToken"] } };
};
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", stores_1.StateStore)
], OAuth2Strategy.prototype, "stateStore", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", String)
], OAuth2Strategy.prototype, "clientId", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", String)
], OAuth2Strategy.prototype, "authorizationURL", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", String)
], OAuth2Strategy.prototype, "tokenURL", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", Function)
], OAuth2Strategy.prototype, "verify", void 0);
tslib_1.__decorate([
components_1.Input({ defaultValue: false }),
tslib_1.__metadata("design:type", Boolean)
], OAuth2Strategy.prototype, "skipUserProfile", void 0);
tslib_1.__decorate([
components_1.Input({ defaultValue: ' ' }),
tslib_1.__metadata("design:type", String)
], OAuth2Strategy.prototype, "scopeSeparator", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", String)
], OAuth2Strategy.prototype, "callbackURL", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", Object)
], OAuth2Strategy.prototype, "scope", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", String)
], OAuth2Strategy.prototype, "sessionKey", void 0);
tslib_1.__decorate([
components_1.Input({ defaultValue: '' }),
tslib_1.__metadata("design:type", String)
], OAuth2Strategy.prototype, "clientSecret", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", Object)
], OAuth2Strategy.prototype, "customHeaders", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", Function)
], OAuth2Strategy.prototype, "userProfile", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", Function)
], OAuth2Strategy.prototype, "tokenParams", void 0);
tslib_1.__decorate([
components_1.Input(),
tslib_1.__metadata("design:type", Function)
], OAuth2Strategy.prototype, "authorizationParams", void 0);
OAuth2Strategy = tslib_1.__decorate([
components_1.Component({
selector: 'oauth2'
})
], OAuth2Strategy);
return OAuth2Strategy;
}(Strategy_1.Strategy));
exports.OAuth2Strategy = OAuth2Strategy;
//# sourceMappingURL=../sourcemaps/passports/OAuth2Strategy.js.map