diff --git a/src/services/authentication-service-v2.js b/src/services/authentication-service-v2.js deleted file mode 100644 index 11e40694d38385f3a4a47f10a68226b51527fe20..0000000000000000000000000000000000000000 --- a/src/services/authentication-service-v2.js +++ /dev/null @@ -1,226 +0,0 @@ -import config from '../config.json'; - -/* global Keycloak */ - -let keycloakClient = undefined; - -const INIT_CHECK_INTERVAL_MS = 100; -const INIT_CHECK_MAX_RETRIES = 10; - -let _instance = null; -const SINGLETON_ENFORCER = Symbol(); - -/** - * Service taking care of OIDC/FS authentication for NRP accounts - */ -class AuthenticationService { - constructor(enforcer) { - if (enforcer !== SINGLETON_ENFORCER) { - throw new Error('Use ' + this.constructor.name + '.instance'); - } - - this.proxyURL = config.api.proxy.url; - this.oidcEnabled = config.auth.enableOIDC; - this.clientId = config.auth.clientId; - this.authURL = config.auth.url; - this.STORAGE_KEY = `tokens-${this.clientId}@https://iam.ebrains.eu/auth/realms/hbp`; - - this.init(); - } - - static get instance() { - if (_instance == null) { - _instance = new AuthenticationService(SINGLETON_ENFORCER); - } - - return _instance; - } - - init() { - this.initialized = false; - if (this.oidcEnabled) { - this.authCollab().then(() => { - this.initialized = true; - }); - } - else { - this.checkForNewLocalTokenToStore(); - this.initialized = true; - } - - this.promiseInitialized = new Promise((resolve, reject) => { - let numChecks = 0; - let checkInterval = setInterval(() => { - numChecks++; - if (numChecks > INIT_CHECK_MAX_RETRIES) { - clearInterval(checkInterval); - reject(); - } - if (this.initialized) { - clearInterval(checkInterval); - resolve(); - } - }, INIT_CHECK_INTERVAL_MS); - }); - } - - authenticate(config) { - if (this.oidcEnabled) { - this.authCollab(config); - } - else { - this.authLocal(config); - } - } - - getToken() { - if (this.oidcEnabled) { - if (keycloakClient && keycloakClient.authenticated) { - keycloakClient - .updateToken(30) - .then(function() {}) - .catch(function() { - console.error('Failed to refresh token'); - }); - return keycloakClient.token; - } - else { - console.error('getToken() - Client is not authenticated'); - } - } - else { - return this.getStoredLocalToken(); - } - } - - logout() { - if (this.oidcEnabled) { - if (keycloakClient && keycloakClient.authenticated) { - keycloakClient.logout(); - keycloakClient.clearStoredLocalToken(); - } - else { - console.error('Client is not authenticated'); - } - } - else { - return this.clearStoredLocalToken(); - } - } - - authLocal(config) { - if (this.authenticating) { - return; - } - this.authenticating = true; - - this.authURL = this.authURL || config.url; - this.clientId = this.clientId || config.clientId; - - let absoluteUrl = /^https?:\/\//i; - if (!absoluteUrl.test(this.authURL)) { - this.authURL = `${this.proxyURL}${this.authURL}`; - } - - this.clearStoredLocalToken(); - window.location.href = `${this.authURL}&client_id=${this - .clientId}&redirect_uri=${encodeURIComponent(window.location.href)}`; - } - - checkForNewLocalTokenToStore() { - const path = window.location.pathname; - - const accessTokenMatch = /&access_token=([^&]*)/.exec(path); - if (!accessTokenMatch || !accessTokenMatch[1]) { - return; - } - - let accessToken = accessTokenMatch[1]; - localStorage.setItem( - this.STORAGE_KEY, - //eslint-disable-next-line camelcase - JSON.stringify([{ access_token: accessToken }]) - ); - - // navigate to clean url - let cleanedPath = path.substr(0, path.indexOf('&')); - window.location = cleanedPath; - } - - clearStoredLocalToken() { - localStorage.removeItem(this.STORAGE_KEY); - } - - getStoredLocalToken() { - let storedItem = localStorage.getItem(this.STORAGE_KEY); - if (!storedItem) { - // this token will be rejected by the server and the client will get a proper auth error - return 'no-token'; - } - - try { - let tokens = JSON.parse(storedItem); - return tokens.length ? tokens[tokens.length - 1].access_token : null; - } - catch (e) { - // this token will be rejected by the server and the client will get a proper auth error - return 'malformed-token'; - } - } - - authCollab(config) { - if (this.authenticating) { - return; - } - this.authenticating = true; - - return new Promise(resolve => { - this.authURL = this.authURL || config.url; - - this.initKeycloakClient().then(() => { - if (!keycloakClient.authenticated) { - // User is not authenticated, run login - keycloakClient - .login({ scope: 'openid profile email group' }) - .then(() => { - resolve(true); - }); - } - else { - keycloakClient.loadUserInfo().then(userInfo => { - this.userInfo = userInfo; - resolve(true); - }); - } - }); - }); - } - - initKeycloakClient() { - return new Promise(resolve => { - keycloakClient = Keycloak({ - realm: 'hbp', - clientId: this.clientId, - //'public-client': true, - 'confidential-port': 0, - url: this.authURL, - redirectUri: window.location.href // 'http://localhost:9001/#/esv-private' // - }); - - keycloakClient - .init({ - flow: 'hybrid' /*, responseMode: 'fragment'*/ - }) - .then(() => { - resolve(keycloakClient); - }); - }); - } -} - -AuthenticationService.CONSTANTS = Object.freeze({ - MALFORMED_TOKEN: 'malformed-token', - NO_TOKEN: 'no-token' -}); - -export default AuthenticationService; diff --git a/src/services/authentication-service.js b/src/services/authentication-service.js index 53eaf6864fa63b584c1159094f85265b92fca023..11e40694d38385f3a4a47f10a68226b51527fe20 100644 --- a/src/services/authentication-service.js +++ b/src/services/authentication-service.js @@ -1,5 +1,12 @@ import config from '../config.json'; +/* global Keycloak */ + +let keycloakClient = undefined; + +const INIT_CHECK_INTERVAL_MS = 100; +const INIT_CHECK_MAX_RETRIES = 10; + let _instance = null; const SINGLETON_ENFORCER = Symbol(); @@ -12,11 +19,13 @@ class AuthenticationService { throw new Error('Use ' + this.constructor.name + '.instance'); } - this.CLIENT_ID = config.auth.clientId; - this.STORAGE_KEY = `tokens-${this.CLIENT_ID}@https://services.humanbrainproject.eu/oidc`; - this.PROXY_URL = config.api.proxy.url; + this.proxyURL = config.api.proxy.url; + this.oidcEnabled = config.auth.enableOIDC; + this.clientId = config.auth.clientId; + this.authURL = config.auth.url; + this.STORAGE_KEY = `tokens-${this.clientId}@https://iam.ebrains.eu/auth/realms/hbp`; - this.checkForNewTokenToStore(); + this.init(); } static get instance() { @@ -27,44 +36,122 @@ class AuthenticationService { return _instance; } - /** - * Checks if the current page URL contains access tokens. - * This happens when the successfully logging in at the proxy login page and - * being redirected back with the token info. - * Will automatically remove additional access info and present a clean URL after being redirected. - */ - checkForNewTokenToStore() { - const path = window.location.href; - const accessTokenMatch = /&access_token=([^&]*)/.exec(path); + init() { + this.initialized = false; + if (this.oidcEnabled) { + this.authCollab().then(() => { + this.initialized = true; + }); + } + else { + this.checkForNewLocalTokenToStore(); + this.initialized = true; + } + + this.promiseInitialized = new Promise((resolve, reject) => { + let numChecks = 0; + let checkInterval = setInterval(() => { + numChecks++; + if (numChecks > INIT_CHECK_MAX_RETRIES) { + clearInterval(checkInterval); + reject(); + } + if (this.initialized) { + clearInterval(checkInterval); + resolve(); + } + }, INIT_CHECK_INTERVAL_MS); + }); + } + + authenticate(config) { + if (this.oidcEnabled) { + this.authCollab(config); + } + else { + this.authLocal(config); + } + } + + getToken() { + if (this.oidcEnabled) { + if (keycloakClient && keycloakClient.authenticated) { + keycloakClient + .updateToken(30) + .then(function() {}) + .catch(function() { + console.error('Failed to refresh token'); + }); + return keycloakClient.token; + } + else { + console.error('getToken() - Client is not authenticated'); + } + } + else { + return this.getStoredLocalToken(); + } + } + + logout() { + if (this.oidcEnabled) { + if (keycloakClient && keycloakClient.authenticated) { + keycloakClient.logout(); + keycloakClient.clearStoredLocalToken(); + } + else { + console.error('Client is not authenticated'); + } + } + else { + return this.clearStoredLocalToken(); + } + } + + authLocal(config) { + if (this.authenticating) { + return; + } + this.authenticating = true; + + this.authURL = this.authURL || config.url; + this.clientId = this.clientId || config.clientId; + + let absoluteUrl = /^https?:\/\//i; + if (!absoluteUrl.test(this.authURL)) { + this.authURL = `${this.proxyURL}${this.authURL}`; + } + + this.clearStoredLocalToken(); + window.location.href = `${this.authURL}&client_id=${this + .clientId}&redirect_uri=${encodeURIComponent(window.location.href)}`; + } + checkForNewLocalTokenToStore() { + const path = window.location.pathname; + + const accessTokenMatch = /&access_token=([^&]*)/.exec(path); if (!accessTokenMatch || !accessTokenMatch[1]) { return; } let accessToken = accessTokenMatch[1]; - localStorage.setItem( this.STORAGE_KEY, //eslint-disable-next-line camelcase JSON.stringify([{ access_token: accessToken }]) ); - const pathMinusAccessToken = path.substr(0, path.indexOf('&access_token=')); - window.location.href = pathMinusAccessToken; + + // navigate to clean url + let cleanedPath = path.substr(0, path.indexOf('&')); + window.location = cleanedPath; } - /** - * Clear currently stored access token. - */ - clearStoredToken() { + clearStoredLocalToken() { localStorage.removeItem(this.STORAGE_KEY); } - /** - * Get the stored access token. - * - * @return token The stored access token. Or strings identifying 'no-token' / 'malformed-token'. - */ - getStoredToken() { + getStoredLocalToken() { let storedItem = localStorage.getItem(this.STORAGE_KEY); if (!storedItem) { // this token will be rejected by the server and the client will get a proper auth error @@ -77,31 +164,63 @@ class AuthenticationService { } catch (e) { // this token will be rejected by the server and the client will get a proper auth error - return AuthenticationService.CONSTANTS.MALFORMED_TOKEN; + return 'malformed-token'; } } - /** - * Opens the proxy's authentication page. - * - * @param {*} url The URL of the authentication page. - * If not an absolute URL it is assumed to be a subpage of the proxy. - */ - openAuthenticationPage(url) { - this.clearStoredToken(); - - let absoluteUrl = /^https?:\/\//i; - if (!absoluteUrl.test(url)) { - url = `${this.PROXY_URL}${url}`; + authCollab(config) { + if (this.authenticating) { + return; } - window.location.href = `${url}&client_id=${ - this.CLIENT_ID - }&redirect_uri=${encodeURIComponent(window.location.href)}`; + this.authenticating = true; + + return new Promise(resolve => { + this.authURL = this.authURL || config.url; + + this.initKeycloakClient().then(() => { + if (!keycloakClient.authenticated) { + // User is not authenticated, run login + keycloakClient + .login({ scope: 'openid profile email group' }) + .then(() => { + resolve(true); + }); + } + else { + keycloakClient.loadUserInfo().then(userInfo => { + this.userInfo = userInfo; + resolve(true); + }); + } + }); + }); + } + + initKeycloakClient() { + return new Promise(resolve => { + keycloakClient = Keycloak({ + realm: 'hbp', + clientId: this.clientId, + //'public-client': true, + 'confidential-port': 0, + url: this.authURL, + redirectUri: window.location.href // 'http://localhost:9001/#/esv-private' // + }); + + keycloakClient + .init({ + flow: 'hybrid' /*, responseMode: 'fragment'*/ + }) + .then(() => { + resolve(keycloakClient); + }); + }); } } AuthenticationService.CONSTANTS = Object.freeze({ - MALFORMED_TOKEN: 'malformed-token' + MALFORMED_TOKEN: 'malformed-token', + NO_TOKEN: 'no-token' }); export default AuthenticationService; diff --git a/src/services/http-service.js b/src/services/http-service.js index 87abf46ca57cf2916767b1c93ec6aadf5b6eb1a2..56ae7299cdd552ebf80e5fb2dbbf002cdf7efdf2 100644 --- a/src/services/http-service.js +++ b/src/services/http-service.js @@ -1,7 +1,7 @@ import { EventEmitter } from 'events'; -import AuthenticationService from './authentication-service-v2.js'; +import AuthenticationService from './authentication-service'; /** * Base class that performs http requests with default request options. diff --git a/src/services/roslib-service.js b/src/services/roslib-service.js index aa3244737607488847634d04d10232c088a3a1c7..fb47207692ae0a73d3fb7cd02a291a6e988ebdac 100644 --- a/src/services/roslib-service.js +++ b/src/services/roslib-service.js @@ -1,7 +1,7 @@ import * as ROSLIB from 'roslib'; import _ from 'lodash'; -import AuthenticationService from './authentication-service-v2'; +import AuthenticationService from './authentication-service'; let _instance = null; const SINGLETON_ENFORCER = Symbol();