Merge pull request #862 from stianst/master
KEYCLOAK-791 Denial of Service by invalid character injection
This commit is contained in:
commit
0327bc0d5a
4 changed files with 46 additions and 60 deletions
|
@ -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>
|
<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'.
|
From the 'tomcat7' package to just 'tomcat'.
|
||||||
</listitem>
|
</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>
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
|
|
@ -157,6 +157,7 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
|
||||||
<listitem>tokenParsed - the parsed token</listitem>
|
<listitem>tokenParsed - the parsed token</listitem>
|
||||||
<listitem>subject - the user id</listitem>
|
<listitem>subject - the user id</listitem>
|
||||||
<listitem>idToken - the id token if claims is enabled for the application, null otherwise</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>realmAccess - the realm roles associated with the token</listitem>
|
||||||
<listitem>resourceAccess - the resource roles assocaited 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>
|
<listitem>refreshToken - the base64 encoded token that can be used to retrieve a new token</listitem>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
<button onclick="loadProfile()">Get Profile</button>
|
<button onclick="loadProfile()">Get Profile</button>
|
||||||
<button onclick="output(keycloak.tokenParsed)">Show Token</button>
|
<button onclick="output(keycloak.tokenParsed)">Show Token</button>
|
||||||
<button onclick="output(keycloak.refreshTokenParsed)">Show Refresh 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="showExpires()">Show Expires</button>
|
||||||
<button onclick="output(keycloak.idToken)">Show ID Token</button>
|
<button onclick="output(keycloak.idToken)">Show ID Token</button>
|
||||||
<button onclick="output(keycloak)">Show Details</button>
|
<button onclick="output(keycloak)">Show Details</button>
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initOptions.token || initOptions.refreshToken) {
|
if (initOptions.token || initOptions.refreshToken) {
|
||||||
setToken(initOptions.token, initOptions.refreshToken);
|
setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
|
||||||
initPromise.setSuccess();
|
initPromise.setSuccess();
|
||||||
} else if (initOptions.onLoad) {
|
} else if (initOptions.onLoad) {
|
||||||
var options = {};
|
var options = {};
|
||||||
|
@ -255,7 +255,7 @@
|
||||||
if (req.readyState == 4) {
|
if (req.readyState == 4) {
|
||||||
if (req.status == 200) {
|
if (req.status == 200) {
|
||||||
var tokenResponse = JSON.parse(req.responseText);
|
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();
|
kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
|
||||||
for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
|
for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
|
||||||
p.setSuccess(true);
|
p.setSuccess(true);
|
||||||
|
@ -325,7 +325,7 @@
|
||||||
if (req.readyState == 4) {
|
if (req.readyState == 4) {
|
||||||
if (req.status == 200) {
|
if (req.status == 200) {
|
||||||
var tokenResponse = JSON.parse(req.responseText);
|
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();
|
kc.onAuthSuccess && kc.onAuthSuccess();
|
||||||
promise && promise.setSuccess();
|
promise && promise.setSuccess();
|
||||||
} else {
|
} else {
|
||||||
|
@ -411,7 +411,7 @@
|
||||||
|
|
||||||
function clearToken() {
|
function clearToken() {
|
||||||
if (kc.token) {
|
if (kc.token) {
|
||||||
setToken(null, null);
|
setToken(null, null, null);
|
||||||
kc.onAuthLogout && kc.onAuthLogout();
|
kc.onAuthLogout && kc.onAuthLogout();
|
||||||
if (kc.loginRequired) {
|
if (kc.loginRequired) {
|
||||||
kc.login();
|
kc.login();
|
||||||
|
@ -419,14 +419,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setToken(token, refreshToken) {
|
function setToken(token, refreshToken, idToken) {
|
||||||
if (token || refreshToken) {
|
if (token || refreshToken) {
|
||||||
setupCheckLoginIframe();
|
setupCheckLoginIframe();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
kc.token = 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;
|
var sessionId = kc.realm + '/' + kc.tokenParsed.sub;
|
||||||
if (kc.tokenParsed.session_state) {
|
if (kc.tokenParsed.session_state) {
|
||||||
sessionId = sessionId + '/' + kc.tokenParsed.session_state;
|
sessionId = sessionId + '/' + kc.tokenParsed.session_state;
|
||||||
|
@ -436,34 +436,59 @@
|
||||||
kc.subject = kc.tokenParsed.sub;
|
kc.subject = kc.tokenParsed.sub;
|
||||||
kc.realmAccess = kc.tokenParsed.realm_access;
|
kc.realmAccess = kc.tokenParsed.realm_access;
|
||||||
kc.resourceAccess = kc.tokenParsed.resource_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 {
|
} else {
|
||||||
delete kc.token;
|
delete kc.token;
|
||||||
delete kc.tokenParsed;
|
delete kc.tokenParsed;
|
||||||
delete kc.subject;
|
delete kc.subject;
|
||||||
delete kc.realmAccess;
|
delete kc.realmAccess;
|
||||||
delete kc.resourceAccess;
|
delete kc.resourceAccess;
|
||||||
delete kc.idToken;
|
|
||||||
|
|
||||||
kc.authenticated = false;
|
kc.authenticated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refreshToken) {
|
if (refreshToken) {
|
||||||
kc.refreshToken = refreshToken;
|
kc.refreshToken = refreshToken;
|
||||||
kc.refreshTokenParsed = JSON.parse(atob(refreshToken.split('.')[1]));
|
kc.refreshTokenParsed = decodeToken(refreshToken);
|
||||||
} else {
|
} else {
|
||||||
delete kc.refreshToken;
|
delete kc.refreshToken;
|
||||||
delete kc.refreshTokenParsed;
|
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() {
|
function createUUID() {
|
||||||
|
@ -763,57 +788,15 @@
|
||||||
|
|
||||||
throw 'invalid adapter type: ' + type;
|
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" ) {
|
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;
|
module.exports = Keycloak;
|
||||||
} else {
|
} else {
|
||||||
// Otherwise expose KeyCloak to the global object as usual
|
|
||||||
window.Keycloak = Keycloak;
|
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 ) {
|
if ( typeof define === "function" && define.amd ) {
|
||||||
define( "keycloak", [], function () { return Keycloak; } );
|
define( "keycloak", [], function () { return Keycloak; } );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
})( window );
|
})( window );
|
||||||
|
|
Loading…
Reference in a new issue