stateless access codes
This commit is contained in:
parent
a1e9d99d3e
commit
d21a19925b
12 changed files with 312 additions and 237 deletions
102
core/src/main/java/org/keycloak/representations/AccessCode.java
Executable file
102
core/src/main/java/org/keycloak/representations/AccessCode.java
Executable file
|
@ -0,0 +1,102 @@
|
|||
package org.keycloak.representations;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AccessCode {
|
||||
protected String id;
|
||||
protected String usernameUsed;
|
||||
protected String state;
|
||||
protected String redirectUri;
|
||||
protected boolean rememberMe;
|
||||
protected String authMethod;
|
||||
protected int timestamp;
|
||||
protected int expiration;
|
||||
protected AccessToken accessToken;
|
||||
protected Set<String> requiredActions;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
this.redirectUri = redirectUri;
|
||||
}
|
||||
|
||||
public boolean isRememberMe() {
|
||||
return rememberMe;
|
||||
}
|
||||
|
||||
public void setRememberMe(boolean rememberMe) {
|
||||
this.rememberMe = rememberMe;
|
||||
}
|
||||
|
||||
public String getAuthMethod() {
|
||||
return authMethod;
|
||||
}
|
||||
|
||||
public void setAuthMethod(String authMethod) {
|
||||
this.authMethod = authMethod;
|
||||
}
|
||||
|
||||
public int getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(int expiration) {
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
public AccessToken getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(AccessToken accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(int timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public Set<String> getRequiredActions() {
|
||||
return requiredActions;
|
||||
}
|
||||
|
||||
public void setRequiredActions(Set<String> requiredActions) {
|
||||
this.requiredActions = requiredActions;
|
||||
}
|
||||
|
||||
public String getUsernameUsed() {
|
||||
return usernameUsed;
|
||||
}
|
||||
|
||||
public void setUsernameUsed(String usernameUsed) {
|
||||
this.usernameUsed = usernameUsed;
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ public interface LoginFormsProvider extends Provider {
|
|||
|
||||
public Response createCode();
|
||||
|
||||
public LoginFormsProvider setAccessCode(String accessCodeId, String accessCode);
|
||||
public LoginFormsProvider setAccessCode(String accessCode);
|
||||
|
||||
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String,RoleModel> resourceRolesRequested);
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
private static final Logger logger = Logger.getLogger(FreeMarkerLoginFormsProvider.class);
|
||||
|
||||
private String message;
|
||||
private String accessCodeId;
|
||||
private String accessCode;
|
||||
private Response.Status status = Response.Status.OK;
|
||||
private List<RoleModel> realmRolesRequested;
|
||||
|
@ -108,7 +107,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
case VERIFY_EMAIL:
|
||||
try {
|
||||
UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri());
|
||||
builder.queryParam("key", accessCodeId);
|
||||
builder.queryParam("key", accessCode);
|
||||
|
||||
String link = builder.build(realm.getName()).toString();
|
||||
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
||||
|
@ -284,8 +283,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public LoginFormsProvider setAccessCode(String accessCodeId, String accessCode) {
|
||||
this.accessCodeId = accessCodeId;
|
||||
public LoginFormsProvider setAccessCode(String accessCode) {
|
||||
this.accessCode = accessCode;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
package org.keycloak.services.managers;
|
||||
|
||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||
import org.keycloak.jose.jws.JWSBuilder;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserModel.RequiredAction;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.AccessCode;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
@ -21,141 +24,101 @@ import java.util.UUID;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AccessCodeEntry {
|
||||
protected String id = UUID.randomUUID().toString() + System.currentTimeMillis();
|
||||
protected String code;
|
||||
protected String state;
|
||||
protected String sessionState;
|
||||
protected String redirectUri;
|
||||
protected boolean rememberMe;
|
||||
protected String authMethod;
|
||||
protected String username;
|
||||
|
||||
protected int expiration;
|
||||
protected AccessCode accessCode;
|
||||
protected RealmModel realm;
|
||||
protected AccessToken token;
|
||||
protected UserModel user;
|
||||
protected Set<RequiredAction> requiredActions;
|
||||
protected ClientModel client;
|
||||
protected List<RoleModel> realmRolesRequested = new ArrayList<RoleModel>();
|
||||
MultivaluedMap<String, RoleModel> resourceRolesRequested = new MultivaluedMapImpl<String, RoleModel>();
|
||||
|
||||
public boolean isExpired() {
|
||||
return expiration != 0 && Time.currentTime() > expiration;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public RealmModel getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
public void setRealm(RealmModel realm) {
|
||||
public AccessCodeEntry(RealmModel realm, AccessCode accessCode) {
|
||||
this.realm = realm;
|
||||
this.accessCode = accessCode;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(int expiration) {
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
public AccessToken getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(AccessToken token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public ClientModel getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(ClientModel client) {
|
||||
this.client = client;
|
||||
public String getCodeId() {
|
||||
return this.accessCode.getId();
|
||||
}
|
||||
|
||||
public UserModel getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(UserModel user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Set<RequiredAction> getRequiredActions() {
|
||||
return requiredActions;
|
||||
}
|
||||
|
||||
public void setRequiredActions(Set<RequiredAction> requiredActions) {
|
||||
this.requiredActions = requiredActions;
|
||||
}
|
||||
|
||||
public List<RoleModel> getRealmRolesRequested() {
|
||||
return realmRolesRequested;
|
||||
}
|
||||
|
||||
public MultivaluedMap<String, RoleModel> getResourceRolesRequested() {
|
||||
return resourceRolesRequested;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
return realm.getUserById(accessCode.getAccessToken().getSubject());
|
||||
}
|
||||
|
||||
public String getSessionState() {
|
||||
return sessionState;
|
||||
return accessCode.getAccessToken().getSessionState();
|
||||
}
|
||||
|
||||
public void setSessionState(String sessionState) {
|
||||
this.sessionState = sessionState;
|
||||
public boolean isExpired() {
|
||||
return accessCode.getExpiration() != 0 && Time.currentTime() > accessCode.getExpiration();
|
||||
}
|
||||
|
||||
public AccessToken getToken() {
|
||||
return accessCode.getAccessToken();
|
||||
}
|
||||
|
||||
public ClientModel getClient() {
|
||||
return realm.findClient(accessCode.getAccessToken().getIssuedFor());
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return accessCode.getState();
|
||||
}
|
||||
|
||||
public String getRedirectUri() {
|
||||
return redirectUri;
|
||||
}
|
||||
|
||||
public void setRedirectUri(String redirectUri) {
|
||||
this.redirectUri = redirectUri;
|
||||
return accessCode.getRedirectUri();
|
||||
}
|
||||
|
||||
public boolean isRememberMe() {
|
||||
return rememberMe;
|
||||
return accessCode.isRememberMe();
|
||||
}
|
||||
|
||||
public void setRememberMe(boolean rememberMe) {
|
||||
this.rememberMe = rememberMe;
|
||||
public void setRememberMe(boolean remember) {
|
||||
accessCode.setRememberMe(remember);
|
||||
}
|
||||
|
||||
public String getAuthMethod() {
|
||||
return authMethod;
|
||||
return accessCode.getAuthMethod();
|
||||
}
|
||||
|
||||
public String getUsernameUsed() {
|
||||
return accessCode.getUsernameUsed();
|
||||
}
|
||||
|
||||
public void setUsernameUsed(String username) {
|
||||
accessCode.setUsernameUsed(username);
|
||||
}
|
||||
|
||||
public void resetExpiration() {
|
||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
|
||||
|
||||
}
|
||||
|
||||
public void setAuthMethod(String authMethod) {
|
||||
this.authMethod = authMethod;
|
||||
accessCode.setAuthMethod(authMethod);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
public Set<RequiredAction> getRequiredActions() {
|
||||
Set<RequiredAction> set = new HashSet<RequiredAction>();
|
||||
for (String action : accessCode.getRequiredActions()) {
|
||||
set.add(RequiredAction.valueOf(action));
|
||||
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
public boolean hasRequiredAction(RequiredAction action) {
|
||||
return accessCode.getRequiredActions().contains(action.toString());
|
||||
}
|
||||
|
||||
public void removeRequiredAction(RequiredAction action) {
|
||||
accessCode.getRequiredActions().remove(action.toString());
|
||||
}
|
||||
|
||||
public void setRequiredActions(Set<RequiredAction> set) {
|
||||
Set<String> newSet = new HashSet<String>();
|
||||
for (RequiredAction action : set) {
|
||||
newSet.add(action.toString());
|
||||
}
|
||||
accessCode.setRequiredActions(newSet);
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return new JWSBuilder().jsonContent(accessCode).rsa256(realm.getPrivateKey());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.keycloak.models.RoleModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.representations.AccessCode;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
@ -31,6 +32,7 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
|
@ -42,6 +44,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
public class TokenManager {
|
||||
protected static final Logger logger = Logger.getLogger(TokenManager.class);
|
||||
|
||||
/*
|
||||
protected Map<String, AccessCodeEntry> accessCodeMap = new ConcurrentHashMap<String, AccessCodeEntry>();
|
||||
|
||||
public void clearAccessCodes() {
|
||||
|
@ -55,6 +58,23 @@ public class TokenManager {
|
|||
public AccessCodeEntry pullAccessCode(String key) {
|
||||
return accessCodeMap.remove(key);
|
||||
}
|
||||
*/
|
||||
|
||||
public AccessCodeEntry parseCode(String code, RealmModel realm) {
|
||||
try {
|
||||
JWSInput input = new JWSInput(code);
|
||||
if (!RSAProvider.verify(input, realm.getPublicKey())) {
|
||||
logger.error("Could not verify access code");
|
||||
return null;
|
||||
}
|
||||
AccessCode accessCode = input.readJsonContent(AccessCode.class);
|
||||
return new AccessCodeEntry(realm, accessCode);
|
||||
} catch (Exception e) {
|
||||
logger.error("error parsing access code", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void applyScope(RoleModel role, RoleModel scope, Set<RoleModel> visited, Set<RoleModel> requested) {
|
||||
if (visited.contains(scope)) return;
|
||||
|
@ -73,38 +93,25 @@ public class TokenManager {
|
|||
|
||||
|
||||
public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
|
||||
AccessCodeEntry code = createAccessCodeEntry(scopeParam, state, redirect, realm, client, user, session);
|
||||
accessCodeMap.put(code.getId(), code);
|
||||
return code;
|
||||
return createAccessCodeEntry(scopeParam, state, redirect, realm, client, user, session);
|
||||
}
|
||||
|
||||
private AccessCodeEntry createAccessCodeEntry(String scopeParam, String state, String redirect, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
|
||||
AccessCodeEntry code = new AccessCodeEntry();
|
||||
if (session != null) {
|
||||
code.setSessionState(session.getId());
|
||||
}
|
||||
|
||||
List<RoleModel> realmRolesRequested = code.getRealmRolesRequested();
|
||||
MultivaluedMap<String, RoleModel> resourceRolesRequested = code.getResourceRolesRequested();
|
||||
List<RoleModel> realmRolesRequested = new LinkedList<RoleModel>();
|
||||
MultivaluedMap<String, RoleModel> resourceRolesRequested = new MultivaluedMapImpl<String, RoleModel>();
|
||||
|
||||
AccessToken token = createClientAccessToken(scopeParam, realm, client, user, session, realmRolesRequested, resourceRolesRequested);
|
||||
token.setSessionState(code.getSessionState());
|
||||
|
||||
code.setToken(token);
|
||||
code.setRealm(realm);
|
||||
if (session != null) token.setSessionState(session.getId());
|
||||
AccessCode code = new AccessCode();
|
||||
code.setId(UUID.randomUUID().toString() + System.currentTimeMillis());
|
||||
code.setAccessToken(token);
|
||||
code.setTimestamp(Time.currentTime());
|
||||
code.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
|
||||
code.setClient(client);
|
||||
code.setUser(user);
|
||||
code.setState(state);
|
||||
code.setRedirectUri(redirect);
|
||||
String accessCode = null;
|
||||
try {
|
||||
accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
code.setCode(accessCode);
|
||||
return code;
|
||||
AccessCodeEntry entry = new AccessCodeEntry(realm, code);
|
||||
return entry;
|
||||
|
||||
}
|
||||
|
||||
public AccessToken refreshAccessToken(UriInfo uriInfo, RealmModel realm, ClientModel client, String encodedRefreshToken, Audit audit) throws OAuthErrorException {
|
||||
|
|
|
@ -31,8 +31,6 @@ import org.keycloak.audit.EventType;
|
|||
import org.keycloak.email.EmailException;
|
||||
import org.keycloak.email.EmailProvider;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
|
@ -52,7 +50,6 @@ import org.keycloak.services.resources.flows.Urls;
|
|||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.authentication.AuthenticationProviderException;
|
||||
import org.keycloak.authentication.AuthenticationProviderManager;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
|
@ -134,7 +131,7 @@ public class RequiredActionsService {
|
|||
user.setEmail(email);
|
||||
|
||||
user.removeRequiredAction(RequiredAction.UPDATE_PROFILE);
|
||||
accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PROFILE);
|
||||
accessCode.removeRequiredAction(RequiredAction.UPDATE_PROFILE);
|
||||
|
||||
audit.clone().event(EventType.UPDATE_PROFILE).success();
|
||||
if (emailChanged) {
|
||||
|
@ -176,7 +173,7 @@ public class RequiredActionsService {
|
|||
user.setTotp(true);
|
||||
|
||||
user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
||||
accessCode.getRequiredActions().remove(RequiredAction.CONFIGURE_TOTP);
|
||||
accessCode.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
||||
|
||||
audit.clone().event(EventType.UPDATE_TOTP).success();
|
||||
|
||||
|
@ -222,7 +219,7 @@ public class RequiredActionsService {
|
|||
|
||||
user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
|
||||
if (accessCode != null) {
|
||||
accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PASSWORD);
|
||||
accessCode.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
|
||||
}
|
||||
|
||||
audit.clone().event(EventType.UPDATE_PASSWORD).success();
|
||||
|
@ -235,9 +232,9 @@ public class RequiredActionsService {
|
|||
@GET
|
||||
public Response emailVerification() {
|
||||
if (uriInfo.getQueryParameters().containsKey("key")) {
|
||||
AccessCodeEntry accessCode = tokenManager.getAccessCode(uriInfo.getQueryParameters().getFirst("key"));
|
||||
AccessCodeEntry accessCode = tokenManager.parseCode(uriInfo.getQueryParameters().getFirst("key"), realm);
|
||||
if (accessCode == null || accessCode.isExpired()
|
||||
|| !accessCode.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL)) {
|
||||
|| !accessCode.hasRequiredAction(RequiredAction.VERIFY_EMAIL)) {
|
||||
return unauthorized();
|
||||
}
|
||||
|
||||
|
@ -248,7 +245,7 @@ public class RequiredActionsService {
|
|||
user.setEmailVerified(true);
|
||||
|
||||
user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
|
||||
accessCode.getRequiredActions().remove(RequiredAction.VERIFY_EMAIL);
|
||||
accessCode.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
|
||||
|
||||
audit.clone().event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
||||
|
||||
|
@ -262,7 +259,7 @@ public class RequiredActionsService {
|
|||
initAudit(accessCode);
|
||||
//audit.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
||||
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).setUser(accessCode.getUser())
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getCode()).setUser(accessCode.getUser())
|
||||
.createResponse(RequiredAction.VERIFY_EMAIL);
|
||||
}
|
||||
}
|
||||
|
@ -271,14 +268,14 @@ public class RequiredActionsService {
|
|||
@GET
|
||||
public Response passwordReset() {
|
||||
if (uriInfo.getQueryParameters().containsKey("key")) {
|
||||
AccessCodeEntry accessCode = tokenManager.getAccessCode(uriInfo.getQueryParameters().getFirst("key"));
|
||||
AccessCodeEntry accessCode = tokenManager.parseCode(uriInfo.getQueryParameters().getFirst("key"), realm);
|
||||
accessCode.setAuthMethod("form");
|
||||
if (accessCode == null || accessCode.isExpired()
|
||||
|| !accessCode.getRequiredActions().contains(RequiredAction.UPDATE_PASSWORD)) {
|
||||
|| !accessCode.hasRequiredAction(RequiredAction.UPDATE_PASSWORD)) {
|
||||
return unauthorized();
|
||||
}
|
||||
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).createResponse(RequiredAction.UPDATE_PASSWORD);
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getCode()).createResponse(RequiredAction.UPDATE_PASSWORD);
|
||||
} else {
|
||||
return Flows.forms(providerSession, realm, uriInfo).createPasswordReset();
|
||||
}
|
||||
|
@ -330,20 +327,19 @@ public class RequiredActionsService {
|
|||
|
||||
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user, session);
|
||||
accessCode.setRequiredActions(requiredActions);
|
||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
|
||||
accessCode.setAuthMethod("form");
|
||||
accessCode.setUsername(username);
|
||||
accessCode.setUsernameUsed(username);
|
||||
|
||||
try {
|
||||
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
|
||||
builder.queryParam("key", accessCode.getId());
|
||||
builder.queryParam("key", accessCode.getCode());
|
||||
|
||||
String link = builder.build(realm.getName()).toString();
|
||||
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
||||
|
||||
providerSession.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendPasswordReset(link, expiration);
|
||||
|
||||
audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getId()).success();
|
||||
audit.user(user).detail(Details.EMAIL, user.getEmail()).detail(Details.CODE_ID, accessCode.getCodeId()).success();
|
||||
} catch (EmailException e) {
|
||||
logger.error("Failed to send password reset email", e);
|
||||
return Flows.forms(providerSession, realm, uriInfo).setError("emailSendError").createErrorPage();
|
||||
|
@ -360,31 +356,15 @@ public class RequiredActionsService {
|
|||
return null;
|
||||
}
|
||||
|
||||
JWSInput input = new JWSInput(code);
|
||||
boolean verifiedCode = false;
|
||||
try {
|
||||
verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
|
||||
} catch (Exception ignored) {
|
||||
logger.debug("getAccessCodeEntry code failed verification");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!verifiedCode) {
|
||||
logger.debug("getAccessCodeEntry code failed verification2");
|
||||
return null;
|
||||
}
|
||||
|
||||
String key = input.readContentAsString();
|
||||
AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key);
|
||||
AccessCodeEntry accessCodeEntry = tokenManager.parseCode(code, realm);
|
||||
if (accessCodeEntry == null) {
|
||||
logger.debug("getAccessCodeEntry access code entry null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (accessCodeEntry.isExpired()) {
|
||||
logger.debugv("getAccessCodeEntry: access code id: {0}", accessCodeEntry.getId());
|
||||
logger.debugv("getAccessCodeEntry access code entry expired: {0}", accessCodeEntry.getExpiration());
|
||||
logger.debugv("getAccessCodeEntry current time: {0}", Time.currentTime());
|
||||
logger.debugv("getAccessCodeEntry: access code id: {0}", accessCodeEntry.getCodeId());
|
||||
logger.debugv("getAccessCodeEntry access code entry expired");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -407,11 +387,11 @@ public class RequiredActionsService {
|
|||
|
||||
Set<RequiredAction> requiredActions = user.getRequiredActions();
|
||||
if (!requiredActions.isEmpty()) {
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).setUser(user)
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getCode()).setUser(user)
|
||||
.createResponse(requiredActions.iterator().next());
|
||||
} else {
|
||||
logger.debugv("redirectOauth: redirecting to: {0}", accessCode.getRedirectUri());
|
||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
|
||||
accessCode.resetExpiration();
|
||||
|
||||
AuthenticationManager authManager = new AuthenticationManager(providerSession);
|
||||
|
||||
|
@ -433,11 +413,11 @@ public class RequiredActionsService {
|
|||
audit.event(EventType.LOGIN).client(accessCode.getClient())
|
||||
.user(accessCode.getUser())
|
||||
.session(accessCode.getSessionState())
|
||||
.detail(Details.CODE_ID, accessCode.getId())
|
||||
.detail(Details.CODE_ID, accessCode.getCodeId())
|
||||
.detail(Details.REDIRECT_URI, accessCode.getRedirectUri())
|
||||
.detail(Details.RESPONSE_TYPE, "code")
|
||||
.detail(Details.AUTH_METHOD, accessCode.getAuthMethod())
|
||||
.detail(Details.USERNAME, accessCode.getUsername());
|
||||
.detail(Details.USERNAME, accessCode.getUsernameUsed());
|
||||
|
||||
if (accessCode.isRememberMe()) {
|
||||
audit.detail(Details.REMEMBER_ME, "true");
|
||||
|
|
|
@ -16,8 +16,6 @@ import org.keycloak.audit.Errors;
|
|||
import org.keycloak.audit.EventType;
|
||||
import org.keycloak.authentication.AuthenticationProviderException;
|
||||
import org.keycloak.authentication.AuthenticationProviderManager;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
|
@ -46,7 +44,6 @@ import org.keycloak.services.resources.flows.OAuthFlows;
|
|||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
|
@ -630,26 +627,9 @@ public class TokenService {
|
|||
throw new BadRequestException("Code not specified", Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build());
|
||||
}
|
||||
|
||||
JWSInput input = new JWSInput(code);
|
||||
boolean verifiedCode = false;
|
||||
try {
|
||||
verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
|
||||
} catch (Exception ignored) {
|
||||
logger.debug("Failed to verify signature", ignored);
|
||||
}
|
||||
if (!verifiedCode) {
|
||||
Map<String, String> res = new HashMap<String, String>();
|
||||
res.put(OAuth2Constants.ERROR, "invalid_grant");
|
||||
res.put(OAuth2Constants.ERROR_DESCRIPTION, "Unable to verify code signature");
|
||||
audit.error(Errors.INVALID_CODE);
|
||||
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
|
||||
.build();
|
||||
}
|
||||
String key = input.readContentAsString();
|
||||
|
||||
audit.detail(Details.CODE_ID, key);
|
||||
|
||||
AccessCodeEntry accessCode = tokenManager.pullAccessCode(key);
|
||||
AccessCodeEntry accessCode = tokenManager.parseCode(code, realm);
|
||||
if (accessCode == null) {
|
||||
Map<String, String> res = new HashMap<String, String>();
|
||||
res.put(OAuth2Constants.ERROR, "invalid_grant");
|
||||
|
@ -658,12 +638,7 @@ public class TokenService {
|
|||
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
|
||||
.build();
|
||||
}
|
||||
|
||||
audit.user(accessCode.getUser());
|
||||
audit.session(accessCode.getSessionState());
|
||||
|
||||
ClientModel client = authorizeClient(authorizationHeader, formData, audit);
|
||||
|
||||
audit.detail(Details.CODE_ID, accessCode.getCodeId());
|
||||
if (accessCode.isExpired()) {
|
||||
Map<String, String> res = new HashMap<String, String>();
|
||||
res.put(OAuth2Constants.ERROR, "invalid_grant");
|
||||
|
@ -680,6 +655,12 @@ public class TokenService {
|
|||
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
|
||||
.build();
|
||||
}
|
||||
|
||||
audit.user(accessCode.getUser());
|
||||
audit.session(accessCode.getSessionState());
|
||||
|
||||
ClientModel client = authorizeClient(authorizationHeader, formData, audit);
|
||||
|
||||
if (!client.getClientId().equals(accessCode.getClient().getClientId())) {
|
||||
Map<String, String> res = new HashMap<String, String>();
|
||||
res.put(OAuth2Constants.ERROR, "invalid_grant");
|
||||
|
@ -993,25 +974,13 @@ public class TokenService {
|
|||
}
|
||||
|
||||
String code = formData.getFirst(OAuth2Constants.CODE);
|
||||
JWSInput input = new JWSInput(code);
|
||||
boolean verifiedCode = false;
|
||||
try {
|
||||
verifiedCode = RSAProvider.verify(input, realm.getPublicKey());
|
||||
} catch (Exception ignored) {
|
||||
logger.debug("Failed to verify signature", ignored);
|
||||
}
|
||||
if (!verifiedCode) {
|
||||
audit.error(Errors.INVALID_CODE);
|
||||
return oauth.forwardToSecurityFailure("Illegal access code.");
|
||||
}
|
||||
String key = input.readContentAsString();
|
||||
audit.detail(Details.CODE_ID, key);
|
||||
|
||||
AccessCodeEntry accessCodeEntry = tokenManager.getAccessCode(key);
|
||||
AccessCodeEntry accessCodeEntry = tokenManager.parseCode(code, realm);
|
||||
if (accessCodeEntry == null) {
|
||||
audit.error(Errors.INVALID_CODE);
|
||||
return oauth.forwardToSecurityFailure("Unknown access code.");
|
||||
}
|
||||
audit.detail(Details.CODE_ID, accessCodeEntry.getCodeId());
|
||||
|
||||
String redirect = accessCodeEntry.getRedirectUri();
|
||||
String state = accessCodeEntry.getState();
|
||||
|
@ -1021,7 +990,7 @@ public class TokenService {
|
|||
.detail(Details.RESPONSE_TYPE, "code")
|
||||
.detail(Details.AUTH_METHOD, accessCodeEntry.getAuthMethod())
|
||||
.detail(Details.REDIRECT_URI, redirect)
|
||||
.detail(Details.USERNAME, accessCodeEntry.getUsername());
|
||||
.detail(Details.USERNAME, accessCodeEntry.getUsernameUsed());
|
||||
|
||||
if (accessCodeEntry.isRememberMe()) {
|
||||
audit.detail(Details.REMEMBER_ME, "true");
|
||||
|
@ -1042,7 +1011,7 @@ public class TokenService {
|
|||
|
||||
audit.success();
|
||||
|
||||
accessCodeEntry.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
|
||||
accessCodeEntry.resetExpiration();
|
||||
return oauth.redirectAccessCode(accessCodeEntry, session, state, redirect);
|
||||
}
|
||||
|
||||
|
@ -1051,7 +1020,7 @@ public class TokenService {
|
|||
public Response installedAppUrnCallback(final @QueryParam("code") String code, final @QueryParam("error") String error, final @QueryParam("error_description") String errorDescription) {
|
||||
LoginFormsProvider forms = Flows.forms(providerSession, realm, uriInfo);
|
||||
if (code != null) {
|
||||
return forms.setAccessCode(null, code).createCode();
|
||||
return forms.setAccessCode(code).createCode();
|
||||
} else {
|
||||
return forms.setError(error).createCode();
|
||||
}
|
||||
|
|
|
@ -825,11 +825,12 @@ public class UsersResource {
|
|||
|
||||
AccessCodeEntry accessCode = tokenManager.createAccessCode(scope, state, redirect, realm, client, user, null);
|
||||
accessCode.setRequiredActions(requiredActions);
|
||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
|
||||
accessCode.setUsernameUsed(username);
|
||||
accessCode.resetExpiration();
|
||||
|
||||
try {
|
||||
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
|
||||
builder.queryParam("key", accessCode.getId());
|
||||
builder.queryParam("key", accessCode.getCode());
|
||||
|
||||
String link = builder.build(realm.getName()).toString();
|
||||
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
package org.keycloak.services.resources.flows;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.audit.Audit;
|
||||
|
@ -32,21 +33,29 @@ import org.keycloak.models.ClientModel;
|
|||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserModel.RequiredAction;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.provider.ProviderSession;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.managers.AccessCodeEntry;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.TokenManager;
|
||||
import org.keycloak.util.MultivaluedHashMap;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import javax.ws.rs.core.Cookie;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -113,36 +122,57 @@ public class OAuthFlows {
|
|||
|
||||
boolean isResource = client instanceof ApplicationModel;
|
||||
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user, session);
|
||||
accessCode.setUsername(username);
|
||||
accessCode.setRememberMe(rememberMe);
|
||||
accessCode.setAuthMethod(authMethod);
|
||||
accessCode.setUsernameUsed(username);
|
||||
|
||||
log.debugv("processAccessCode: isResource: {0}", isResource);
|
||||
log.debugv("processAccessCode: go to oauth page?: {0}",
|
||||
(!isResource && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested()
|
||||
.size() > 0)));
|
||||
!isResource);
|
||||
|
||||
audit.detail(Details.CODE_ID, accessCode.getId());
|
||||
audit.detail(Details.CODE_ID, accessCode.getCodeId());
|
||||
|
||||
Set<RequiredAction> requiredActions = user.getRequiredActions();
|
||||
if (!requiredActions.isEmpty()) {
|
||||
accessCode.setRequiredActions(new HashSet<UserModel.RequiredAction>(requiredActions));
|
||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
|
||||
accessCode.resetExpiration();
|
||||
|
||||
RequiredAction action = user.getRequiredActions().iterator().next();
|
||||
if (action.equals(RequiredAction.VERIFY_EMAIL)) {
|
||||
audit.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
||||
}
|
||||
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).setUser(user)
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getCode()).setUser(user)
|
||||
.createResponse(action);
|
||||
}
|
||||
|
||||
if (!isResource
|
||||
&& (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)) {
|
||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).
|
||||
setAccessRequest(accessCode.getRealmRolesRequested(), accessCode.getResourceRolesRequested()).
|
||||
if (!isResource) {
|
||||
accessCode.resetExpiration();
|
||||
List<RoleModel> realmRolesRequested = new LinkedList<RoleModel>();
|
||||
MultivaluedMap<String, RoleModel> appRolesRequested = new MultivaluedMapImpl<String, RoleModel>();
|
||||
if (accessCode.getToken().getRealmAccess() != null) {
|
||||
if (accessCode.getToken().getRealmAccess().getRoles() != null) {
|
||||
for (String role : accessCode.getToken().getRealmAccess().getRoles()) {
|
||||
RoleModel roleModel = realm.getRole(role);
|
||||
if (roleModel != null) realmRolesRequested.add(roleModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (accessCode.getToken().getResourceAccess().size() > 0) {
|
||||
for (Map.Entry<String, AccessToken.Access> entry : accessCode.getToken().getResourceAccess().entrySet()) {
|
||||
ApplicationModel app = realm.getApplicationByName(entry.getKey());
|
||||
if (app == null) continue;
|
||||
if (entry.getValue().getRoles() != null) {
|
||||
for (String role : entry.getValue().getRoles()) {
|
||||
RoleModel roleModel = app.getRole(role);
|
||||
if (roleModel != null) appRolesRequested.add(entry.getKey(), roleModel);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getCode()).
|
||||
setAccessRequest(realmRolesRequested, appRolesRequested).
|
||||
setClient(client).createOAuthGrant();
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ public class RequiredActionEmailVerificationTest {
|
|||
|
||||
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
|
||||
|
||||
Assert.assertEquals(mailCodeId, verificationUrl.split("key=")[1]);
|
||||
//Assert.assertEquals(mailCodeId, verificationUrl.split("key=")[1]);
|
||||
|
||||
driver.navigate().to(verificationUrl.trim());
|
||||
|
||||
|
|
|
@ -29,7 +29,9 @@ import org.keycloak.OAuth2Constants;
|
|||
import org.keycloak.audit.Details;
|
||||
import org.keycloak.audit.Errors;
|
||||
import org.keycloak.audit.Event;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
|
||||
|
@ -70,6 +72,13 @@ public class AccessTokenTest {
|
|||
|
||||
@Test
|
||||
public void accessTokenRequest() throws Exception {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setAccessCodeLifespan(1);
|
||||
}
|
||||
});
|
||||
|
||||
oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
Event loginEvent = events.expectLogin().assertEvent();
|
||||
|
@ -104,10 +113,21 @@ public class AccessTokenTest {
|
|||
Assert.assertEquals(oauth.verifyRefreshToken(response.getRefreshToken()).getId(), event.getDetails().get(Details.REFRESH_TOKEN_ID));
|
||||
Assert.assertEquals(sessionId, token.getSessionState());
|
||||
|
||||
Thread.sleep(2000);
|
||||
response = oauth.doAccessTokenRequest(code, "password");
|
||||
Assert.assertEquals(400, response.getStatusCode());
|
||||
|
||||
events.expectCodeToToken(codeId, null).error("invalid_code").removeDetail(Details.TOKEN_ID).removeDetail(Details.REFRESH_TOKEN_ID).client((String) null).user((String) null).assertEvent();
|
||||
AssertEvents.ExpectedEvent expectedEvent = events.expectCodeToToken(codeId, null);
|
||||
expectedEvent.error("invalid_code").removeDetail(Details.TOKEN_ID).removeDetail(Details.REFRESH_TOKEN_ID).client((String) null).user((String) null);
|
||||
expectedEvent.assertEvent();
|
||||
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setAccessCodeLifespan(60);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.keycloak.audit.Details;
|
|||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.AccessCode;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
|
@ -80,7 +81,8 @@ public class AuthorizationCodeTest {
|
|||
oauth.verifyCode(response.getCode());
|
||||
|
||||
String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID);
|
||||
Assert.assertEquals(codeId, new JWSInput(response.getCode()).readContentAsString());
|
||||
AccessCode accessCode = new JWSInput(response.getCode()).readJsonContent(AccessCode.class);
|
||||
Assert.assertEquals(codeId,accessCode.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -102,7 +104,8 @@ public class AuthorizationCodeTest {
|
|||
oauth.verifyCode(code);
|
||||
|
||||
String codeId = events.expectLogin().detail(Details.REDIRECT_URI, Constants.INSTALLED_APP_URN).assertEvent().getDetails().get(Details.CODE_ID);
|
||||
Assert.assertEquals(codeId, new JWSInput(code).readContentAsString());
|
||||
AccessCode accessCode = new JWSInput(code).readJsonContent(AccessCode.class);
|
||||
Assert.assertEquals(codeId,accessCode.getId());
|
||||
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
|
@ -160,7 +163,8 @@ public class AuthorizationCodeTest {
|
|||
oauth.verifyCode(response.getCode());
|
||||
|
||||
String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID);
|
||||
Assert.assertEquals(codeId, new JWSInput(response.getCode()).readContentAsString());
|
||||
AccessCode accessCode = new JWSInput(response.getCode()).readJsonContent(AccessCode.class);
|
||||
Assert.assertEquals(codeId,accessCode.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -175,7 +179,8 @@ public class AuthorizationCodeTest {
|
|||
oauth.verifyCode(response.getCode());
|
||||
|
||||
String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID);
|
||||
Assert.assertEquals(codeId, new JWSInput(response.getCode()).readContentAsString());
|
||||
AccessCode accessCode = new JWSInput(response.getCode()).readJsonContent(AccessCode.class);
|
||||
Assert.assertEquals(codeId,accessCode.getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue