UNPKG

@mvx/identity

Version:

identity is oidc for mvc, type-mvc is base on koa. Decorator, Ioc, AOP mvc framework on server.

290 lines (288 loc) 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OAuth2Strategy = void 0; const tslib_1 = require("tslib"); const components_1 = require("@tsdi/components"); const errors_1 = require("../errors"); const oauth2_1 = require("./oauth2"); const Strategy_1 = require("./Strategy"); const stores_1 = require("../stores"); const url_1 = require("url"); const 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); * }); * } * )); * */ let OAuth2Strategy = class OAuth2Strategy extends Strategy_1.Strategy { async onAfterInit() { 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 = (accessToken) => new Object(); } if (!this.tokenParams) { this.tokenParams = (options) => new Object(); } if (!this.authorizationParams) { this.authorizationParams = (options) => new Object(); } } async authenticate(ctx, options = {}) { if (ctx.query && ctx.query.error) { if (ctx.query.error === 'access_denied') { 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); } let callbackURL = options.callbackURL || this.callbackURL; if (callbackURL) { const 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); } } const meta = { authorizationURL: this.authorizationURL, tokenURL: this.tokenURL, clientId: this.clientId, }; if (ctx.query && ctx.query.code) { const state = ctx.query.state; const { result: verifiedResult, message: verifiedMsg } = await this.stateStore.verify(ctx, state); if (!verifiedResult) { return new results_1.FailResult(verifiedMsg, 403); } const code = ctx.query.code; const params = this.tokenParams(options); params.grant_type = 'authorization_code'; if (callbackURL) { params.redirect_uri = callbackURL; } let accessToken; let refreshToken; let accessTokenResult; try { ({ accessToken, refreshToken, result: accessTokenResult, } = await this.oauth2.getOAuthAccessToken(code, params)); } catch (err) { throw this.parseOAuthError(err); } const profile = await this.loadUserProfile(accessToken); const { user, info } = await this.verify(accessToken, refreshToken, accessTokenResult, profile); if (!user) { // TODO, not sure 401 is the correct meaning return new results_1.FailResult(info, 401); } return new results_1.SuccessResult(options, user, info); } else { const params = this.authorizationParams(options); params.response_type = 'code'; if (callbackURL) { params.redirect_uri = callbackURL; } let scope = options.scope || this.scope; if (scope) { if (Array.isArray(scope)) { scope = scope.join(this.scopeSeparator); } params.scope = scope; } let state = options.state; if (state) { params.state = state; } else { state = await this.stateStore.store(ctx, meta); if (state) { params.state = state; } } const 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; const location = url_1.format(parsed); return new results_1.RedirectResult(location); } } /** * 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. * */ parseOAuthError(err) { let e; if (err instanceof oauth2_1.OAuth2Error) { try { const 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. * */ async loadUserProfile(accessToken) { if (this.skipUserProfile) { return Promise.resolve(null); } return await this.userProfile(accessToken); } static ρAnn() { 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); exports.OAuth2Strategy = OAuth2Strategy; //# sourceMappingURL=../sourcemaps/passports/OAuth2Strategy.js.map