Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bb2ff9e8a |
No files matched your search
+1
-1
@@ -86,7 +86,7 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
|
|||||||
|
|
||||||
RealmModel realm = this.session.getContext().getRealm();
|
RealmModel realm = this.session.getContext().getRealm();
|
||||||
|
|
||||||
return tokenManager.isTokenValid(session, realm, accessToken) ? accessToken : null;
|
return tokenManager.checkTokenValidForIntrospection(session, realm, accessToken) ? accessToken : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -202,7 +202,17 @@ public class TokenManager {
|
|||||||
return new TokenValidation(user, userSession, clientSessionCtx, newToken);
|
return new TokenValidation(user, userSession, clientSessionCtx, newToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTokenValid(KeycloakSession session, RealmModel realm, AccessToken token) throws OAuthErrorException {
|
/**
|
||||||
|
* Checks if the token is valid. Intended usage is for token introspection endpoints as the session last refresh
|
||||||
|
* is updated if the token was valid. This is used to keep the session alive when long lived tokens are used.
|
||||||
|
*
|
||||||
|
* @param session
|
||||||
|
* @param realm
|
||||||
|
* @param token
|
||||||
|
* @return
|
||||||
|
* @throws OAuthErrorException
|
||||||
|
*/
|
||||||
|
public boolean checkTokenValidForIntrospection(KeycloakSession session, RealmModel realm, AccessToken token) throws OAuthErrorException {
|
||||||
if (!token.isActive()) {
|
if (!token.isActive()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -216,17 +226,24 @@ public class TokenManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean valid = false;
|
||||||
|
|
||||||
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
|
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
|
||||||
|
|
||||||
if (AuthenticationManager.isSessionValid(realm, userSession)) {
|
if (AuthenticationManager.isSessionValid(realm, userSession)) {
|
||||||
return isUserValid(session, realm, token, userSession);
|
valid = isUserValid(session, realm, token, userSession);
|
||||||
|
} else {
|
||||||
|
userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId());
|
||||||
|
if (AuthenticationManager.isOfflineSessionValid(realm, userSession)) {
|
||||||
|
valid = isUserValid(session, realm, token, userSession);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId());
|
if (valid) {
|
||||||
if (AuthenticationManager.isOfflineSessionValid(realm, userSession)) {
|
userSession.setLastSessionRefresh(Time.currentTime());
|
||||||
return isUserValid(session, realm, token, userSession);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUserValid(KeycloakSession session, RealmModel realm, AccessToken token, UserSessionModel userSession) {
|
private boolean isUserValid(KeycloakSession session, RealmModel realm, AccessToken token, UserSessionModel userSession) {
|
||||||
|
|||||||
+10
-12
@@ -16,8 +16,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.keycloak.protocol.openshift;
|
package org.keycloak.protocol.openshift;
|
||||||
|
|
||||||
import org.keycloak.RSATokenVerifier;
|
import org.keycloak.TokenVerifier;
|
||||||
import org.keycloak.common.VerificationException;
|
import org.keycloak.common.VerificationException;
|
||||||
|
import org.keycloak.crypto.SignatureProvider;
|
||||||
|
import org.keycloak.crypto.SignatureVerifierContext;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
@@ -39,7 +41,6 @@ import javax.ws.rs.PathParam;
|
|||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,22 +90,19 @@ public class OpenShiftTokenReviewEndpoint implements OIDCExtProvider {
|
|||||||
|
|
||||||
AccessToken token = null;
|
AccessToken token = null;
|
||||||
try {
|
try {
|
||||||
RSATokenVerifier verifier = RSATokenVerifier.create(reviewRequest.getSpec().getToken())
|
TokenVerifier<AccessToken> verifier = TokenVerifier.create(reviewRequest.getSpec().getToken(), AccessToken.class)
|
||||||
.realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
|
.realmUrl(Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
|
||||||
|
|
||||||
PublicKey publicKey = session.keys().getRsaPublicKey(realm, verifier.getHeader().getKeyId());
|
SignatureVerifierContext verifierContext = session.getProvider(SignatureProvider.class, verifier.getHeader().getAlgorithm().name()).verifier(verifier.getHeader().getKeyId());
|
||||||
if (publicKey == null) {
|
verifier.verifierContext(verifierContext);
|
||||||
error(401, Errors.INVALID_TOKEN, "Invalid public key");
|
|
||||||
} else {
|
verifier.verify();
|
||||||
verifier.publicKey(publicKey);
|
token = verifier.getToken();
|
||||||
verifier.verify();
|
|
||||||
token = verifier.getToken();
|
|
||||||
}
|
|
||||||
} catch (VerificationException e) {
|
} catch (VerificationException e) {
|
||||||
error(401, Errors.INVALID_TOKEN, "Token verification failure");
|
error(401, Errors.INVALID_TOKEN, "Token verification failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tokenManager.isTokenValid(session, realm, token)) {
|
if (!tokenManager.checkTokenValidForIntrospection(session, realm, token)) {
|
||||||
error(401, Errors.INVALID_TOKEN, "Token verification failure");
|
error(401, Errors.INVALID_TOKEN, "Token verification failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+87
-8
@@ -6,9 +6,14 @@ import org.junit.Before;
|
|||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.admin.client.resource.ClientResource;
|
||||||
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||||
import org.keycloak.common.util.Base64Url;
|
import org.keycloak.common.util.Base64Url;
|
||||||
|
import org.keycloak.crypto.Algorithm;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper;
|
import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper;
|
||||||
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
||||||
@@ -22,6 +27,7 @@ import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
|||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
import org.keycloak.testsuite.util.UserBuilder;
|
import org.keycloak.testsuite.util.UserBuilder;
|
||||||
@@ -114,6 +120,69 @@ public class OpenShiftTokenReviewEndpointTest extends AbstractTestRealmKeycloakT
|
|||||||
r.assertScope("openid", "email", "profile");
|
r.assertScope("openid", "email", "profile");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void longExpiration() {
|
||||||
|
ClientResource client = ApiUtil.findClientByClientId(adminClient.realm("test"), "test-app");
|
||||||
|
ClientRepresentation clientRep = client.toRepresentation();
|
||||||
|
|
||||||
|
try {
|
||||||
|
clientRep.getAttributes().put(OIDCConfigAttributes.ACCESS_TOKEN_LIFESPAN, "-1");
|
||||||
|
client.update(clientRep);
|
||||||
|
|
||||||
|
// Set time offset just before SSO idle, to get session last refresh updated
|
||||||
|
|
||||||
|
setTimeOffset(1500);
|
||||||
|
|
||||||
|
Review review = new Review();
|
||||||
|
|
||||||
|
review.invoke().assertSuccess();
|
||||||
|
|
||||||
|
// Bump last refresh updated again
|
||||||
|
|
||||||
|
setTimeOffset(3000);
|
||||||
|
|
||||||
|
review.invoke().assertSuccess();
|
||||||
|
|
||||||
|
// And, again
|
||||||
|
|
||||||
|
setTimeOffset(4500);
|
||||||
|
|
||||||
|
// Token should still be valid as session last refresh should have been updated
|
||||||
|
|
||||||
|
review.invoke().assertSuccess();
|
||||||
|
} finally {
|
||||||
|
clientRep.getAttributes().put(OIDCConfigAttributes.ACCESS_TOKEN_LIFESPAN, null);
|
||||||
|
client.update(clientRep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hs256() {
|
||||||
|
RealmResource realm = adminClient.realm("test");
|
||||||
|
RealmRepresentation rep = realm.toRepresentation();
|
||||||
|
|
||||||
|
try {
|
||||||
|
rep.setDefaultSignatureAlgorithm(Algorithm.HS256);
|
||||||
|
realm.update(rep);
|
||||||
|
|
||||||
|
Review r = new Review().algorithm(Algorithm.HS256).invoke()
|
||||||
|
.assertSuccess();
|
||||||
|
|
||||||
|
String userId = testRealm().users().search(r.username).get(0).getId();
|
||||||
|
|
||||||
|
OpenShiftTokenReviewResponseRepresentation.User user = r.response.getStatus().getUser();
|
||||||
|
|
||||||
|
assertEquals(userId, user.getUid());
|
||||||
|
assertEquals("test-user@localhost", user.getUsername());
|
||||||
|
assertNotNull(user.getExtra());
|
||||||
|
|
||||||
|
r.assertScope("openid", "email", "profile");
|
||||||
|
} finally {
|
||||||
|
rep.setDefaultSignatureAlgorithm(null);
|
||||||
|
realm.update(rep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void groups() {
|
public void groups() {
|
||||||
new Review().username("groups-user")
|
new Review().username("groups-user")
|
||||||
@@ -194,7 +263,7 @@ public class OpenShiftTokenReviewEndpointTest extends AbstractTestRealmKeycloakT
|
|||||||
i.token = i.token.replaceFirst(header, newHeader);
|
i.token = i.token.replaceFirst(header, newHeader);
|
||||||
})
|
})
|
||||||
.invoke()
|
.invoke()
|
||||||
.assertError(401, "Invalid public key");
|
.assertError(401, "Token verification failure");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -251,6 +320,7 @@ public class OpenShiftTokenReviewEndpointTest extends AbstractTestRealmKeycloakT
|
|||||||
private String clientId = "test-app";
|
private String clientId = "test-app";
|
||||||
private String username = "test-user@localhost";
|
private String username = "test-user@localhost";
|
||||||
private String password = "password";
|
private String password = "password";
|
||||||
|
private String algorithm = Algorithm.RS256;
|
||||||
private InvokeRunnable runAfterTokenRequest;
|
private InvokeRunnable runAfterTokenRequest;
|
||||||
|
|
||||||
private String token;
|
private String token;
|
||||||
@@ -262,6 +332,11 @@ public class OpenShiftTokenReviewEndpointTest extends AbstractTestRealmKeycloakT
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Review algorithm(String algorithm) {
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public Review runAfterTokenRequest(InvokeRunnable runnable) {
|
public Review runAfterTokenRequest(InvokeRunnable runnable) {
|
||||||
this.runAfterTokenRequest = runnable;
|
this.runAfterTokenRequest = runnable;
|
||||||
return this;
|
return this;
|
||||||
@@ -269,16 +344,20 @@ public class OpenShiftTokenReviewEndpointTest extends AbstractTestRealmKeycloakT
|
|||||||
|
|
||||||
public Review invoke() {
|
public Review invoke() {
|
||||||
try {
|
try {
|
||||||
String userId = testRealm().users().search(username).get(0).getId();
|
if (token == null) {
|
||||||
oauth.doLogin(username, password);
|
String userId = testRealm().users().search(username).get(0).getId();
|
||||||
EventRepresentation loginEvent = events.expectLogin().user(userId).assertEvent();
|
oauth.doLogin(username, password);
|
||||||
|
EventRepresentation loginEvent = events.expectLogin().user(userId).assertEvent();
|
||||||
|
|
||||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||||
OAuthClient.AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
|
OAuthClient.AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||||
|
|
||||||
events.expectCodeToToken(loginEvent.getDetails().get(Details.CODE_ID), loginEvent.getSessionId()).detail("client_auth_method", "testsuite-client-dummy").user(userId).assertEvent();
|
events.expectCodeToToken(loginEvent.getDetails().get(Details.CODE_ID), loginEvent.getSessionId()).detail("client_auth_method", "testsuite-client-dummy").user(userId).assertEvent();
|
||||||
|
|
||||||
token = accessTokenResponse.getAccessToken();
|
token = accessTokenResponse.getAccessToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(algorithm, new JWSInput(token).getHeader().getAlgorithm().name());
|
||||||
|
|
||||||
if (runAfterTokenRequest != null) {
|
if (runAfterTokenRequest != null) {
|
||||||
runAfterTokenRequest.run(this);
|
runAfterTokenRequest.run(this);
|
||||||
|
|||||||
Reference in new issue
Block a user