Merge pull request #477 from patriot1burke/master
stateless access codes
This commit is contained in:
commit
15bc33f8e6
14 changed files with 415 additions and 246 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 Response createCode();
|
||||||
|
|
||||||
public LoginFormsProvider setAccessCode(String accessCodeId, String accessCode);
|
public LoginFormsProvider setAccessCode(String accessCode);
|
||||||
|
|
||||||
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String,RoleModel> resourceRolesRequested);
|
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 static final Logger logger = Logger.getLogger(FreeMarkerLoginFormsProvider.class);
|
||||||
|
|
||||||
private String message;
|
private String message;
|
||||||
private String accessCodeId;
|
|
||||||
private String accessCode;
|
private String accessCode;
|
||||||
private Response.Status status = Response.Status.OK;
|
private Response.Status status = Response.Status.OK;
|
||||||
private List<RoleModel> realmRolesRequested;
|
private List<RoleModel> realmRolesRequested;
|
||||||
|
@ -108,7 +107,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
case VERIFY_EMAIL:
|
case VERIFY_EMAIL:
|
||||||
try {
|
try {
|
||||||
UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri());
|
UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri());
|
||||||
builder.queryParam("key", accessCodeId);
|
builder.queryParam("key", accessCode);
|
||||||
|
|
||||||
String link = builder.build(realm.getName()).toString();
|
String link = builder.build(realm.getName()).toString();
|
||||||
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
||||||
|
@ -284,8 +283,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LoginFormsProvider setAccessCode(String accessCodeId, String accessCode) {
|
public LoginFormsProvider setAccessCode(String accessCode) {
|
||||||
this.accessCodeId = accessCodeId;
|
|
||||||
this.accessCode = accessCode;
|
this.accessCode = accessCode;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,6 @@ public class CachedRealm {
|
||||||
private Set<String> auditListeners = new HashSet<String>();
|
private Set<String> auditListeners = new HashSet<String>();
|
||||||
private List<String> defaultRoles = new LinkedList<String>();
|
private List<String> defaultRoles = new LinkedList<String>();
|
||||||
private Map<String, String> realmRoles = new HashMap<String, String>();
|
private Map<String, String> realmRoles = new HashMap<String, String>();
|
||||||
private Set<String> rolesById = new HashSet<String>();
|
|
||||||
private Map<String, String> applications = new HashMap<String, String>();
|
private Map<String, String> applications = new HashMap<String, String>();
|
||||||
private Map<String, String> clients = new HashMap<String, String>();
|
private Map<String, String> clients = new HashMap<String, String>();
|
||||||
|
|
||||||
|
@ -134,7 +133,6 @@ public class CachedRealm {
|
||||||
|
|
||||||
for (RoleModel role : model.getRoles()) {
|
for (RoleModel role : model.getRoles()) {
|
||||||
realmRoles.put(role.getName(), role.getId());
|
realmRoles.put(role.getName(), role.getId());
|
||||||
rolesById.add(role.getId());
|
|
||||||
CachedRole cachedRole = new CachedRealmRole(role);
|
CachedRole cachedRole = new CachedRealmRole(role);
|
||||||
cache.addCachedRole(cachedRole);
|
cache.addCachedRole(cachedRole);
|
||||||
}
|
}
|
||||||
|
@ -143,9 +141,6 @@ public class CachedRealm {
|
||||||
applications.put(app.getName(), app.getId());
|
applications.put(app.getName(), app.getId());
|
||||||
CachedApplication cachedApp = new CachedApplication(cache, delegate, model, app);
|
CachedApplication cachedApp = new CachedApplication(cache, delegate, model, app);
|
||||||
cache.addCachedApplication(cachedApp);
|
cache.addCachedApplication(cachedApp);
|
||||||
for (String roleId : cachedApp.getRoles().values()) {
|
|
||||||
rolesById.add(roleId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (OAuthClientModel client : model.getOAuthClients()) {
|
for (OAuthClientModel client : model.getOAuthClients()) {
|
||||||
|
@ -177,10 +172,6 @@ public class CachedRealm {
|
||||||
return realmRoles;
|
return realmRoles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<String> getRolesById() {
|
|
||||||
return rolesById;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getApplications() {
|
public Map<String, String> getApplications() {
|
||||||
return applications;
|
return applications;
|
||||||
}
|
}
|
||||||
|
|
103
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
vendored
Executable file
103
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
vendored
Executable file
|
@ -0,0 +1,103 @@
|
||||||
|
package org.keycloak.models.cache.entities;
|
||||||
|
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class CachedUser {
|
||||||
|
private String id;
|
||||||
|
private String loginName;
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
private String email;
|
||||||
|
private boolean emailVerified;
|
||||||
|
private int notBefore;
|
||||||
|
private List<UserCredentialValueModel> credentials = new LinkedList<UserCredentialValueModel>();
|
||||||
|
private boolean enabled;
|
||||||
|
private boolean totp;
|
||||||
|
private Map<String, String> attributes = new HashMap<String, String>();
|
||||||
|
private Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>();
|
||||||
|
private Set<String> roleMappings = new HashSet<String>();
|
||||||
|
|
||||||
|
|
||||||
|
public CachedUser(UserModel user) {
|
||||||
|
this.id = user.getId();
|
||||||
|
this.loginName = user.getLoginName();
|
||||||
|
this.firstName = user.getFirstName();
|
||||||
|
this.lastName = user.getLastName();
|
||||||
|
this.attributes.putAll(user.getAttributes());
|
||||||
|
this.email = user.getEmail();
|
||||||
|
this.emailVerified = user.isEmailVerified();
|
||||||
|
this.notBefore = user.getNotBefore();
|
||||||
|
this.credentials.addAll(user.getCredentialsDirectly());
|
||||||
|
this.enabled = user.isEnabled();
|
||||||
|
this.totp = user.isTotp();
|
||||||
|
this.requiredActions.addAll(user.getRequiredActions());
|
||||||
|
for (RoleModel role : user.getRoleMappings()) {
|
||||||
|
roleMappings.add(role.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLoginName() {
|
||||||
|
return loginName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmailVerified() {
|
||||||
|
return emailVerified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNotBefore() {
|
||||||
|
return notBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<UserCredentialValueModel> getCredentials() {
|
||||||
|
return credentials;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTotp() {
|
||||||
|
return totp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getAttributes() {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<UserModel.RequiredAction> getRequiredActions() {
|
||||||
|
return requiredActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getRoleMappings() {
|
||||||
|
return roleMappings;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,17 +1,20 @@
|
||||||
package org.keycloak.services.managers;
|
package org.keycloak.services.managers;
|
||||||
|
|
||||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||||
|
import org.keycloak.jose.jws.JWSBuilder;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
import org.keycloak.models.UserModel.RequiredAction;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.representations.AccessCode;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.util.Time;
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -21,141 +24,101 @@ import java.util.UUID;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class AccessCodeEntry {
|
public class AccessCodeEntry {
|
||||||
protected String id = UUID.randomUUID().toString() + System.currentTimeMillis();
|
protected AccessCode accessCode;
|
||||||
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 RealmModel realm;
|
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() {
|
public AccessCodeEntry(RealmModel realm, AccessCode accessCode) {
|
||||||
return expiration != 0 && Time.currentTime() > expiration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RealmModel getRealm() {
|
|
||||||
return realm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRealm(RealmModel realm) {
|
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
|
this.accessCode = accessCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCode() {
|
public String getCodeId() {
|
||||||
return code;
|
return this.accessCode.getId();
|
||||||
}
|
|
||||||
|
|
||||||
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 UserModel getUser() {
|
public UserModel getUser() {
|
||||||
return user;
|
return realm.getUserById(accessCode.getAccessToken().getSubject());
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSessionState() {
|
public String getSessionState() {
|
||||||
return sessionState;
|
return accessCode.getAccessToken().getSessionState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSessionState(String sessionState) {
|
public boolean isExpired() {
|
||||||
this.sessionState = sessionState;
|
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() {
|
public String getRedirectUri() {
|
||||||
return redirectUri;
|
return accessCode.getRedirectUri();
|
||||||
}
|
|
||||||
|
|
||||||
public void setRedirectUri(String redirectUri) {
|
|
||||||
this.redirectUri = redirectUri;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRememberMe() {
|
public boolean isRememberMe() {
|
||||||
return rememberMe;
|
return accessCode.isRememberMe();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRememberMe(boolean rememberMe) {
|
public void setRememberMe(boolean remember) {
|
||||||
this.rememberMe = rememberMe;
|
accessCode.setRememberMe(remember);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAuthMethod() {
|
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) {
|
public void setAuthMethod(String authMethod) {
|
||||||
this.authMethod = authMethod;
|
accessCode.setAuthMethod(authMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public Set<RequiredAction> getRequiredActions() {
|
||||||
return username;
|
Set<RequiredAction> set = new HashSet<RequiredAction>();
|
||||||
|
for (String action : accessCode.getRequiredActions()) {
|
||||||
|
set.add(RequiredAction.valueOf(action));
|
||||||
|
|
||||||
|
}
|
||||||
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUsername(String username) {
|
public boolean hasRequiredAction(RequiredAction action) {
|
||||||
this.username = username;
|
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.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.representations.AccessCode;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.AccessTokenResponse;
|
import org.keycloak.representations.AccessTokenResponse;
|
||||||
import org.keycloak.representations.IDToken;
|
import org.keycloak.representations.IDToken;
|
||||||
|
@ -31,6 +32,7 @@ import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,6 +44,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
public class TokenManager {
|
public class TokenManager {
|
||||||
protected static final Logger logger = Logger.getLogger(TokenManager.class);
|
protected static final Logger logger = Logger.getLogger(TokenManager.class);
|
||||||
|
|
||||||
|
/*
|
||||||
protected Map<String, AccessCodeEntry> accessCodeMap = new ConcurrentHashMap<String, AccessCodeEntry>();
|
protected Map<String, AccessCodeEntry> accessCodeMap = new ConcurrentHashMap<String, AccessCodeEntry>();
|
||||||
|
|
||||||
public void clearAccessCodes() {
|
public void clearAccessCodes() {
|
||||||
|
@ -55,6 +58,23 @@ public class TokenManager {
|
||||||
public AccessCodeEntry pullAccessCode(String key) {
|
public AccessCodeEntry pullAccessCode(String key) {
|
||||||
return accessCodeMap.remove(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) {
|
public static void applyScope(RoleModel role, RoleModel scope, Set<RoleModel> visited, Set<RoleModel> requested) {
|
||||||
if (visited.contains(scope)) return;
|
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) {
|
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);
|
return createAccessCodeEntry(scopeParam, state, redirect, realm, client, user, session);
|
||||||
accessCodeMap.put(code.getId(), code);
|
|
||||||
return code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AccessCodeEntry createAccessCodeEntry(String scopeParam, String state, String redirect, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
|
private AccessCodeEntry createAccessCodeEntry(String scopeParam, String state, String redirect, RealmModel realm, ClientModel client, UserModel user, UserSessionModel session) {
|
||||||
AccessCodeEntry code = new AccessCodeEntry();
|
List<RoleModel> realmRolesRequested = new LinkedList<RoleModel>();
|
||||||
if (session != null) {
|
MultivaluedMap<String, RoleModel> resourceRolesRequested = new MultivaluedMapImpl<String, RoleModel>();
|
||||||
code.setSessionState(session.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
List<RoleModel> realmRolesRequested = code.getRealmRolesRequested();
|
|
||||||
MultivaluedMap<String, RoleModel> resourceRolesRequested = code.getResourceRolesRequested();
|
|
||||||
|
|
||||||
AccessToken token = createClientAccessToken(scopeParam, realm, client, user, session, realmRolesRequested, resourceRolesRequested);
|
AccessToken token = createClientAccessToken(scopeParam, realm, client, user, session, realmRolesRequested, resourceRolesRequested);
|
||||||
token.setSessionState(code.getSessionState());
|
if (session != null) token.setSessionState(session.getId());
|
||||||
|
AccessCode code = new AccessCode();
|
||||||
code.setToken(token);
|
code.setId(UUID.randomUUID().toString() + System.currentTimeMillis());
|
||||||
code.setRealm(realm);
|
code.setAccessToken(token);
|
||||||
|
code.setTimestamp(Time.currentTime());
|
||||||
code.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
|
code.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
|
||||||
code.setClient(client);
|
|
||||||
code.setUser(user);
|
|
||||||
code.setState(state);
|
code.setState(state);
|
||||||
code.setRedirectUri(redirect);
|
code.setRedirectUri(redirect);
|
||||||
String accessCode = null;
|
AccessCodeEntry entry = new AccessCodeEntry(realm, code);
|
||||||
try {
|
return entry;
|
||||||
accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey());
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
code.setCode(accessCode);
|
|
||||||
return code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccessToken refreshAccessToken(UriInfo uriInfo, RealmModel realm, ClientModel client, String encodedRefreshToken, Audit audit) throws OAuthErrorException {
|
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.EmailException;
|
||||||
import org.keycloak.email.EmailProvider;
|
import org.keycloak.email.EmailProvider;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
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.ClientModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
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.services.validation.Validation;
|
||||||
import org.keycloak.authentication.AuthenticationProviderException;
|
import org.keycloak.authentication.AuthenticationProviderException;
|
||||||
import org.keycloak.authentication.AuthenticationProviderManager;
|
import org.keycloak.authentication.AuthenticationProviderManager;
|
||||||
import org.keycloak.util.Time;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
|
@ -134,7 +131,7 @@ public class RequiredActionsService {
|
||||||
user.setEmail(email);
|
user.setEmail(email);
|
||||||
|
|
||||||
user.removeRequiredAction(RequiredAction.UPDATE_PROFILE);
|
user.removeRequiredAction(RequiredAction.UPDATE_PROFILE);
|
||||||
accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PROFILE);
|
accessCode.removeRequiredAction(RequiredAction.UPDATE_PROFILE);
|
||||||
|
|
||||||
audit.clone().event(EventType.UPDATE_PROFILE).success();
|
audit.clone().event(EventType.UPDATE_PROFILE).success();
|
||||||
if (emailChanged) {
|
if (emailChanged) {
|
||||||
|
@ -176,7 +173,7 @@ public class RequiredActionsService {
|
||||||
user.setTotp(true);
|
user.setTotp(true);
|
||||||
|
|
||||||
user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
||||||
accessCode.getRequiredActions().remove(RequiredAction.CONFIGURE_TOTP);
|
accessCode.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
||||||
|
|
||||||
audit.clone().event(EventType.UPDATE_TOTP).success();
|
audit.clone().event(EventType.UPDATE_TOTP).success();
|
||||||
|
|
||||||
|
@ -222,7 +219,7 @@ public class RequiredActionsService {
|
||||||
|
|
||||||
user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
|
user.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
|
||||||
if (accessCode != null) {
|
if (accessCode != null) {
|
||||||
accessCode.getRequiredActions().remove(RequiredAction.UPDATE_PASSWORD);
|
accessCode.removeRequiredAction(RequiredAction.UPDATE_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
audit.clone().event(EventType.UPDATE_PASSWORD).success();
|
audit.clone().event(EventType.UPDATE_PASSWORD).success();
|
||||||
|
@ -235,9 +232,9 @@ public class RequiredActionsService {
|
||||||
@GET
|
@GET
|
||||||
public Response emailVerification() {
|
public Response emailVerification() {
|
||||||
if (uriInfo.getQueryParameters().containsKey("key")) {
|
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()
|
if (accessCode == null || accessCode.isExpired()
|
||||||
|| !accessCode.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL)) {
|
|| !accessCode.hasRequiredAction(RequiredAction.VERIFY_EMAIL)) {
|
||||||
return unauthorized();
|
return unauthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,7 +245,7 @@ public class RequiredActionsService {
|
||||||
user.setEmailVerified(true);
|
user.setEmailVerified(true);
|
||||||
|
|
||||||
user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
|
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();
|
audit.clone().event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
||||||
|
|
||||||
|
@ -262,7 +259,7 @@ public class RequiredActionsService {
|
||||||
initAudit(accessCode);
|
initAudit(accessCode);
|
||||||
//audit.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
//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);
|
.createResponse(RequiredAction.VERIFY_EMAIL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,14 +268,14 @@ public class RequiredActionsService {
|
||||||
@GET
|
@GET
|
||||||
public Response passwordReset() {
|
public Response passwordReset() {
|
||||||
if (uriInfo.getQueryParameters().containsKey("key")) {
|
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");
|
accessCode.setAuthMethod("form");
|
||||||
if (accessCode == null || accessCode.isExpired()
|
if (accessCode == null || accessCode.isExpired()
|
||||||
|| !accessCode.getRequiredActions().contains(RequiredAction.UPDATE_PASSWORD)) {
|
|| !accessCode.hasRequiredAction(RequiredAction.UPDATE_PASSWORD)) {
|
||||||
return unauthorized();
|
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 {
|
} else {
|
||||||
return Flows.forms(providerSession, realm, uriInfo).createPasswordReset();
|
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);
|
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user, session);
|
||||||
accessCode.setRequiredActions(requiredActions);
|
accessCode.setRequiredActions(requiredActions);
|
||||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
|
|
||||||
accessCode.setAuthMethod("form");
|
accessCode.setAuthMethod("form");
|
||||||
accessCode.setUsername(username);
|
accessCode.setUsernameUsed(username);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
|
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
|
||||||
builder.queryParam("key", accessCode.getId());
|
builder.queryParam("key", accessCode.getCode());
|
||||||
|
|
||||||
String link = builder.build(realm.getName()).toString();
|
String link = builder.build(realm.getName()).toString();
|
||||||
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
||||||
|
|
||||||
providerSession.getProvider(EmailProvider.class).setRealm(realm).setUser(user).sendPasswordReset(link, expiration);
|
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) {
|
} catch (EmailException e) {
|
||||||
logger.error("Failed to send password reset email", e);
|
logger.error("Failed to send password reset email", e);
|
||||||
return Flows.forms(providerSession, realm, uriInfo).setError("emailSendError").createErrorPage();
|
return Flows.forms(providerSession, realm, uriInfo).setError("emailSendError").createErrorPage();
|
||||||
|
@ -360,31 +356,15 @@ public class RequiredActionsService {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
JWSInput input = new JWSInput(code);
|
AccessCodeEntry accessCodeEntry = tokenManager.parseCode(code, realm);
|
||||||
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);
|
|
||||||
if (accessCodeEntry == null) {
|
if (accessCodeEntry == null) {
|
||||||
logger.debug("getAccessCodeEntry access code entry null");
|
logger.debug("getAccessCodeEntry access code entry null");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessCodeEntry.isExpired()) {
|
if (accessCodeEntry.isExpired()) {
|
||||||
logger.debugv("getAccessCodeEntry: access code id: {0}", accessCodeEntry.getId());
|
logger.debugv("getAccessCodeEntry: access code id: {0}", accessCodeEntry.getCodeId());
|
||||||
logger.debugv("getAccessCodeEntry access code entry expired: {0}", accessCodeEntry.getExpiration());
|
logger.debugv("getAccessCodeEntry access code entry expired");
|
||||||
logger.debugv("getAccessCodeEntry current time: {0}", Time.currentTime());
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,11 +387,11 @@ public class RequiredActionsService {
|
||||||
|
|
||||||
Set<RequiredAction> requiredActions = user.getRequiredActions();
|
Set<RequiredAction> requiredActions = user.getRequiredActions();
|
||||||
if (!requiredActions.isEmpty()) {
|
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());
|
.createResponse(requiredActions.iterator().next());
|
||||||
} else {
|
} else {
|
||||||
logger.debugv("redirectOauth: redirecting to: {0}", accessCode.getRedirectUri());
|
logger.debugv("redirectOauth: redirecting to: {0}", accessCode.getRedirectUri());
|
||||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
|
accessCode.resetExpiration();
|
||||||
|
|
||||||
AuthenticationManager authManager = new AuthenticationManager(providerSession);
|
AuthenticationManager authManager = new AuthenticationManager(providerSession);
|
||||||
|
|
||||||
|
@ -433,11 +413,11 @@ public class RequiredActionsService {
|
||||||
audit.event(EventType.LOGIN).client(accessCode.getClient())
|
audit.event(EventType.LOGIN).client(accessCode.getClient())
|
||||||
.user(accessCode.getUser())
|
.user(accessCode.getUser())
|
||||||
.session(accessCode.getSessionState())
|
.session(accessCode.getSessionState())
|
||||||
.detail(Details.CODE_ID, accessCode.getId())
|
.detail(Details.CODE_ID, accessCode.getCodeId())
|
||||||
.detail(Details.REDIRECT_URI, accessCode.getRedirectUri())
|
.detail(Details.REDIRECT_URI, accessCode.getRedirectUri())
|
||||||
.detail(Details.RESPONSE_TYPE, "code")
|
.detail(Details.RESPONSE_TYPE, "code")
|
||||||
.detail(Details.AUTH_METHOD, accessCode.getAuthMethod())
|
.detail(Details.AUTH_METHOD, accessCode.getAuthMethod())
|
||||||
.detail(Details.USERNAME, accessCode.getUsername());
|
.detail(Details.USERNAME, accessCode.getUsernameUsed());
|
||||||
|
|
||||||
if (accessCode.isRememberMe()) {
|
if (accessCode.isRememberMe()) {
|
||||||
audit.detail(Details.REMEMBER_ME, "true");
|
audit.detail(Details.REMEMBER_ME, "true");
|
||||||
|
|
|
@ -16,8 +16,6 @@ import org.keycloak.audit.Errors;
|
||||||
import org.keycloak.audit.EventType;
|
import org.keycloak.audit.EventType;
|
||||||
import org.keycloak.authentication.AuthenticationProviderException;
|
import org.keycloak.authentication.AuthenticationProviderException;
|
||||||
import org.keycloak.authentication.AuthenticationProviderManager;
|
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.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.ClientModel;
|
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.resources.flows.Urls;
|
||||||
import org.keycloak.services.validation.Validation;
|
import org.keycloak.services.validation.Validation;
|
||||||
import org.keycloak.util.BasicAuthHelper;
|
import org.keycloak.util.BasicAuthHelper;
|
||||||
import org.keycloak.util.Time;
|
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
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());
|
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) {
|
if (accessCode == null) {
|
||||||
Map<String, String> res = new HashMap<String, String>();
|
Map<String, String> res = new HashMap<String, String>();
|
||||||
res.put(OAuth2Constants.ERROR, "invalid_grant");
|
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)
|
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
audit.detail(Details.CODE_ID, accessCode.getCodeId());
|
||||||
audit.user(accessCode.getUser());
|
|
||||||
audit.session(accessCode.getSessionState());
|
|
||||||
|
|
||||||
ClientModel client = authorizeClient(authorizationHeader, formData, audit);
|
|
||||||
|
|
||||||
if (accessCode.isExpired()) {
|
if (accessCode.isExpired()) {
|
||||||
Map<String, String> res = new HashMap<String, String>();
|
Map<String, String> res = new HashMap<String, String>();
|
||||||
res.put(OAuth2Constants.ERROR, "invalid_grant");
|
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)
|
return Response.status(Response.Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).entity(res)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audit.user(accessCode.getUser());
|
||||||
|
audit.session(accessCode.getSessionState());
|
||||||
|
|
||||||
|
ClientModel client = authorizeClient(authorizationHeader, formData, audit);
|
||||||
|
|
||||||
if (!client.getClientId().equals(accessCode.getClient().getClientId())) {
|
if (!client.getClientId().equals(accessCode.getClient().getClientId())) {
|
||||||
Map<String, String> res = new HashMap<String, String>();
|
Map<String, String> res = new HashMap<String, String>();
|
||||||
res.put(OAuth2Constants.ERROR, "invalid_grant");
|
res.put(OAuth2Constants.ERROR, "invalid_grant");
|
||||||
|
@ -993,25 +974,13 @@ public class TokenService {
|
||||||
}
|
}
|
||||||
|
|
||||||
String code = formData.getFirst(OAuth2Constants.CODE);
|
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) {
|
if (accessCodeEntry == null) {
|
||||||
audit.error(Errors.INVALID_CODE);
|
audit.error(Errors.INVALID_CODE);
|
||||||
return oauth.forwardToSecurityFailure("Unknown access code.");
|
return oauth.forwardToSecurityFailure("Unknown access code.");
|
||||||
}
|
}
|
||||||
|
audit.detail(Details.CODE_ID, accessCodeEntry.getCodeId());
|
||||||
|
|
||||||
String redirect = accessCodeEntry.getRedirectUri();
|
String redirect = accessCodeEntry.getRedirectUri();
|
||||||
String state = accessCodeEntry.getState();
|
String state = accessCodeEntry.getState();
|
||||||
|
@ -1021,7 +990,7 @@ public class TokenService {
|
||||||
.detail(Details.RESPONSE_TYPE, "code")
|
.detail(Details.RESPONSE_TYPE, "code")
|
||||||
.detail(Details.AUTH_METHOD, accessCodeEntry.getAuthMethod())
|
.detail(Details.AUTH_METHOD, accessCodeEntry.getAuthMethod())
|
||||||
.detail(Details.REDIRECT_URI, redirect)
|
.detail(Details.REDIRECT_URI, redirect)
|
||||||
.detail(Details.USERNAME, accessCodeEntry.getUsername());
|
.detail(Details.USERNAME, accessCodeEntry.getUsernameUsed());
|
||||||
|
|
||||||
if (accessCodeEntry.isRememberMe()) {
|
if (accessCodeEntry.isRememberMe()) {
|
||||||
audit.detail(Details.REMEMBER_ME, "true");
|
audit.detail(Details.REMEMBER_ME, "true");
|
||||||
|
@ -1042,7 +1011,7 @@ public class TokenService {
|
||||||
|
|
||||||
audit.success();
|
audit.success();
|
||||||
|
|
||||||
accessCodeEntry.setExpiration(Time.currentTime() + realm.getAccessCodeLifespan());
|
accessCodeEntry.resetExpiration();
|
||||||
return oauth.redirectAccessCode(accessCodeEntry, session, state, redirect);
|
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) {
|
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);
|
LoginFormsProvider forms = Flows.forms(providerSession, realm, uriInfo);
|
||||||
if (code != null) {
|
if (code != null) {
|
||||||
return forms.setAccessCode(null, code).createCode();
|
return forms.setAccessCode(code).createCode();
|
||||||
} else {
|
} else {
|
||||||
return forms.setError(error).createCode();
|
return forms.setError(error).createCode();
|
||||||
}
|
}
|
||||||
|
|
|
@ -825,11 +825,12 @@ public class UsersResource {
|
||||||
|
|
||||||
AccessCodeEntry accessCode = tokenManager.createAccessCode(scope, state, redirect, realm, client, user, null);
|
AccessCodeEntry accessCode = tokenManager.createAccessCode(scope, state, redirect, realm, client, user, null);
|
||||||
accessCode.setRequiredActions(requiredActions);
|
accessCode.setRequiredActions(requiredActions);
|
||||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
|
accessCode.setUsernameUsed(username);
|
||||||
|
accessCode.resetExpiration();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
|
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
|
||||||
builder.queryParam("key", accessCode.getId());
|
builder.queryParam("key", accessCode.getCode());
|
||||||
|
|
||||||
String link = builder.build(realm.getName()).toString();
|
String link = builder.build(realm.getName()).toString();
|
||||||
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
package org.keycloak.services.resources.flows;
|
package org.keycloak.services.resources.flows;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.audit.Audit;
|
import org.keycloak.audit.Audit;
|
||||||
|
@ -32,21 +33,29 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredCredentialModel;
|
import org.keycloak.models.RequiredCredentialModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
import org.keycloak.models.UserModel.RequiredAction;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.provider.ProviderSession;
|
import org.keycloak.provider.ProviderSession;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.services.managers.AccessCodeEntry;
|
import org.keycloak.services.managers.AccessCodeEntry;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
import org.keycloak.services.managers.TokenManager;
|
import org.keycloak.services.managers.TokenManager;
|
||||||
|
import org.keycloak.util.MultivaluedHashMap;
|
||||||
import org.keycloak.util.Time;
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.core.Cookie;
|
import javax.ws.rs.core.Cookie;
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -113,36 +122,57 @@ public class OAuthFlows {
|
||||||
|
|
||||||
boolean isResource = client instanceof ApplicationModel;
|
boolean isResource = client instanceof ApplicationModel;
|
||||||
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user, session);
|
AccessCodeEntry accessCode = tokenManager.createAccessCode(scopeParam, state, redirect, realm, client, user, session);
|
||||||
accessCode.setUsername(username);
|
|
||||||
accessCode.setRememberMe(rememberMe);
|
accessCode.setRememberMe(rememberMe);
|
||||||
accessCode.setAuthMethod(authMethod);
|
accessCode.setAuthMethod(authMethod);
|
||||||
|
accessCode.setUsernameUsed(username);
|
||||||
|
|
||||||
log.debugv("processAccessCode: isResource: {0}", isResource);
|
log.debugv("processAccessCode: isResource: {0}", isResource);
|
||||||
log.debugv("processAccessCode: go to oauth page?: {0}",
|
log.debugv("processAccessCode: go to oauth page?: {0}",
|
||||||
(!isResource && (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested()
|
!isResource);
|
||||||
.size() > 0)));
|
|
||||||
|
|
||||||
audit.detail(Details.CODE_ID, accessCode.getId());
|
audit.detail(Details.CODE_ID, accessCode.getCodeId());
|
||||||
|
|
||||||
Set<RequiredAction> requiredActions = user.getRequiredActions();
|
Set<RequiredAction> requiredActions = user.getRequiredActions();
|
||||||
if (!requiredActions.isEmpty()) {
|
if (!requiredActions.isEmpty()) {
|
||||||
accessCode.setRequiredActions(new HashSet<UserModel.RequiredAction>(requiredActions));
|
accessCode.setRequiredActions(new HashSet<UserModel.RequiredAction>(requiredActions));
|
||||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
|
accessCode.resetExpiration();
|
||||||
|
|
||||||
RequiredAction action = user.getRequiredActions().iterator().next();
|
RequiredAction action = user.getRequiredActions().iterator().next();
|
||||||
if (action.equals(RequiredAction.VERIFY_EMAIL)) {
|
if (action.equals(RequiredAction.VERIFY_EMAIL)) {
|
||||||
audit.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, accessCode.getUser().getEmail()).success();
|
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);
|
.createResponse(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isResource
|
if (!isResource) {
|
||||||
&& (accessCode.getRealmRolesRequested().size() > 0 || accessCode.getResourceRolesRequested().size() > 0)) {
|
accessCode.resetExpiration();
|
||||||
accessCode.setExpiration(Time.currentTime() + realm.getAccessCodeLifespanUserAction());
|
List<RoleModel> realmRolesRequested = new LinkedList<RoleModel>();
|
||||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), accessCode.getCode()).
|
MultivaluedMap<String, RoleModel> appRolesRequested = new MultivaluedMapImpl<String, RoleModel>();
|
||||||
setAccessRequest(accessCode.getRealmRolesRequested(), accessCode.getResourceRolesRequested()).
|
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();
|
setClient(client).createOAuthGrant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,7 +120,7 @@ public class RequiredActionEmailVerificationTest {
|
||||||
|
|
||||||
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
|
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());
|
driver.navigate().to(verificationUrl.trim());
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,9 @@ import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.audit.Details;
|
import org.keycloak.audit.Details;
|
||||||
import org.keycloak.audit.Errors;
|
import org.keycloak.audit.Errors;
|
||||||
import org.keycloak.audit.Event;
|
import org.keycloak.audit.Event;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
|
import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.OAuthClient;
|
import org.keycloak.testsuite.OAuthClient;
|
||||||
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
|
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
|
||||||
|
@ -70,6 +72,13 @@ public class AccessTokenTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void accessTokenRequest() throws Exception {
|
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");
|
oauth.doLogin("test-user@localhost", "password");
|
||||||
|
|
||||||
Event loginEvent = events.expectLogin().assertEvent();
|
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(oauth.verifyRefreshToken(response.getRefreshToken()).getId(), event.getDetails().get(Details.REFRESH_TOKEN_ID));
|
||||||
Assert.assertEquals(sessionId, token.getSessionState());
|
Assert.assertEquals(sessionId, token.getSessionState());
|
||||||
|
|
||||||
|
Thread.sleep(2000);
|
||||||
response = oauth.doAccessTokenRequest(code, "password");
|
response = oauth.doAccessTokenRequest(code, "password");
|
||||||
Assert.assertEquals(400, response.getStatusCode());
|
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
|
@Test
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.keycloak.audit.Details;
|
||||||
import org.keycloak.jose.jws.JWSInput;
|
import org.keycloak.jose.jws.JWSInput;
|
||||||
import org.keycloak.models.Constants;
|
import org.keycloak.models.Constants;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.representations.AccessCode;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.OAuthClient;
|
import org.keycloak.testsuite.OAuthClient;
|
||||||
|
@ -80,7 +81,8 @@ public class AuthorizationCodeTest {
|
||||||
oauth.verifyCode(response.getCode());
|
oauth.verifyCode(response.getCode());
|
||||||
|
|
||||||
String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID);
|
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
|
@Test
|
||||||
|
@ -102,7 +104,8 @@ public class AuthorizationCodeTest {
|
||||||
oauth.verifyCode(code);
|
oauth.verifyCode(code);
|
||||||
|
|
||||||
String codeId = events.expectLogin().detail(Details.REDIRECT_URI, Constants.INSTALLED_APP_URN).assertEvent().getDetails().get(Details.CODE_ID);
|
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() {
|
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -160,7 +163,8 @@ public class AuthorizationCodeTest {
|
||||||
oauth.verifyCode(response.getCode());
|
oauth.verifyCode(response.getCode());
|
||||||
|
|
||||||
String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID);
|
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
|
@Test
|
||||||
|
@ -175,7 +179,8 @@ public class AuthorizationCodeTest {
|
||||||
oauth.verifyCode(response.getCode());
|
oauth.verifyCode(response.getCode());
|
||||||
|
|
||||||
String codeId = events.expectLogin().assertEvent().getDetails().get(Details.CODE_ID);
|
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