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..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,8 +18,8 @@
Workspace token loader
-
-
+
+
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
new file mode 100644
index 00000000000..615ed6698cc
--- /dev/null
+++ b/assembly/assembly-root-war/src/main/webapp/_app/oauth.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+ 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
new file mode 100644
index 00000000000..85af5c72f8e
--- /dev/null
+++ b/assembly/assembly-root-war/src/main/webapp/_app/oauthLoader.js
@@ -0,0 +1,180 @@
+/*
+ * 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
+ */
+
+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
+ */
+function getQueryParams(name) {
+ const queryString = window.location.search;
+ return new URLSearchParams(queryString).getAll(name);
+}
+
+/**
+ * Fetches userId
+ */
+function asyncGetUserId() {
+ 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);
+ };
+ });
+ });
+}
+
+/**
+ * Sets authorization header for a request
+ * @param {XMLHttpRequest} xhr
+ */
+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);
+ });
+}
+
+function postMessage(message) {
+ if (window.opener) {
+ window.opener.postMessage(message, '*');
+ }
+}
+
+/**
+ * Fetches workspace details by ID
+ * @param {string} workspaceId a workspace id
+ */
+function getWorkspace(workspaceId) {
+ return new Promise((resolve, reject) => {
+ const request = new XMLHttpRequest();
+ request.open("GET", '/api/workspace/' + workspaceId);
+ 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(undefined);
+ };
+ });
+ });
+}
+
+
+function parseToken (token) {
+ return JSON.parse(atob(token.split('.')[1]));
+}
+
+(async () => {
+ let token;
+ try {
+ const keycloak = await new KeycloakLoader().loadKeycloakSettings();
+ token = keycloak ? keycloak.token : undefined;
+ } catch (e) {
+ console.log(e.message);
+ }
+ try {
+ if (token) {
+ await new Promise((resolve, reject) => {
+ window.addEventListener('message', async data => {
+ if (data.data.startsWith('token:')) {
+ const machineToken = parseToken(data.data.substring(6, data.data.length));
+ const userToken = parseToken(token);
+ if (machineToken.uid === userToken.sub) {
+ try {
+ await getWorkspace(machineToken.wsid);
+ } catch (e) {
+ reject(e);
+ }
+ } else {
+ reject(new Error('Machine and user token mismatch'));
+ }
+ resolve(undefined);
+ }
+ });
+ postMessage('status:ready-to-receive-messages');
+ });
+ }
+ const status = getQueryParams('status')[0]; {
+ if (status && status === 'ready') {
+ postMessage('token:' + (token ? token : ''));
+ return;
+ }
+ }
+ const oauthProvider = getQueryParams('oauth_provider')[0];
+ if (!oauthProvider) {
+ postMessage('token:' + (token ? token : ''));
+ return;
+ }
+ 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 asyncGetUserId()}`;
+ const scope = getQueryParams('scope'); {
+ for (const s of scope) {
+ url += `&scope=${s}`;
+ }
+ }
+ url += `&redirect_after_login=${redirectUrl}`;
+ if (token) {
+ url += `&token=${token}`;
+ }
+ window.location.replace(url);
+ } catch (errorMessage) {
+ error(errorMessage);
+ console.error(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()