From 2f3478d043dc2a4c6e240be8da7ac85acaacfedd Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 21 Jan 2021 14:26:02 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20initial=20flow=20to=20op?= =?UTF-8?q?en=20new=20window=20to=203rd=20party=20auth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/route-handlers/auth.js | 45 ++++++++++--------- addons/auth/addon/authenticators/oidc.js | 42 +++++++++++++++-- addons/core/addon/authenticators/oidc.js | 31 ++++++++++++- .../scopes/scope/authenticate/method.js | 32 ++++++++++--- 4 files changed, 117 insertions(+), 33 deletions(-) diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index 113ca5b40..8f5d48b3f 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -25,19 +25,24 @@ import { Response } from 'miragejs'; const methodHandlers = { password: { - authenticate: (scopeAttrs) => - new Response(200, {}, { - scope: scopeAttrs, - id: 'token123', - token: 'thetokenstring', - account_id: '1', - user_id: 'user123', - auth_method_id: 'authmethod123', - created_time: '', - updated_time: '', - last_used_time: '', - expiration_time: '' - }) + authenticate: (payload, scopeAttrs) => { + if (payload.credentials.login_name === 'error') { + return new Response(400); + } else { + return new Response(200, {}, { + scope: scopeAttrs, + id: 'token123', + token: 'thetokenstring', + account_id: '1', + user_id: 'user123', + auth_method_id: 'authmethod123', + created_time: '', + updated_time: '', + last_used_time: '', + expiration_time: '' + }); + } + } }, /** * OIDC authentication is a two-step process: @@ -63,15 +68,11 @@ const methodHandlers = { export function authHandler({ scopes, authMethods }, request) { const payload = JSON.parse(request.requestBody); - if (payload.credentials.login_name === 'error') { - return new Response(400); - } else { - const [, id, method] = request.params.id_method.match(/(?.[^:]*)(?::)?(?.*)?/); - const authMethod = authMethods.find(id); - const scope = scopes.find(authMethod.scopeId); - const scopeAttrs = this.serialize(scopes.find(scope.id)); - return methodHandlers[authMethod.type][method](scopeAttrs); - } + const [, id, method] = request.params.id_method.match(/(?.[^:]*)(?::)?(?.*)?/); + const authMethod = authMethods.find(id); + const scope = scopes.find(authMethod.scopeId); + const scopeAttrs = this.serialize(scopes.find(scope.id)); + return methodHandlers[authMethod.type][method](payload, scopeAttrs); } export function deauthHandler() { diff --git a/addons/auth/addon/authenticators/oidc.js b/addons/auth/addon/authenticators/oidc.js index 4fd24845c..2f95f0517 100644 --- a/addons/auth/addon/authenticators/oidc.js +++ b/addons/auth/addon/authenticators/oidc.js @@ -1,7 +1,6 @@ import BaseAuthenticator from './base'; -// import { resolve, reject } from 'rsvp'; -// import fetch from 'fetch'; -//import { Promise } from 'rsvp'; +import { resolve, reject } from 'rsvp'; +import fetch from 'fetch'; /** * @@ -16,6 +15,43 @@ export default class OIDCAuthenticator extends BaseAuthenticator { authMethod: { id: authMethodID }, } */) { } + buildStartAuthEndpointURL(/* { + scope: { id: scopeID }, + authMethod: { id: authMethodID }, + } */) { } + + /** + * + */ + async startAuthentication( + creds, + requestCookies = true, + options + ) { + const body = JSON.stringify({ + token_type: requestCookies ? 'cookie' : null + }); + const authEndpointURL = this.buildStartAuthEndpointURL(options); + const response = await fetch(authEndpointURL, { method: 'post', body }); + const json = await response.json(); + const normalized = this.normalizeData(json); + if (response.status < 400) { + await this.openExternalOIDCFlow(normalized.authorization_request_url); + resolve(normalized); + } else { + reject(); + } + } + + /** + * Opens the specified URL in a new tab or window. By default this uses + * `window.open`, but may be overriden. + * @param {string} url + */ + openExternalOIDCFlow(url) { + window.open(url); + } + async authenticate( // creds, // requestCookies = true, diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js index 77dd7e29e..a805b79d6 100644 --- a/addons/core/addon/authenticators/oidc.js +++ b/addons/core/addon/authenticators/oidc.js @@ -1,8 +1,37 @@ import BaseOIDCAuthenticator from 'auth/authenticators/oidc'; -//import { inject as service } from '@ember/service'; +import { inject as service } from '@ember/service'; /** * */ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { + + // =services + + @service store; + + // =attributes + + /** + * Generates an auth method URL with which to authenticate. + * @override + * @param {object} options + * @param {string} options.scope.scope_id + * @param {string} options.authMethod.id + * @return {string} + */ + buildStartAuthEndpointURL({ + scope: { id: scopeID }, + authMethod: { id: authMethodID }, + }) { + const adapter = this.store.adapterFor('application'); + const options = { + adapterOptions: { + scopeID, + method: 'authenticate:start' + } + }; + return adapter.buildURL('auth-method', authMethodID, options, 'findRecord'); + } + } diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index cfb2c5acf..499c9a0a0 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -1,5 +1,6 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; +import { getOwner } from '@ember/application'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; import { notifyError } from 'core/decorators/notify'; @@ -37,12 +38,29 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { const authMethod = this.modelFor('scopes.scope.authenticate.method'); const authenticatorName = `authenticator:${authMethod.type}`; const requestCookies = false; - await this.session.authenticate( - authenticatorName, - creds, - requestCookies, - { scope, authMethod } - ); - this.transitionTo('index'); + switch (authMethod.type) { + case 'password': + await this.session.authenticate( + authenticatorName, + creds, + requestCookies, + { scope, authMethod } + ); + this.transitionTo('index'); + break; + case 'oidc': + await this.startOIDCAuthentication( + authenticatorName, + creds, + requestCookies, + { scope, authMethod } + ); + break; + } + } + + async startOIDCAuthentication(authenticatorName, creds, requestCookies, options) { + const oidc = getOwner(this).lookup(authenticatorName); + oidc.startAuthentication(creds, requestCookies, options); } }