From 84246f7424873059a38b17629e2cf7cf4fd3c51c Mon Sep 17 00:00:00 2001 From: Igor Vinokur Date: Tue, 17 Mar 2020 14:08:19 +0200 Subject: [PATCH 1/4] Make it possible to call oAuth service API with machine token --- .../machine/authentication/server/MachineAuthModule.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java index 1d61f70cdf7..cead897e7f2 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java @@ -79,12 +79,17 @@ protected void configure() { .toInstance( new MachineAuthenticatedResource( "/preferences", "find", "save", "update", "removePreferences")); + machineAuthenticatedResources + .addBinding() + .toInstance(new MachineAuthenticatedResource("/user", "getCurrent")); machineAuthenticatedResources .addBinding() .toInstance(new MachineAuthenticatedResource("/activity", "active")); machineAuthenticatedResources .addBinding() - .toInstance(new MachineAuthenticatedResource("oauth", "token")); + .toInstance( + new MachineAuthenticatedResource( + "oauth", "token", "authenticate", "callback", "getRegisteredAuthenticators")); machineAuthenticatedResources .addBinding() From 595b37baad9c73a88d019f64aec4df27751c232c Mon Sep 17 00:00:00 2001 From: Igor Vinokur Date: Tue, 24 Mar 2020 18:48:23 +0200 Subject: [PATCH 2/4] commit --- .../src/main/webapp/_app/oauth.html | 27 +++ .../src/main/webapp/_app/oauthLoader.js | 198 ++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 assembly/assembly-root-war/src/main/webapp/_app/oauth.html create mode 100644 assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js diff --git a/assembly/assembly-root-war/src/main/webapp/_app/oauth.html b/assembly/assembly-root-war/src/main/webapp/_app/oauth.html new file mode 100644 index 00000000000..fe288c3c01b --- /dev/null +++ b/assembly/assembly-root-war/src/main/webapp/_app/oauth.html @@ -0,0 +1,27 @@ + + + + + + + oAuth token loader + + + + + + + + diff --git a/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js b/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js new file mode 100644 index 00000000000..8e9d6b1d85f --- /dev/null +++ b/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +class KeycloakLoader { + /** + * Load keycloak settings + */ + loadKeycloakSettings() { + const msg = "Cannot load keycloak settings. This is normal for single-user mode."; + + return new Promise((resolve, reject) => { + try { + if (window.parent && window.parent['_keycloak']) { + window['_keycloak'] = window.parent['_keycloak']; + resolve(window['_keycloak']); + return; + } + } catch (e) { + // parent frame has different origin, so access to parent frame is forbidden + console.error(msg, e); + } + + try { + const request = new XMLHttpRequest(); + + request.onerror = request.onabort = function () { + reject(new Error(msg)); + }; + + request.onload = () => { + if (request.status === 200) { + resolve(this.injectKeycloakScript(JSON.parse(request.responseText))); + } else { + reject(new Error(msg)); + } + }; + + const url = "/api/keycloak/settings"; + request.open("GET", url, true); + request.send(); + } catch (e) { + reject(new Error(msg + e.message)); + } + }); + } + + /** + * Injects keycloak javascript + */ + injectKeycloakScript(keycloakSettings) { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.language = 'javascript'; + script.async = true; + script.src = keycloakSettings['che.keycloak.js_adapter_url']; + + script.onload = () => { + resolve(this.initKeycloak(keycloakSettings)); + }; + + script.onerror = script.onabort = () => { + reject(new Error('cannot load ' + script.src)); + }; + + document.head.appendChild(script); + }); + } + + /** + * Initialize keycloak + */ + initKeycloak(keycloakSettings) { + return new Promise((resolve, reject) => { + + function keycloakConfig() { + const theOidcProvider = keycloakSettings['che.keycloak.oidc_provider']; + if (!theOidcProvider) { + return { + url: keycloakSettings['che.keycloak.auth_server_url'], + realm: keycloakSettings['che.keycloak.realm'], + clientId: keycloakSettings['che.keycloak.client_id'] + }; + } else { + return { + oidcProvider: theOidcProvider, + clientId: keycloakSettings['che.keycloak.client_id'] + }; + } + } + + const keycloak = Keycloak(keycloakConfig()); + + window['_keycloak'] = keycloak; + + var useNonce; + if (typeof keycloakSettings['che.keycloak.use_nonce'] === 'string') { + useNonce = keycloakSettings['che.keycloak.use_nonce'].toLowerCase() === 'true'; + } + window.sessionStorage.setItem('oidcIdeRedirectUrl', location.href); + keycloak + .init({ + onLoad: 'login-required', + checkLoginIframe: false, + useNonce: useNonce, + scope: 'email profile', + redirectUri: keycloakSettings['che.keycloak.redirect_url.ide'] + }) + .success(() => { + resolve(keycloak); + }) + .error(() => { + reject(new Error('[Keycloak] Failed to initialize Keycloak')); + }); + }); + } + +} + +/** + * Returns query parameter value if it is present + * @param {string} name a query parameter name + */ +function getQueryParam(name) { + const params = window.location.search.substr(1), + paramEntries = params.split('&'); + const entry = paramEntries.find(_entry => { + return _entry.startsWith(name + '='); + }); + if (!entry) { + return; + } + const [_, value] = entry.split('='); + return decodeURIComponent(value); +} + +(async function () { + + // const machineToken = this.getQueryParam('token'); + // const parcedToken = window.atob(machineToken); + // const tokenObject = JSON.parse(parcedToken); + + const apiUrl = 'http://che-che.192.168.99.254.nip.io/api'; + const oauthProvider = 'github'; + const userId = this.getQueryParam('userId'); + + try { + await new KeycloakLoader().loadKeycloakSettings(); + const token = window._keycloak.token; + + const redirectUrl = window.location.href; + let url = `${apiUrl}/oauth/authenticate?oauth_provider=${oauthProvider}&userId=${userId}`; + // if (scope) { + // for (const s of scope) { + // url += `&scope=${s}`; + // } + // } + if (token) { + url += `&token=${token}`; + } + url += `&redirect_after_login=${redirectUrl}`; + const popupWindow = window.open(url, 'popup'); + const popup_close_handler = async () => { + if (!popupWindow || popupWindow.closed) { + if (popupCloseHandlerIntervalId) { + window.clearInterval(popupCloseHandlerIntervalId); + } + window.close(); + } else { + try { + if (redirectUrl === popupWindow.location.href) { + if (popupCloseHandlerIntervalId) { + window.clearInterval(popupCloseHandlerIntervalId); + } + popupWindow.close(); + window.close(); + } + } catch (error) { + } + } + }; + + const popupCloseHandlerIntervalId = window.setInterval(popup_close_handler, 80); + } catch (errorMessage) { + console.error(errorMessage); + loader.hideLoader(); + loader.error(errorMessage); + } +}) +(); From 1d738356d231d0d05d4169c810921fa96eb31e56 Mon Sep 17 00:00:00 2001 From: Igor Vinokur Date: Thu, 26 Mar 2020 15:52:34 +0200 Subject: [PATCH 3/4] commit --- .../src/main/webapp/_app/keycloackLoader.js | 125 ++++++++ .../src/main/webapp/_app/loader.html | 13 +- .../src/main/webapp/_app/loader.js | 114 +------- .../src/main/webapp/_app/oauth.html | 8 +- .../src/main/webapp/_app/oauthLoader.js | 275 +++++++----------- .../server/MachineAuthModule.java | 7 +- 6 files changed, 247 insertions(+), 295 deletions(-) create mode 100644 assembly/assembly-root-war/src/main/webapp/_app/keycloackLoader.js diff --git a/assembly/assembly-root-war/src/main/webapp/_app/keycloackLoader.js b/assembly/assembly-root-war/src/main/webapp/_app/keycloackLoader.js new file mode 100644 index 00000000000..ce0e07cc8e4 --- /dev/null +++ b/assembly/assembly-root-war/src/main/webapp/_app/keycloackLoader.js @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2012-2018 Red Hat, Inc. + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + */ +export class KeycloakLoader { + /** + * Load keycloak settings + */ + loadKeycloakSettings() { + const msg = "Cannot load keycloak settings. This is normal for single-user mode."; + + return new Promise((resolve, reject) => { + try { + if (window.parent && window.parent['_keycloak']) { + window['_keycloak'] = window.parent['_keycloak']; + resolve(window['_keycloak']); + return; + } + } catch (e) { + // parent frame has different origin, so access to parent frame is forbidden + console.error(msg, e); + } + + try { + const request = new XMLHttpRequest(); + + request.onerror = request.onabort = function() { + reject(new Error(msg)); + }; + + request.onload = () => { + if (request.status == 200) { + resolve(this.injectKeycloakScript(JSON.parse(request.responseText))); + } else { + reject(new Error(msg)); + } + }; + + const url = "/api/keycloak/settings"; + request.open("GET", url, true); + request.send(); + } catch (e) { + reject(new Error(msg + e.message)); + } + }); + } + + /** + * Injects keycloak javascript + */ + injectKeycloakScript(keycloakSettings) { + return new Promise((resolve, reject) => { + const script = document.createElement('script'); + script.type = 'text/javascript'; + script.language = 'javascript'; + script.async = true; + script.src = keycloakSettings['che.keycloak.js_adapter_url']; + + script.onload = () => { + resolve(this.initKeycloak(keycloakSettings)); + }; + + script.onerror = script.onabort = () => { + reject(new Error('cannot load ' + script.src)); + }; + + document.head.appendChild(script); + }); + } + + /** + * Initialize keycloak + */ + initKeycloak(keycloakSettings) { + return new Promise((resolve, reject) => { + + function keycloakConfig() { + const theOidcProvider = keycloakSettings['che.keycloak.oidc_provider']; + if (!theOidcProvider) { + return { + url: keycloakSettings['che.keycloak.auth_server_url'], + realm: keycloakSettings['che.keycloak.realm'], + clientId: keycloakSettings['che.keycloak.client_id'] + }; + } else { + return { + oidcProvider: theOidcProvider, + clientId: keycloakSettings['che.keycloak.client_id'] + }; + } + } + const keycloak = Keycloak(keycloakConfig()); + + window['_keycloak'] = keycloak; + + var useNonce; + if (typeof keycloakSettings['che.keycloak.use_nonce'] === 'string') { + useNonce = keycloakSettings['che.keycloak.use_nonce'].toLowerCase() === 'true'; + } + window.sessionStorage.setItem('oidcIdeRedirectUrl', location.href); + keycloak + .init({ + onLoad: 'login-required', + checkLoginIframe: false, + useNonce: useNonce, + scope: 'email profile', + redirectUri: keycloakSettings['che.keycloak.redirect_url.ide'] + }) + .success(() => { + resolve(keycloak); + }) + .error(() => { + reject(new Error('[Keycloak] Failed to initialize Keycloak')); + }); + }); + } + +} diff --git a/assembly/assembly-root-war/src/main/webapp/_app/loader.html b/assembly/assembly-root-war/src/main/webapp/_app/loader.html index f3291aeaf67..54076edc3d2 100644 --- a/assembly/assembly-root-war/src/main/webapp/_app/loader.html +++ b/assembly/assembly-root-war/src/main/webapp/_app/loader.html @@ -19,24 +19,13 @@ Workspace token loader - + - -
-
Loading a runtime token...
-
-
-
-
-
-
Press F5 or click here to try again.
-
- diff --git a/assembly/assembly-root-war/src/main/webapp/_app/loader.js b/assembly/assembly-root-war/src/main/webapp/_app/loader.js index d1045a7c727..ca773caa4df 100644 --- a/assembly/assembly-root-war/src/main/webapp/_app/loader.js +++ b/assembly/assembly-root-war/src/main/webapp/_app/loader.js @@ -9,120 +9,8 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -class KeycloakLoader { - /** - * Load keycloak settings - */ - loadKeycloakSettings() { - const msg = "Cannot load keycloak settings. This is normal for single-user mode."; - - return new Promise((resolve, reject) => { - try { - if (window.parent && window.parent['_keycloak']) { - window['_keycloak'] = window.parent['_keycloak']; - resolve(window['_keycloak']); - return; - } - } catch (e) { - // parent frame has different origin, so access to parent frame is forbidden - console.error(msg, e); - } - - try { - const request = new XMLHttpRequest(); - - request.onerror = request.onabort = function() { - reject(new Error(msg)); - }; - - request.onload = () => { - if (request.status == 200) { - resolve(this.injectKeycloakScript(JSON.parse(request.responseText))); - } else { - reject(new Error(msg)); - } - }; - - const url = "/api/keycloak/settings"; - request.open("GET", url, true); - request.send(); - } catch (e) { - reject(new Error(msg + e.message)); - } - }); - } - - /** - * Injects keycloak javascript - */ - injectKeycloakScript(keycloakSettings) { - return new Promise((resolve, reject) => { - const script = document.createElement('script'); - script.type = 'text/javascript'; - script.language = 'javascript'; - script.async = true; - script.src = keycloakSettings['che.keycloak.js_adapter_url']; - script.onload = () => { - resolve(this.initKeycloak(keycloakSettings)); - }; - - script.onerror = script.onabort = () => { - reject(new Error('cannot load ' + script.src)); - }; - - document.head.appendChild(script); - }); - } - - /** - * Initialize keycloak - */ - initKeycloak(keycloakSettings) { - return new Promise((resolve, reject) => { - - function keycloakConfig() { - const theOidcProvider = keycloakSettings['che.keycloak.oidc_provider']; - if (!theOidcProvider) { - return { - url: keycloakSettings['che.keycloak.auth_server_url'], - realm: keycloakSettings['che.keycloak.realm'], - clientId: keycloakSettings['che.keycloak.client_id'] - }; - } else { - return { - oidcProvider: theOidcProvider, - clientId: keycloakSettings['che.keycloak.client_id'] - }; - } - } - const keycloak = Keycloak(keycloakConfig()); - - window['_keycloak'] = keycloak; - - var useNonce; - if (typeof keycloakSettings['che.keycloak.use_nonce'] === 'string') { - useNonce = keycloakSettings['che.keycloak.use_nonce'].toLowerCase() === 'true'; - } - window.sessionStorage.setItem('oidcIdeRedirectUrl', location.href); - keycloak - .init({ - onLoad: 'login-required', - checkLoginIframe: false, - useNonce: useNonce, - scope: 'email profile', - redirectUri: keycloakSettings['che.keycloak.redirect_url.ide'] - }) - .success(() => { - resolve(keycloak); - }) - .error(() => { - reject(new Error('[Keycloak] Failed to initialize Keycloak')); - }); - }); - } - -} +import { KeycloakLoader } from './keycloackLoader.js'; class Loader { diff --git a/assembly/assembly-root-war/src/main/webapp/_app/oauth.html b/assembly/assembly-root-war/src/main/webapp/_app/oauth.html index fe288c3c01b..9eccd100752 100644 --- a/assembly/assembly-root-war/src/main/webapp/_app/oauth.html +++ b/assembly/assembly-root-war/src/main/webapp/_app/oauth.html @@ -16,12 +16,16 @@ - oAuth token loader - + Authentication + +
+
+
+ diff --git a/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js b/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js index 8e9d6b1d85f..8269036346d 100644 --- a/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js +++ b/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js @@ -9,190 +9,141 @@ * Contributors: * Red Hat, Inc. - initial API and implementation */ -class KeycloakLoader { - /** - * Load keycloak settings - */ - loadKeycloakSettings() { - const msg = "Cannot load keycloak settings. This is normal for single-user mode."; - return new Promise((resolve, reject) => { - try { - if (window.parent && window.parent['_keycloak']) { - window['_keycloak'] = window.parent['_keycloak']; - resolve(window['_keycloak']); - return; - } - } catch (e) { - // parent frame has different origin, so access to parent frame is forbidden - console.error(msg, e); - } - - try { - const request = new XMLHttpRequest(); +import { KeycloakLoader } from './keycloackLoader.js'; - request.onerror = request.onabort = function () { - reject(new Error(msg)); - }; - - request.onload = () => { - if (request.status === 200) { - resolve(this.injectKeycloakScript(JSON.parse(request.responseText))); - } else { - reject(new Error(msg)); - } - }; - - const url = "/api/keycloak/settings"; - request.open("GET", url, true); - request.send(); - } catch (e) { - reject(new Error(msg + e.message)); - } - }); - } - - /** - * Injects keycloak javascript - */ - injectKeycloakScript(keycloakSettings) { - return new Promise((resolve, reject) => { - const script = document.createElement('script'); - script.type = 'text/javascript'; - script.language = 'javascript'; - script.async = true; - script.src = keycloakSettings['che.keycloak.js_adapter_url']; - - script.onload = () => { - resolve(this.initKeycloak(keycloakSettings)); - }; +/** + * Returns an array of query parameter values if it is present + * @param {string} name of the query parameter + */ +function getQueryParams(name) { + const queryString = window.location.search; + return new URLSearchParams(queryString).getAll(name); +} - script.onerror = script.onabort = () => { - reject(new Error('cannot load ' + script.src)); +/** + * Fetches userId + */ +function asyncGeUserId() { + return new Promise((resolve, reject) => { + const request = new XMLHttpRequest(); + request.open("GET", '/api/user'); + setAuthorizationHeader(request).then((xhr) => { + xhr.send(); + xhr.onreadystatechange = () => { + if (xhr.readyState !== 4) { + return; + } + if (xhr.status !== 200) { + const errorMessage = 'Failed to get the workspace: "' + this.getRequestErrorMessage(xhr) + '"'; + reject(new Error(errorMessage)); + return; + } + resolve(JSON.parse(xhr.responseText).id); }; - - document.head.appendChild(script); }); - } - - /** - * Initialize keycloak - */ - initKeycloak(keycloakSettings) { - return new Promise((resolve, reject) => { + }); +} - function keycloakConfig() { - const theOidcProvider = keycloakSettings['che.keycloak.oidc_provider']; - if (!theOidcProvider) { - return { - url: keycloakSettings['che.keycloak.auth_server_url'], - realm: keycloakSettings['che.keycloak.realm'], - clientId: keycloakSettings['che.keycloak.client_id'] - }; - } else { - return { - oidcProvider: theOidcProvider, - clientId: keycloakSettings['che.keycloak.client_id'] - }; +/** + * Fetches userId + */ +function asyncGeToken(provider) { + return new Promise((resolve, reject) => { + const request = new XMLHttpRequest(); + request.open("GET", '/api/oauth/token?oauth_provider=' + provider); + setAuthorizationHeader(request).then((xhr) => { + xhr.send(); + xhr.onreadystatechange = () => { + if (xhr.readyState !== 4) { + return; } - } - - const keycloak = Keycloak(keycloakConfig()); - - window['_keycloak'] = keycloak; - - var useNonce; - if (typeof keycloakSettings['che.keycloak.use_nonce'] === 'string') { - useNonce = keycloakSettings['che.keycloak.use_nonce'].toLowerCase() === 'true'; - } - window.sessionStorage.setItem('oidcIdeRedirectUrl', location.href); - keycloak - .init({ - onLoad: 'login-required', - checkLoginIframe: false, - useNonce: useNonce, - scope: 'email profile', - redirectUri: keycloakSettings['che.keycloak.redirect_url.ide'] - }) - .success(() => { - resolve(keycloak); - }) - .error(() => { - reject(new Error('[Keycloak] Failed to initialize Keycloak')); - }); + if (xhr.status !== 200) { + const errorMessage = 'Failed to get the workspace: "' + this.getRequestErrorMessage(xhr) + '"'; + reject(new Error(errorMessage)); + return; + } + resolve(JSON.parse(xhr.responseText).id); + }; }); - } - + }); } /** - * Returns query parameter value if it is present - * @param {string} name a query parameter name + * Sets authorization header for a request + * @param {XMLHttpRequest} xhr */ -function getQueryParam(name) { - const params = window.location.search.substr(1), - paramEntries = params.split('&'); - const entry = paramEntries.find(_entry => { - return _entry.startsWith(name + '='); +function setAuthorizationHeader(xhr) { + return new Promise((resolve, reject) => { + if (window._keycloak && window._keycloak.token) { + window._keycloak.updateToken(5).success(() => { + xhr.setRequestHeader('Authorization', 'Bearer ' + window._keycloak.token); + resolve(xhr); + }).error(() => { + window.sessionStorage.setItem('oidcIdeRedirectUrl', location.href); + window._keycloak.login(); + reject(new Error('Failed to refresh token')); + }); + } + + resolve(xhr); }); - if (!entry) { - return; - } - const [_, value] = entry.split('='); - return decodeURIComponent(value); } -(async function () { - - // const machineToken = this.getQueryParam('token'); - // const parcedToken = window.atob(machineToken); - // const tokenObject = JSON.parse(parcedToken); +/** + * Displays error message + * @param {string} message an error message to show + */ +function error(message) { - const apiUrl = 'http://che-che.192.168.99.254.nip.io/api'; - const oauthProvider = 'github'; - const userId = this.getQueryParam('userId'); + const element = document.createElement("pre"); + element.innerHTML = message; + window.appendChild(element); + if (element.scrollIntoView) { + element.scrollIntoView(); + } + element.style.color = "error"; + return element; +} +(async () => { try { - await new KeycloakLoader().loadKeycloakSettings(); - const token = window._keycloak.token; - - const redirectUrl = window.location.href; - let url = `${apiUrl}/oauth/authenticate?oauth_provider=${oauthProvider}&userId=${userId}`; - // if (scope) { - // for (const s of scope) { - // url += `&scope=${s}`; - // } - // } - if (token) { - url += `&token=${token}`; + const status = getQueryParams('status')[0]; { + if (status && status === 'ready') { + window.opener.postMessage('authentication:ready','*'); + return; + } } - url += `&redirect_after_login=${redirectUrl}`; - const popupWindow = window.open(url, 'popup'); - const popup_close_handler = async () => { - if (!popupWindow || popupWindow.closed) { - if (popupCloseHandlerIntervalId) { - window.clearInterval(popupCloseHandlerIntervalId); - } - window.close(); - } else { - try { - if (redirectUrl === popupWindow.location.href) { - if (popupCloseHandlerIntervalId) { - window.clearInterval(popupCloseHandlerIntervalId); - } - popupWindow.close(); - window.close(); - } - } catch (error) { - } + const oauthProvider = getQueryParams('providerName')[0]; + if (!oauthProvider) { + throw new Error('Provider name isn\'t found in query parameters.') + } + const method = getQueryParams('method')[0]; { + if (method && method === 'getToken') { + const token = await asyncGeToken(oauthProvider); + window.opener.postMessage('token:' + token,'*'); + return; } - }; - - const popupCloseHandlerIntervalId = window.setInterval(popup_close_handler, 80); + } + const currentUrl = window.location.href; + const cheUrl = currentUrl.substring(0, currentUrl.indexOf('/_app')); + const apiUrl = cheUrl + '/api'; + const redirectUrl = currentUrl.substring(0, currentUrl.indexOf('?')) + '?status=ready'; + let url = `${apiUrl}/oauth/authenticate?oauth_provider=${oauthProvider}&userId=${await asyncGeUserId()}`; + const scope = this.getQueryParams('scope'); { + for (const s of scope) { + url += `&scope=${s}`; + } + } + url += `&redirect_after_login=${redirectUrl}`; + const keycloak = await new KeycloakLoader().loadKeycloakSettings(); + if (keycloak && keycloak.token) { + url += `&token=${keycloak.token}`; + } + window.location.replace(url); } catch (errorMessage) { + error(errorMessage); console.error(errorMessage); - loader.hideLoader(); - loader.error(errorMessage); } }) (); diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java index cead897e7f2..1d61f70cdf7 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java @@ -79,17 +79,12 @@ protected void configure() { .toInstance( new MachineAuthenticatedResource( "/preferences", "find", "save", "update", "removePreferences")); - machineAuthenticatedResources - .addBinding() - .toInstance(new MachineAuthenticatedResource("/user", "getCurrent")); machineAuthenticatedResources .addBinding() .toInstance(new MachineAuthenticatedResource("/activity", "active")); machineAuthenticatedResources .addBinding() - .toInstance( - new MachineAuthenticatedResource( - "oauth", "token", "authenticate", "callback", "getRegisteredAuthenticators")); + .toInstance(new MachineAuthenticatedResource("oauth", "token")); machineAuthenticatedResources .addBinding() From 20564318a06dbdb94433bda439dbd0360dcd7779 Mon Sep 17 00:00:00 2001 From: Igor Vinokur Date: Mon, 30 Mar 2020 14:24:23 +0300 Subject: [PATCH 4/4] commit --- .../src/main/webapp/_app/loader.html | 13 ++++- .../src/main/webapp/_app/oauth.html | 4 +- .../src/main/webapp/_app/oauthLoader.js | 51 ++++++++++--------- .../WEB-INF/classes/che/multiuser.properties | 2 +- .../server/MachineAuthModule.java | 3 -- 5 files changed, 40 insertions(+), 33 deletions(-) diff --git a/assembly/assembly-root-war/src/main/webapp/_app/loader.html b/assembly/assembly-root-war/src/main/webapp/_app/loader.html index 54076edc3d2..53d78183f79 100644 --- a/assembly/assembly-root-war/src/main/webapp/_app/loader.html +++ b/assembly/assembly-root-war/src/main/webapp/_app/loader.html @@ -18,14 +18,25 @@ Workspace token loader - + + +
+
Loading a runtime token...
+
+
+
+
+
+
Press F5 or click here to try again.
+
+ diff --git a/assembly/assembly-root-war/src/main/webapp/_app/oauth.html b/assembly/assembly-root-war/src/main/webapp/_app/oauth.html index 9eccd100752..615ed6698cc 100644 --- a/assembly/assembly-root-war/src/main/webapp/_app/oauth.html +++ b/assembly/assembly-root-war/src/main/webapp/_app/oauth.html @@ -22,9 +22,7 @@ -
-
-
+
diff --git a/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js b/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js index 8269036346d..54915d782ef 100644 --- a/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js +++ b/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js @@ -12,6 +12,20 @@ import { KeycloakLoader } from './keycloackLoader.js'; +/** + * Displays error message + * @param {string} message an error message to show + */ +function error(message) { + const container = document.getElementById("error-console"); + + const element = document.createElement("pre"); + element.innerHTML = message; + element.style.color = 'red'; + container.appendChild(element); + return element; +} + /** * Returns an array of query parameter values if it is present * @param {string} name of the query parameter @@ -90,38 +104,26 @@ function setAuthorizationHeader(xhr) { }); } -/** - * Displays error message - * @param {string} message an error message to show - */ -function error(message) { - - const element = document.createElement("pre"); - element.innerHTML = message; - window.appendChild(element); - if (element.scrollIntoView) { - element.scrollIntoView(); +function sendToken(token) { + if (window.opener) { + window.opener.postMessage('token:' + (token ? token : ''),'*'); } - element.style.color = "error"; - return element; } (async () => { try { + const keycloak = await new KeycloakLoader().loadKeycloakSettings(); + const oauthProvider = getQueryParams('providerName')[0]; + const token = keycloak ? keycloak.token : undefined; const status = getQueryParams('status')[0]; { if (status && status === 'ready') { - window.opener.postMessage('authentication:ready','*'); + sendToken(token); return; } } - const oauthProvider = getQueryParams('providerName')[0]; if (!oauthProvider) { - throw new Error('Provider name isn\'t found in query parameters.') - } - const method = getQueryParams('method')[0]; { - if (method && method === 'getToken') { - const token = await asyncGeToken(oauthProvider); - window.opener.postMessage('token:' + token,'*'); + if (window.opener) { + sendToken(token); return; } } @@ -130,15 +132,14 @@ function error(message) { const apiUrl = cheUrl + '/api'; const redirectUrl = currentUrl.substring(0, currentUrl.indexOf('?')) + '?status=ready'; let url = `${apiUrl}/oauth/authenticate?oauth_provider=${oauthProvider}&userId=${await asyncGeUserId()}`; - const scope = this.getQueryParams('scope'); { + const scope = getQueryParams('scope'); { for (const s of scope) { url += `&scope=${s}`; } } url += `&redirect_after_login=${redirectUrl}`; - const keycloak = await new KeycloakLoader().loadKeycloakSettings(); - if (keycloak && keycloak.token) { - url += `&token=${keycloak.token}`; + if (token) { + url += `&token=${token}`; } window.location.replace(url); } catch (errorMessage) { diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties index cc5675e916e..a976e6e0802 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/multiuser.properties @@ -161,4 +161,4 @@ che.keycloak.username_claim=NULL # If set to "embedded", then the service work as a wrapper to Che's OAuthAuthenticator ( as in Single User mode). # If set to "delegated", then the service will use Keycloak IdentityProvider mechanism. # Runtime Exception wii be thrown, in case if this property is not set properly. -che.oauth.service_mode=embedded +che.oauth.service_mode=delegated diff --git a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java index 1d61f70cdf7..4a3826dbe28 100644 --- a/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java +++ b/multiuser/machine-auth/che-multiuser-machine-authentication/src/main/java/org/eclipse/che/multiuser/machine/authentication/server/MachineAuthModule.java @@ -82,9 +82,6 @@ protected void configure() { machineAuthenticatedResources .addBinding() .toInstance(new MachineAuthenticatedResource("/activity", "active")); - machineAuthenticatedResources - .addBinding() - .toInstance(new MachineAuthenticatedResource("oauth", "token")); machineAuthenticatedResources .addBinding()