Merge pull request #862 from stianst/master

KEYCLOAK-791 Denial of Service by invalid character injection
This commit is contained in:
Stian Thorgersen 2014-11-18 14:59:01 +01:00
commit 0327bc0d5a
4 changed files with 46 additions and 60 deletions

View file

@ -85,6 +85,7 @@
<listitem>The tomcat adapter valve has moved to a different package. From <literal>org.keycloak.adapters.tomcat7.KeycloakAuthenticatorValve</literal> to <literal>org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve</literal>
From the 'tomcat7' package to just 'tomcat'.
</listitem>
<listitem>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.</listitem>
</itemizedlist>
</section>
<section>

View file

@ -157,6 +157,7 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
<listitem>tokenParsed - the parsed token</listitem>
<listitem>subject - the user id</listitem>
<listitem>idToken - the id token if claims is enabled for the application, null otherwise</listitem>
<listitem>idTokenParsed - the parsed id token</listitem>
<listitem>realmAccess - the realm roles associated with the token</listitem>
<listitem>resourceAccess - the resource roles assocaited with the token</listitem>
<listitem>refreshToken - the base64 encoded token that can be used to retrieve a new token</listitem>

View file

@ -12,6 +12,7 @@
<button onclick="loadProfile()">Get Profile</button>
<button onclick="output(keycloak.tokenParsed)">Show Token</button>
<button onclick="output(keycloak.refreshTokenParsed)">Show Refresh Token</button>
<button onclick="output(keycloak.idTokenParsed)">Show ID Token</button>
<button onclick="showExpires()">Show Expires</button>
<button onclick="output(keycloak.idToken)">Show ID Token</button>
<button onclick="output(keycloak)">Show Details</button>

View file

@ -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 );