diff --git a/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml b/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml index 93987adffe..3e05860b3f 100755 --- a/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml +++ b/docbook/reference/en/en-US/modules/MigrationFromOlderVersions.xml @@ -85,6 +85,7 @@ The tomcat adapter valve has moved to a different package. From org.keycloak.adapters.tomcat7.KeycloakAuthenticatorValve to org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve From the 'tomcat7' package to just 'tomcat'. + JavaScript adapter now has idToken and idTokenParsed properties. If you use idToken to retrieve first name, email, etc. you need to change this to idTokenParsed.
diff --git a/docbook/reference/en/en-US/modules/javascript-adapter.xml b/docbook/reference/en/en-US/modules/javascript-adapter.xml index 03a303d041..dd6c3ee56e 100755 --- a/docbook/reference/en/en-US/modules/javascript-adapter.xml +++ b/docbook/reference/en/en-US/modules/javascript-adapter.xml @@ -157,6 +157,7 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp' tokenParsed - the parsed token subject - the user id idToken - the id token if claims is enabled for the application, null otherwise + idTokenParsed - the parsed id token realmAccess - the realm roles associated with the token resourceAccess - the resource roles assocaited with the token refreshToken - the base64 encoded token that can be used to retrieve a new token diff --git a/examples/js-console/src/main/webapp/index.html b/examples/js-console/src/main/webapp/index.html index b0cbf3987d..afb2811632 100644 --- a/examples/js-console/src/main/webapp/index.html +++ b/examples/js-console/src/main/webapp/index.html @@ -12,6 +12,7 @@ + diff --git a/integration/js/src/main/resources/keycloak.js b/integration/js/src/main/resources/keycloak.js index d412f198c1..d57c48de58 100755 --- a/integration/js/src/main/resources/keycloak.js +++ b/integration/js/src/main/resources/keycloak.js @@ -70,7 +70,7 @@ } if (initOptions.token || initOptions.refreshToken) { - setToken(initOptions.token, initOptions.refreshToken); + setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken); initPromise.setSuccess(); } else if (initOptions.onLoad) { var options = {}; @@ -255,7 +255,7 @@ if (req.readyState == 4) { if (req.status == 200) { var tokenResponse = JSON.parse(req.responseText); - setToken(tokenResponse['access_token'], tokenResponse['refresh_token']); + setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']); kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess(); for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) { p.setSuccess(true); @@ -325,7 +325,7 @@ if (req.readyState == 4) { if (req.status == 200) { var tokenResponse = JSON.parse(req.responseText); - setToken(tokenResponse['access_token'], tokenResponse['refresh_token']); + setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']); kc.onAuthSuccess && kc.onAuthSuccess(); promise && promise.setSuccess(); } else { @@ -411,7 +411,7 @@ function clearToken() { if (kc.token) { - setToken(null, null); + setToken(null, null, null); kc.onAuthLogout && kc.onAuthLogout(); if (kc.loginRequired) { kc.login(); @@ -419,14 +419,14 @@ } } - function setToken(token, refreshToken) { + function setToken(token, refreshToken, idToken) { if (token || refreshToken) { setupCheckLoginIframe(); } if (token) { kc.token = token; - kc.tokenParsed = JSON.parse(decodeURIComponent(escape(window.atob( token.split('.')[1] )))); + kc.tokenParsed = decodeToken(token); var sessionId = kc.realm + '/' + kc.tokenParsed.sub; if (kc.tokenParsed.session_state) { sessionId = sessionId + '/' + kc.tokenParsed.session_state; @@ -436,34 +436,59 @@ kc.subject = kc.tokenParsed.sub; kc.realmAccess = kc.tokenParsed.realm_access; kc.resourceAccess = kc.tokenParsed.resource_access; - - for (var i = 0; i < idTokenProperties.length; i++) { - var n = idTokenProperties[i]; - if (kc.tokenParsed[n]) { - if (!kc.idToken) { - kc.idToken = {}; - } - kc.idToken[n] = kc.tokenParsed[n]; - } - } } else { delete kc.token; delete kc.tokenParsed; delete kc.subject; delete kc.realmAccess; delete kc.resourceAccess; - delete kc.idToken; kc.authenticated = false; } if (refreshToken) { kc.refreshToken = refreshToken; - kc.refreshTokenParsed = JSON.parse(atob(refreshToken.split('.')[1])); + kc.refreshTokenParsed = decodeToken(refreshToken); } else { delete kc.refreshToken; delete kc.refreshTokenParsed; } + + if (idToken) { + kc.idToken = idToken; + kc.idTokenParsed = decodeToken(idToken); + } else { + delete kc.idToken; + delete kc.idTokenParsed; + } + } + + function decodeToken(str) { + str = str.split('.')[1]; + + str = str.replace('/-/g', '+'); + str = str.replace('/_/g', '/'); + switch (str.length % 4) + { + case 0: + break; + case 2: + str += '=='; + break; + case 3: + str += '='; + break; + default: + throw 'Invalid token'; + } + + str = (str + '===').slice(0, str.length + (str.length % 4)); + str = str.replace(/-/g, '+').replace(/_/g, '/'); + + str = decodeURIComponent(escape(atob(str))); + + str = JSON.parse(str); + return str; } function createUUID() { @@ -763,57 +788,15 @@ throw 'invalid adapter type: ' + type; } - - var idTokenProperties = [ - "name", - "given_name", - "family_name", - "middle_name", - "nickname", - "preferred_username", - "profile", - "picture", - "website", - "email", - "email_verified", - "gender", - "birthdate", - "zoneinfo", - "locale", - "phone_number", - "phone_number_verified", - "address", - "updated_at", - "formatted", - "street_address", - "locality", - "region", - "postal_code", - "country", - "claims_locales" - ] } if ( typeof module === "object" && module && typeof module.exports === "object" ) { - // Expose KeyCloak as module.exports in loaders that implement the Node - // module pattern (including browserify). Do not create the global, since - // the user will be storing it themselves locally, and globals are frowned - // upon in the Node module world. module.exports = Keycloak; } else { - // Otherwise expose KeyCloak to the global object as usual window.Keycloak = Keycloak; - // Register as a named AMD module, since KeyCloak can be concatenated with other - // files that may use define, but not via a proper concatenation script that - // understands anonymous AMD modules. A named AMD is safest and most robust - // way to register. Lowercase jquery is used because AMD module names are - // derived from file names, and KeyCloak is normally delivered in a lowercase - // file name. Do this after creating the global so that if an AMD module wants - // to call noConflict to hide this version of KeyCloak, it will work. if ( typeof define === "function" && define.amd ) { define( "keycloak", [], function () { return Keycloak; } ); } } - })( window );