refactor token creation
This commit is contained in:
parent
9442601e42
commit
9607acdb6a
20 changed files with 232 additions and 173 deletions
|
@ -11,13 +11,13 @@
|
|||
<form class="form-horizontal" name="realmForm" novalidate>
|
||||
<fieldset class="border-top">
|
||||
<div class="form-group input-select">
|
||||
<label class="col-sm-2 control-label" for="tokenLifespan">Token lifespan</label>
|
||||
<label class="col-sm-2 control-label" for="accessTokenLifespan">Token lifespan</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<input class="form-control" type="number" required min="1"
|
||||
max="31536000" data-ng-model="realm.tokenLifespan"
|
||||
id="tokenLifespan" name="tokenLifespan"/>
|
||||
max="31536000" data-ng-model="realm.accessTokenLifespan"
|
||||
id="accessTokenLifespan" name="accessTokenLifespan"/>
|
||||
</div>
|
||||
<div class="col-sm-2 select-kc">
|
||||
<select name="tokenLifespanUnit" data-ng-model="realm.tokenLifespanUnit" >
|
||||
|
|
|
@ -19,6 +19,19 @@ public class AccessToken extends JsonWebToken {
|
|||
@JsonProperty("verify_caller")
|
||||
protected Boolean verifyCaller;
|
||||
|
||||
public Access() {
|
||||
}
|
||||
|
||||
public Access clone() {
|
||||
Access access = new Access();
|
||||
access.verifyCaller = verifyCaller;
|
||||
if (roles != null) {
|
||||
access.roles = new HashSet<String>();
|
||||
access.roles.addAll(roles);
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
public Set<String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
|
36
core/src/main/java/org/keycloak/representations/RefreshToken.java
Executable file
36
core/src/main/java/org/keycloak/representations/RefreshToken.java
Executable file
|
@ -0,0 +1,36 @@
|
|||
package org.keycloak.representations;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RefreshToken extends AccessToken {
|
||||
public RefreshToken() {
|
||||
type("REFRESH");
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep copies issuer, subject, issuedFor, realmAccess, and resourceAccess
|
||||
* from AccessToken.
|
||||
*
|
||||
* @param token
|
||||
*/
|
||||
public RefreshToken(AccessToken token) {
|
||||
this();
|
||||
this.issuer = token.issuer;
|
||||
this.subject = token.subject;
|
||||
this.issuedFor = token.issuedFor;
|
||||
if (token.realmAccess != null) {
|
||||
realmAccess = token.realmAccess.clone();
|
||||
}
|
||||
if (token.resourceAccess != null) {
|
||||
resourceAccess = new HashMap<String, Access>();
|
||||
for (Map.Entry<String, Access> entry : token.resourceAccess.entrySet()) {
|
||||
resourceAccess.put(entry.getKey(), entry.getValue().clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,9 +29,6 @@ public class PublishedRealmRepresentation {
|
|||
@JsonProperty("grants")
|
||||
protected String grantUrl;
|
||||
|
||||
@JsonProperty("identity-grants")
|
||||
protected String identityGrantUrl;
|
||||
|
||||
@JsonProperty("admin-role")
|
||||
protected String adminRole;
|
||||
|
||||
|
@ -124,12 +121,4 @@ public class PublishedRealmRepresentation {
|
|||
public void setGrantUrl(String grantUrl) {
|
||||
this.grantUrl = grantUrl;
|
||||
}
|
||||
|
||||
public String getIdentityGrantUrl() {
|
||||
return identityGrantUrl;
|
||||
}
|
||||
|
||||
public void setIdentityGrantUrl(String identityGrantUrl) {
|
||||
this.identityGrantUrl = identityGrantUrl;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,9 +38,13 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
|
|||
|
||||
void setResetPasswordAllowed(boolean resetPasswordAllowed);
|
||||
|
||||
int getTokenLifespan();
|
||||
int getAccessTokenLifespan();
|
||||
|
||||
void setTokenLifespan(int tokenLifespan);
|
||||
void setAccessTokenLifespan(int tokenLifespan);
|
||||
|
||||
int getRefreshTokenLifespan();
|
||||
|
||||
void setRefreshTokenLifespan(int tokenLifespan);
|
||||
|
||||
int getAccessCodeLifespan();
|
||||
|
||||
|
|
|
@ -128,13 +128,24 @@ public class RealmAdapter implements RealmModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getTokenLifespan() {
|
||||
return realm.getTokenLifespan();
|
||||
public int getAccessTokenLifespan() {
|
||||
return realm.getAccessTokenLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTokenLifespan(int tokenLifespan) {
|
||||
realm.setTokenLifespan(tokenLifespan);
|
||||
public void setAccessTokenLifespan(int tokenLifespan) {
|
||||
realm.setAccessTokenLifespan(tokenLifespan);
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRefreshTokenLifespan() {
|
||||
return realm.getRefreshTokenLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRefreshTokenLifespan(int tokenLifespan) {
|
||||
realm.setRefreshTokenLifespan(tokenLifespan);
|
||||
em.flush();
|
||||
}
|
||||
|
||||
|
|
|
@ -43,9 +43,10 @@ public class RealmEntity {
|
|||
protected boolean updateProfileOnInitialSocialLogin;
|
||||
protected String passwordPolicy;
|
||||
|
||||
protected int tokenLifespan;
|
||||
protected int accessTokenLifespan;
|
||||
protected int accessCodeLifespan;
|
||||
protected int accessCodeLifespanUserAction;
|
||||
protected int refreshTokenLifespan;
|
||||
|
||||
@Column(length = 2048)
|
||||
protected String publicKeyPem;
|
||||
|
@ -161,12 +162,20 @@ public class RealmEntity {
|
|||
this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
|
||||
}
|
||||
|
||||
public int getTokenLifespan() {
|
||||
return tokenLifespan;
|
||||
public int getRefreshTokenLifespan() {
|
||||
return refreshTokenLifespan;
|
||||
}
|
||||
|
||||
public void setTokenLifespan(int tokenLifespan) {
|
||||
this.tokenLifespan = tokenLifespan;
|
||||
public void setRefreshTokenLifespan(int refreshTokenLifespan) {
|
||||
this.refreshTokenLifespan = refreshTokenLifespan;
|
||||
}
|
||||
|
||||
public int getAccessTokenLifespan() {
|
||||
return accessTokenLifespan;
|
||||
}
|
||||
|
||||
public void setAccessTokenLifespan(int accessTokenLifespan) {
|
||||
this.accessTokenLifespan = accessTokenLifespan;
|
||||
}
|
||||
|
||||
public int getAccessCodeLifespan() {
|
||||
|
|
|
@ -167,13 +167,24 @@ public class RealmAdapter extends AbstractAdapter implements RealmModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getTokenLifespan() {
|
||||
return realm.getTokenLifespan();
|
||||
public int getAccessTokenLifespan() {
|
||||
return realm.getAccessTokenLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTokenLifespan(int tokenLifespan) {
|
||||
realm.setTokenLifespan(tokenLifespan);
|
||||
public void setAccessTokenLifespan(int tokenLifespan) {
|
||||
realm.setAccessTokenLifespan(tokenLifespan);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRefreshTokenLifespan() {
|
||||
return realm.getRefreshTokenLifespan();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRefreshTokenLifespan(int tokenLifespan) {
|
||||
realm.setRefreshTokenLifespan(tokenLifespan);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
|
|
|
@ -29,9 +29,10 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong
|
|||
private boolean updateProfileOnInitialSocialLogin;
|
||||
private String passwordPolicy;
|
||||
|
||||
private int tokenLifespan;
|
||||
private int accessTokenLifespan;
|
||||
private int accessCodeLifespan;
|
||||
private int accessCodeLifespanUserAction;
|
||||
private int refreshTokenLifespan;
|
||||
|
||||
private String publicKeyPem;
|
||||
private String privateKeyPem;
|
||||
|
@ -131,12 +132,21 @@ public class RealmEntity extends AbstractMongoIdentifiableEntity implements Mong
|
|||
}
|
||||
|
||||
@MongoField
|
||||
public int getTokenLifespan() {
|
||||
return tokenLifespan;
|
||||
public int getAccessTokenLifespan() {
|
||||
return accessTokenLifespan;
|
||||
}
|
||||
|
||||
public void setTokenLifespan(int tokenLifespan) {
|
||||
this.tokenLifespan = tokenLifespan;
|
||||
public void setAccessTokenLifespan(int accessTokenLifespan) {
|
||||
this.accessTokenLifespan = accessTokenLifespan;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
public int getRefreshTokenLifespan() {
|
||||
return refreshTokenLifespan;
|
||||
}
|
||||
|
||||
public void setRefreshTokenLifespan(int refreshTokenLifespan) {
|
||||
this.refreshTokenLifespan = refreshTokenLifespan;
|
||||
}
|
||||
|
||||
@MongoField
|
||||
|
|
|
@ -47,7 +47,7 @@ public class AdapterTest extends AbstractModelTest {
|
|||
realmModel.setName("JUGGLER");
|
||||
realmModel.setPrivateKeyPem("0234234");
|
||||
realmModel.setPublicKeyPem("0234234");
|
||||
realmModel.setTokenLifespan(1000);
|
||||
realmModel.setAccessTokenLifespan(1000);
|
||||
realmModel.setUpdateProfileOnInitialSocialLogin(true);
|
||||
realmModel.addDefaultRole("foo");
|
||||
|
||||
|
@ -56,7 +56,7 @@ public class AdapterTest extends AbstractModelTest {
|
|||
Assert.assertNotNull(realmModel);
|
||||
Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100);
|
||||
Assert.assertEquals(600, realmModel.getAccessCodeLifespanUserAction());
|
||||
Assert.assertEquals(realmModel.getTokenLifespan(), 1000);
|
||||
Assert.assertEquals(realmModel.getAccessTokenLifespan(), 1000);
|
||||
Assert.assertEquals(realmModel.isEnabled(), true);
|
||||
Assert.assertEquals(realmModel.getName(), "JUGGLER");
|
||||
Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234");
|
||||
|
@ -75,7 +75,7 @@ public class AdapterTest extends AbstractModelTest {
|
|||
realmModel.setName("JUGGLER");
|
||||
realmModel.setPrivateKeyPem("0234234");
|
||||
realmModel.setPublicKeyPem("0234234");
|
||||
realmModel.setTokenLifespan(1000);
|
||||
realmModel.setAccessTokenLifespan(1000);
|
||||
realmModel.setUpdateProfileOnInitialSocialLogin(true);
|
||||
realmModel.addDefaultRole("foo");
|
||||
|
||||
|
@ -84,7 +84,7 @@ public class AdapterTest extends AbstractModelTest {
|
|||
Assert.assertNotNull(realmModel);
|
||||
Assert.assertEquals(realmModel.getAccessCodeLifespan(), 100);
|
||||
Assert.assertEquals(600, realmModel.getAccessCodeLifespanUserAction());
|
||||
Assert.assertEquals(realmModel.getTokenLifespan(), 1000);
|
||||
Assert.assertEquals(realmModel.getAccessTokenLifespan(), 1000);
|
||||
Assert.assertEquals(realmModel.isEnabled(), true);
|
||||
Assert.assertEquals(realmModel.getName(), "JUGGLER");
|
||||
Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234");
|
||||
|
|
|
@ -127,7 +127,7 @@ public class AuthenticationManagerTest extends AbstractModelTest {
|
|||
realm.setName("Test");
|
||||
realm.setPrivateKeyPem("0234234");
|
||||
realm.setPublicKeyPem("0234234");
|
||||
realm.setTokenLifespan(1000);
|
||||
realm.setAccessTokenLifespan(1000);
|
||||
realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
|
||||
|
||||
am = new AuthenticationManager();
|
||||
|
|
|
@ -22,7 +22,7 @@ public class ModelTest extends AbstractModelTest {
|
|||
realm.setSocial(true);
|
||||
realm.setSslNotRequired(true);
|
||||
realm.setVerifyEmail(true);
|
||||
realm.setTokenLifespan(1000);
|
||||
realm.setAccessTokenLifespan(1000);
|
||||
realm.setPasswordPolicy(new PasswordPolicy("length"));
|
||||
realm.setAccessCodeLifespan(1001);
|
||||
realm.setAccessCodeLifespanUserAction(1002);
|
||||
|
@ -55,7 +55,7 @@ public class ModelTest extends AbstractModelTest {
|
|||
Assert.assertEquals(expected.isSocial(), actual.isSocial());
|
||||
Assert.assertEquals(expected.isSslNotRequired(), actual.isSslNotRequired());
|
||||
Assert.assertEquals(expected.isVerifyEmail(), actual.isVerifyEmail());
|
||||
Assert.assertEquals(expected.getTokenLifespan(), actual.getTokenLifespan());
|
||||
Assert.assertEquals(expected.getAccessTokenLifespan(), actual.getAccessTokenLifespan());
|
||||
|
||||
Assert.assertEquals(expected.getAccessCodeLifespan(), actual.getAccessCodeLifespan());
|
||||
Assert.assertEquals(expected.getAccessCodeLifespanUserAction(), actual.getAccessCodeLifespanUserAction());
|
||||
|
|
|
@ -11,8 +11,6 @@ import org.keycloak.models.UserCredentialModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
|
@ -48,7 +46,7 @@ public class ApplianceBootstrap {
|
|||
realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
|
||||
realm.addRequiredOAuthClientCredential(CredentialRepresentation.PASSWORD);
|
||||
realm.addRequiredResourceCredential(CredentialRepresentation.PASSWORD);
|
||||
realm.setTokenLifespan(300);
|
||||
realm.setAccessTokenLifespan(300);
|
||||
realm.setAccessCodeLifespan(60);
|
||||
realm.setAccessCodeLifespanUserAction(300);
|
||||
realm.setSslNotRequired(true);
|
||||
|
|
|
@ -46,8 +46,8 @@ public class AuthenticationManager {
|
|||
token.issuedNow();
|
||||
token.subject(user.getId());
|
||||
token.audience(realm.getName());
|
||||
if (realm.getTokenLifespan() > 0) {
|
||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
|
||||
if (realm.getAccessTokenLifespan() > 0) {
|
||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ public class ModelToRepresentation {
|
|||
rep.setRegistrationAllowed(realm.isRegistrationAllowed());
|
||||
rep.setVerifyEmail(realm.isVerifyEmail());
|
||||
rep.setResetPasswordAllowed(realm.isResetPasswordAllowed());
|
||||
rep.setTokenLifespan(realm.getTokenLifespan());
|
||||
rep.setTokenLifespan(realm.getAccessTokenLifespan());
|
||||
rep.setAccessCodeLifespan(realm.getAccessCodeLifespan());
|
||||
rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction());
|
||||
rep.setSmtpServer(realm.getSmtpConfig());
|
||||
|
|
|
@ -7,7 +7,6 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.OAuthClientModel;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RequiredCredentialModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.SocialLinkModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
|
@ -28,14 +27,10 @@ import org.keycloak.representations.idm.UserRoleMappingRepresentation;
|
|||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* Per request object
|
||||
|
@ -110,7 +105,7 @@ public class RealmManager {
|
|||
if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
|
||||
if (rep.getAccessCodeLifespanUserAction() != null)
|
||||
realm.setAccessCodeLifespanUserAction(rep.getAccessCodeLifespanUserAction());
|
||||
if (rep.getTokenLifespan() != null) realm.setTokenLifespan(rep.getTokenLifespan());
|
||||
if (rep.getTokenLifespan() != null) realm.setAccessTokenLifespan(rep.getTokenLifespan());
|
||||
if (rep.getRequiredCredentials() != null) {
|
||||
realm.updateRequiredCredentials(rep.getRequiredCredentials());
|
||||
}
|
||||
|
@ -163,8 +158,8 @@ public class RealmManager {
|
|||
if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled());
|
||||
if (rep.isSocial() != null) newRealm.setSocial(rep.isSocial());
|
||||
|
||||
if (rep.getTokenLifespan() != null) newRealm.setTokenLifespan(rep.getTokenLifespan());
|
||||
else newRealm.setTokenLifespan(300);
|
||||
if (rep.getTokenLifespan() != null) newRealm.setAccessTokenLifespan(rep.getTokenLifespan());
|
||||
else newRealm.setAccessTokenLifespan(300);
|
||||
|
||||
if (rep.getAccessCodeLifespan() != null) newRealm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
|
||||
else newRealm.setAccessCodeLifespan(60);
|
||||
|
|
|
@ -12,10 +12,12 @@ import org.keycloak.representations.AccessToken;
|
|||
import org.keycloak.util.Base64Url;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedHashMap;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -78,12 +80,44 @@ public class TokenManager {
|
|||
|
||||
|
||||
public AccessCodeEntry createAccessCode(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) {
|
||||
AccessCodeEntry code = createAccessCodeEntry(scopeParam, state, redirect, realm, client, user);
|
||||
accessCodeMap.put(code.getId(), code);
|
||||
return code;
|
||||
}
|
||||
|
||||
private AccessCodeEntry createAccessCodeEntry(String scopeParam, String state, String redirect, RealmModel realm, UserModel client, UserModel user) {
|
||||
AccessCodeEntry code = new AccessCodeEntry();
|
||||
AccessScope scopeMap = null;
|
||||
if (scopeParam != null) scopeMap = decodeScope(scopeParam);
|
||||
List<RoleModel> realmRolesRequested = code.getRealmRolesRequested();
|
||||
MultivaluedMap<String, RoleModel> resourceRolesRequested = code.getResourceRolesRequested();
|
||||
|
||||
AccessToken token = createClientAccessToken(scopeParam, realm, client, user, realmRolesRequested, resourceRolesRequested);
|
||||
|
||||
code.setToken(token);
|
||||
code.setRealm(realm);
|
||||
code.setExpiration((System.currentTimeMillis() / 1000) + 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;
|
||||
}
|
||||
|
||||
public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, UserModel client, UserModel user) {
|
||||
return createClientAccessToken(scopeParam, realm, client, user, new LinkedList<RoleModel>(), new MultivaluedHashMap<String, RoleModel>());
|
||||
}
|
||||
|
||||
|
||||
public AccessToken createClientAccessToken(String scopeParam, RealmModel realm, UserModel client, UserModel user, List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested) {
|
||||
AccessScope scopeMap = null;
|
||||
if (scopeParam != null) scopeMap = decodeScope(scopeParam);
|
||||
|
||||
|
||||
Set<RoleModel> roleMappings = realm.getRoleMappings(user);
|
||||
Set<RoleModel> scopeMappings = realm.getScopeMappings(client);
|
||||
|
@ -113,22 +147,22 @@ public class TokenManager {
|
|||
}
|
||||
}
|
||||
|
||||
createToken(code, realm, client, user);
|
||||
code.setRealm(realm);
|
||||
code.setExpiration((System.currentTimeMillis() / 1000) + realm.getAccessCodeLifespan());
|
||||
code.setClient(client);
|
||||
code.setUser(user);
|
||||
code.setState(state);
|
||||
code.setRedirectUri(redirect);
|
||||
accessCodeMap.put(code.getId(), code);
|
||||
String accessCode = null;
|
||||
try {
|
||||
accessCode = new JWSBuilder().content(code.getId().getBytes("UTF-8")).rsa256(realm.getPrivateKey());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
AccessToken token = initToken(realm, client, user);
|
||||
|
||||
if (realmRolesRequested.size() > 0) {
|
||||
for (RoleModel role : realmRolesRequested) {
|
||||
addComposites(token, role);
|
||||
}
|
||||
code.setCode(accessCode);
|
||||
return code;
|
||||
}
|
||||
|
||||
if (resourceRolesRequested.size() > 0) {
|
||||
for (List<RoleModel> roles : resourceRolesRequested.values()) {
|
||||
for (RoleModel role : roles) {
|
||||
addComposites(token, role);
|
||||
}
|
||||
}
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
protected AccessToken initToken(RealmModel realm, UserModel client, UserModel user) {
|
||||
|
@ -138,8 +172,8 @@ public class TokenManager {
|
|||
token.audience(realm.getName());
|
||||
token.issuedNow();
|
||||
token.issuedFor(client.getLoginName());
|
||||
if (realm.getTokenLifespan() > 0) {
|
||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
|
||||
if (realm.getAccessTokenLifespan() > 0) {
|
||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
|
||||
}
|
||||
Set<String> allowedOrigins = client.getWebOrigins();
|
||||
if (allowedOrigins != null) {
|
||||
|
@ -176,26 +210,6 @@ public class TokenManager {
|
|||
|
||||
}
|
||||
|
||||
protected void createToken(AccessCodeEntry accessCodeEntry, RealmModel realm, UserModel client, UserModel user) {
|
||||
|
||||
AccessToken token = initToken(realm, client, user);
|
||||
|
||||
if (accessCodeEntry.getRealmRolesRequested().size() > 0) {
|
||||
for (RoleModel role : accessCodeEntry.getRealmRolesRequested()) {
|
||||
addComposites(token, role);
|
||||
}
|
||||
}
|
||||
|
||||
if (accessCodeEntry.getResourceRolesRequested().size() > 0) {
|
||||
for (List<RoleModel> roles : accessCodeEntry.getResourceRolesRequested().values()) {
|
||||
for (RoleModel role : roles) {
|
||||
addComposites(token, role);
|
||||
}
|
||||
}
|
||||
}
|
||||
accessCodeEntry.setToken(token);
|
||||
}
|
||||
|
||||
public String encodeScope(AccessScope scope) {
|
||||
String token = null;
|
||||
try {
|
||||
|
@ -224,8 +238,8 @@ public class TokenManager {
|
|||
token.issuedNow();
|
||||
token.subject(user.getId());
|
||||
token.audience(realm.getName());
|
||||
if (realm.getTokenLifespan() > 0) {
|
||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getTokenLifespan());
|
||||
if (realm.getAccessTokenLifespan() > 0) {
|
||||
token.expiration((System.currentTimeMillis() / 1000) + realm.getAccessTokenLifespan());
|
||||
}
|
||||
for (RoleModel role : realm.getRoleMappings(user)) {
|
||||
addComposites(token, role);
|
||||
|
|
|
@ -53,13 +53,11 @@ public class PublicRealmResource {
|
|||
String authUri = TokenService.loginPageUrl(uriInfo).build(realm.getName()).toString();
|
||||
String codeUri = TokenService.accessCodeToTokenUrl(uriInfo).build(realm.getName()).toString();
|
||||
String grantUrl = TokenService.grantAccessTokenUrl(uriInfo).build(realm.getName()).toString();
|
||||
String idGrantUrl = TokenService.grantIdentityTokenUrl(uriInfo).build(realm.getName()).toString();
|
||||
|
||||
html.append("<html><body><h1>Realm: ").append(realm.getName()).append("</h1>");
|
||||
html.append("<p>auth: ").append(authUri).append("</p>");
|
||||
html.append("<p>code: ").append(codeUri).append("</p>");
|
||||
html.append("<p>grant: ").append(grantUrl).append("</p>");
|
||||
html.append("<p>identity grant: ").append(idGrantUrl).append("</p>");
|
||||
html.append("<p>public key: ").append(realm.getPublicKeyPem()).append("</p>");
|
||||
html.append("</body></html>");
|
||||
|
||||
|
@ -77,8 +75,6 @@ public class PublicRealmResource {
|
|||
rep.setAuthorizationUrl(TokenService.loginPageUrl(uriInfo).build(realm.getName()).toString());
|
||||
rep.setCodeUrl(TokenService.accessCodeToTokenUrl(uriInfo).build(realm.getName()).toString());
|
||||
rep.setGrantUrl(TokenService.grantAccessTokenUrl(uriInfo).build(realm.getName()).toString());
|
||||
String idGrantUrl = TokenService.grantIdentityTokenUrl(uriInfo).build(realm.getName()).toString();
|
||||
rep.setIdentityGrantUrl(idGrantUrl);
|
||||
return rep;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.keycloak.services.resources.flows.OAuthFlows;
|
|||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
|
||||
import javax.ws.rs.BadRequestException;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
|
@ -108,11 +109,6 @@ public class TokenService {
|
|||
|
||||
}
|
||||
|
||||
public static UriBuilder grantIdentityTokenUrl(UriInfo uriInfo) {
|
||||
return tokenServiceBaseUrl(uriInfo).path(TokenService.class, "grantIdentityToken");
|
||||
|
||||
}
|
||||
|
||||
public static UriBuilder loginPageUrl(UriInfo uriInfo) {
|
||||
return tokenServiceBaseUrl(uriInfo).path(TokenService.class, "loginPage");
|
||||
}
|
||||
|
@ -129,45 +125,19 @@ public class TokenService {
|
|||
return tokenServiceBaseUrl(uriInfo).path(TokenService.class, "processOAuth");
|
||||
}
|
||||
|
||||
@Path("grants/identity-token")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response grantIdentityToken(final MultivaluedMap<String, String> form) {
|
||||
if (!checkSsl()) {
|
||||
throw new NotAcceptableException("HTTPS required");
|
||||
}
|
||||
|
||||
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
|
||||
if (username == null) {
|
||||
throw new NotAuthorizedException("No user");
|
||||
}
|
||||
if (!realm.isEnabled()) {
|
||||
throw new NotAuthorizedException("Disabled realm");
|
||||
}
|
||||
UserModel user = realm.getUser(username);
|
||||
|
||||
AuthenticationStatus status = authManager.authenticateForm(realm, user, form);
|
||||
if (status != AuthenticationStatus.SUCCESS) {
|
||||
throw new NotAuthorizedException(status);
|
||||
}
|
||||
|
||||
tokenManager = new TokenManager();
|
||||
AccessToken token = authManager.createIdentityToken(realm, user);
|
||||
String encoded = tokenManager.encodeToken(realm, token);
|
||||
AccessTokenResponse res = accessTokenResponse(token, encoded);
|
||||
return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
|
||||
}
|
||||
|
||||
@Path("grants/access")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response grantAccessToken(final MultivaluedMap<String, String> form) {
|
||||
public Response grantAccessToken(final @HeaderParam(HttpHeaders.AUTHORIZATION) String authorizationHeader,
|
||||
final MultivaluedMap<String, String> form) {
|
||||
if (!checkSsl()) {
|
||||
throw new NotAcceptableException("HTTPS required");
|
||||
}
|
||||
|
||||
UserModel client = authorizeClient(authorizationHeader);
|
||||
|
||||
|
||||
String username = form.getFirst(AuthenticationManager.FORM_USERNAME);
|
||||
if (username == null) {
|
||||
throw new NotAuthorizedException("No user");
|
||||
|
@ -185,7 +155,8 @@ public class TokenService {
|
|||
if (authManager.authenticateForm(realm, user, form) != AuthenticationStatus.SUCCESS) {
|
||||
throw new NotAuthorizedException("Auth failed");
|
||||
}
|
||||
AccessToken token = tokenManager.createAccessToken(realm, user);
|
||||
String scope = form.getFirst("scope");
|
||||
AccessToken token = tokenManager.createClientAccessToken(scope, realm, client, user);
|
||||
String encoded = tokenManager.encodeToken(realm, token);
|
||||
AccessTokenResponse res = accessTokenResponse(token, encoded);
|
||||
return Response.ok(res, MediaType.APPLICATION_JSON_TYPE).build();
|
||||
|
@ -338,47 +309,14 @@ public class TokenService {
|
|||
throw new NotAuthorizedException("Realm not enabled");
|
||||
}
|
||||
|
||||
if (authorizationHeader == null) {
|
||||
throw new NotAuthorizedException("No Authorization header to authenticate client", "Basic realm=\"" + realm.getName() + "\"");
|
||||
}
|
||||
|
||||
String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
|
||||
if (usernameSecret == null) {
|
||||
throw new NotAuthorizedException("No Authorization header to authenticate client", "Basic realm=\"" + realm.getName() + "\"");
|
||||
}
|
||||
|
||||
String client_id = usernameSecret[0];
|
||||
String clientSecret = usernameSecret[1];
|
||||
UserModel client = realm.getUser(client_id);
|
||||
if (client == null) {
|
||||
logger.debug("Could not find user");
|
||||
Map<String, String> error = new HashMap<String, String>();
|
||||
error.put("error", "invalid_client");
|
||||
error.put("error_description", "Could not find user");
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
|
||||
}
|
||||
|
||||
if (!client.isEnabled()) {
|
||||
logger.debug("user is not enabled");
|
||||
Map<String, String> error = new HashMap<String, String>();
|
||||
error.put("error", "invalid_client");
|
||||
error.put("error_description", "User is not enabled");
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
|
||||
}
|
||||
|
||||
if (!realm.validateSecret(client, clientSecret)) {
|
||||
Map<String, String> error = new HashMap<String, String>();
|
||||
error.put("error", "unauthorized_client");
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build();
|
||||
}
|
||||
UserModel client = authorizeClient(authorizationHeader);
|
||||
|
||||
String code = formData.getFirst("code");
|
||||
if (code == null) {
|
||||
logger.debug("code not specified");
|
||||
Map<String, String> error = new HashMap<String, String>();
|
||||
error.put("error", "invalid_request");
|
||||
error.put("error_description", "code not specified");
|
||||
return 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());
|
||||
|
||||
}
|
||||
|
||||
|
@ -432,6 +370,41 @@ public class TokenService {
|
|||
return Cors.add(request, Response.ok(res)).allowedOrigins(client).allowedMethods("POST").build();
|
||||
}
|
||||
|
||||
protected UserModel authorizeClient(String authorizationHeader) {
|
||||
if (authorizationHeader == null) {
|
||||
throw new NotAuthorizedException("No Authorization header to authenticate client", "Basic realm=\"" + realm.getName() + "\"");
|
||||
}
|
||||
|
||||
String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
|
||||
if (usernameSecret == null) {
|
||||
throw new NotAuthorizedException("No Authorization header to authenticate client", "Basic realm=\"" + realm.getName() + "\"");
|
||||
}
|
||||
|
||||
String client_id = usernameSecret[0];
|
||||
String clientSecret = usernameSecret[1];
|
||||
UserModel client = realm.getUser(client_id);
|
||||
if (client == null) {
|
||||
Map<String, String> error = new HashMap<String, String>();
|
||||
error.put("error", "invalid_client");
|
||||
error.put("error_description", "Could not find client");
|
||||
throw new BadRequestException("Could not find client", Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build());
|
||||
}
|
||||
|
||||
if (!client.isEnabled()) {
|
||||
Map<String, String> error = new HashMap<String, String>();
|
||||
error.put("error", "invalid_client");
|
||||
error.put("error_description", "Client is not enabled");
|
||||
throw new BadRequestException("Client is not enabled", Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build());
|
||||
}
|
||||
|
||||
if (!realm.validateSecret(client, clientSecret)) {
|
||||
Map<String, String> error = new HashMap<String, String>();
|
||||
error.put("error", "unauthorized_client");
|
||||
throw new BadRequestException("Unauthorized Client", Response.status(Response.Status.BAD_REQUEST).entity(error).type("application/json").build());
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
protected AccessTokenResponse accessTokenResponse(PrivateKey privateKey, AccessToken token) {
|
||||
String encodedToken = new JWSBuilder().jsonContent(token).rsa256(privateKey);
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ public class CompositeRoleTest {
|
|||
RealmModel realm = manager.createRealm("Test");
|
||||
manager.generateRealmKeys(realm);
|
||||
realmPublicKey = realm.getPublicKey();
|
||||
realm.setTokenLifespan(10000);
|
||||
realm.setAccessTokenLifespan(10000);
|
||||
realm.setAccessCodeLifespanUserAction(1000);
|
||||
realm.setAccessCodeLifespan(1000);
|
||||
realm.setSslNotRequired(true);
|
||||
|
|
Loading…
Reference in a new issue