This commit is contained in:
Bill Burke 2016-09-22 19:57:39 -04:00
commit 8e65356891
288 changed files with 7282 additions and 1082 deletions

View file

@ -52,7 +52,7 @@ To stop the server press `Ctrl + C`.
Help and Documentation Help and Documentation
---------------------- ----------------------
* [Documentation](http://keycloak.jboss.org/docs) - User Guide, Admin REST API and Javadocs * [Documentation](http://www.keycloak.org/documentation.html) - User Guide, Admin REST API and Javadocs
* [User Mailing List](https://lists.jboss.org/mailman/listinfo/keycloak-user) - Mailing list to ask for help and general questions about Keycloak * [User Mailing List](https://lists.jboss.org/mailman/listinfo/keycloak-user) - Mailing list to ask for help and general questions about Keycloak
* [JIRA](https://issues.jboss.org/projects/KEYCLOAK) - Issue tracker for bugs and feature requests * [JIRA](https://issues.jboss.org/projects/KEYCLOAK) - Issue tracker for bugs and feature requests
@ -72,4 +72,4 @@ Contributing
License License
------- -------
* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) * [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)

View file

@ -17,26 +17,21 @@
package org.keycloak.adapters; package org.keycloak.adapters;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProvider; import org.keycloak.adapters.authentication.ClientCredentialsProvider;
import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.adapters.rotation.PublicKeyLocator;
import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.enums.RelativeUrlsUsed; import org.keycloak.common.enums.RelativeUrlsUsed;
import org.keycloak.common.enums.SslRequired; import org.keycloak.common.enums.SslRequired;
import org.keycloak.enums.TokenStore; import org.keycloak.enums.TokenStore;
import org.keycloak.representations.adapters.config.AdapterConfig; import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.idm.PublishedRealmRepresentation;
import org.keycloak.util.JsonSerialization;
import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.KeycloakUriBuilder;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.security.PublicKey;
import java.util.Map; import java.util.Map;
/** /**
@ -57,7 +52,7 @@ public class AdapterDeploymentContext {
* during the application deployment's life cycle. * during the application deployment's life cycle.
* *
* @param deployment A KeycloakConfigResolver, possibly missing the Auth * @param deployment A KeycloakConfigResolver, possibly missing the Auth
* Server URL and/or Realm Public Key * Server URL
*/ */
public AdapterDeploymentContext(KeycloakDeployment deployment) { public AdapterDeploymentContext(KeycloakDeployment deployment) {
this.deployment = deployment; this.deployment = deployment;
@ -79,7 +74,6 @@ public class AdapterDeploymentContext {
/** /**
* For single-tenant deployments, it complements KeycloakDeployment * For single-tenant deployments, it complements KeycloakDeployment
* by resolving a relative Auth Server's URL based on the current request * by resolving a relative Auth Server's URL based on the current request
* and, if needed, will lazily resolve the Realm's Public Key.
* *
* For multi-tenant deployments, defers the resolution of KeycloakDeployment * For multi-tenant deployments, defers the resolution of KeycloakDeployment
* to the KeycloakConfigResolver . * to the KeycloakConfigResolver .
@ -98,8 +92,8 @@ public class AdapterDeploymentContext {
if (deployment.getAuthServerBaseUrl() == null) return deployment; if (deployment.getAuthServerBaseUrl() == null) return deployment;
KeycloakDeployment resolvedDeployment = resolveUrls(deployment, facade); KeycloakDeployment resolvedDeployment = resolveUrls(deployment, facade);
if (resolvedDeployment.getRealmKey() == null) { if (resolvedDeployment.getPublicKeyLocator() == null) {
resolveRealmKey(resolvedDeployment); throw new RuntimeException("KeycloakDeployment was never initialized through appropriate SPIs");
} }
return resolvedDeployment; return resolvedDeployment;
} }
@ -115,45 +109,6 @@ public class AdapterDeploymentContext {
} }
} }
public void resolveRealmKey(KeycloakDeployment deployment) {
if (deployment.getClient() == null) {
throw new RuntimeException("KeycloakDeployment was never initialized through appropriate SPIs");
}
HttpGet get = new HttpGet(deployment.getRealmInfoUrl());
try {
HttpResponse response = deployment.getClient().execute(get);
int status = response.getStatusLine().getStatusCode();
if (status != 200) {
close(response);
throw new RuntimeException("Unable to resolve realm public key remotely, status = " + status);
}
HttpEntity entity = response.getEntity();
if (entity == null) {
throw new RuntimeException("Unable to resolve realm public key remotely. There was no entity.");
}
InputStream is = entity.getContent();
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ((c = is.read()) != -1) {
os.write(c);
}
byte[] bytes = os.toByteArray();
String json = new String(bytes);
PublishedRealmRepresentation rep = JsonSerialization.readValue(json, PublishedRealmRepresentation.class);
deployment.setRealmKey(rep.getPublicKey());
} finally {
try {
is.close();
} catch (IOException ignored) {
}
}
} catch (IOException e) {
throw new RuntimeException("Unable to resolve realm public key remotely", e);
}
}
/** /**
* This delegate is used to store temporary, per-request metadata like request resolved URLs. * This delegate is used to store temporary, per-request metadata like request resolved URLs.
* Ever method is delegated except URL get methods and isConfigured() * Ever method is delegated except URL get methods and isConfigured()
@ -207,6 +162,11 @@ public class AdapterDeploymentContext {
return (this.unregisterNodeUrl != null) ? this.unregisterNodeUrl : delegate.getUnregisterNodeUrl(); return (this.unregisterNodeUrl != null) ? this.unregisterNodeUrl : delegate.getUnregisterNodeUrl();
} }
@Override
public String getJwksUrl() {
return (this.jwksUrl != null) ? this.jwksUrl : delegate.getJwksUrl();
}
@Override @Override
public String getResourceName() { public String getResourceName() {
return delegate.getResourceName(); return delegate.getResourceName();
@ -223,13 +183,13 @@ public class AdapterDeploymentContext {
} }
@Override @Override
public PublicKey getRealmKey() { public void setPublicKeyLocator(PublicKeyLocator publicKeyLocator) {
return delegate.getRealmKey(); delegate.setPublicKeyLocator(publicKeyLocator);
} }
@Override @Override
public void setRealmKey(PublicKey realmKey) { public PublicKeyLocator getPublicKeyLocator() {
delegate.setRealmKey(realmKey); return delegate.getPublicKeyLocator();
} }
@Override @Override
@ -466,6 +426,26 @@ public class AdapterDeploymentContext {
public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) { public void setTokenMinimumTimeToLive(final int tokenMinimumTimeToLive) {
delegate.setTokenMinimumTimeToLive(tokenMinimumTimeToLive); delegate.setTokenMinimumTimeToLive(tokenMinimumTimeToLive);
} }
@Override
public PolicyEnforcer getPolicyEnforcer() {
return delegate.getPolicyEnforcer();
}
@Override
public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) {
delegate.setPolicyEnforcer(policyEnforcer);
}
@Override
public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) {
delegate.setMinTimeBetweenJwksRequests(minTimeBetweenJwksRequests);
}
@Override
public int getMinTimeBetweenJwksRequests() {
return delegate.getMinTimeBetweenJwksRequests();
}
} }
protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) { protected KeycloakUriBuilder getBaseBuilder(HttpFacade facade, String base) {

View file

@ -19,6 +19,7 @@ package org.keycloak.adapters;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier; import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.AuthChallenge; import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome; import org.keycloak.adapters.spi.AuthOutcome;
import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.HttpFacade;
@ -84,7 +85,7 @@ public class BearerTokenRequestAuthenticator {
protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) { protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) {
try { try {
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
} catch (VerificationException e) { } catch (VerificationException e) {
log.error("Failed to verify token", e); log.error("Failed to verify token", e);
challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", e.getMessage()); challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", e.getMessage());

View file

@ -22,6 +22,7 @@ import java.io.IOException;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.KeycloakPrincipal; import org.keycloak.KeycloakPrincipal;
import org.keycloak.RSATokenVerifier; import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.constants.AdapterConstants; import org.keycloak.constants.AdapterConstants;
@ -73,7 +74,7 @@ public class CookieTokenStore {
try { try {
// Skip check if token is active now. It's supposed to be done later by the caller // Skip check if token is active now. It's supposed to be done later by the caller
AccessToken accessToken = RSATokenVerifier.verifyToken(accessTokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl(), false, true); AccessToken accessToken = AdapterRSATokenVerifier.verifyToken(accessTokenString, deployment, false, true);
IDToken idToken; IDToken idToken;
if (idTokenString != null && idTokenString.length() > 0) { if (idTokenString != null && idTokenString.length() > 0) {
try { try {

View file

@ -0,0 +1,79 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.keycloak.util.JsonSerialization;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class HttpAdapterUtils {
public static <T> T sendJsonHttpRequest(KeycloakDeployment deployment, HttpRequestBase httpRequest, Class<T> clazz) throws HttpClientAdapterException {
try {
HttpResponse response = deployment.getClient().execute(httpRequest);
int status = response.getStatusLine().getStatusCode();
if (status != 200) {
close(response);
throw new HttpClientAdapterException("Unexpected status = " + status);
}
HttpEntity entity = response.getEntity();
if (entity == null) {
throw new HttpClientAdapterException("There was no entity.");
}
InputStream is = entity.getContent();
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
int c;
while ((c = is.read()) != -1) {
os.write(c);
}
byte[] bytes = os.toByteArray();
String json = new String(bytes);
return JsonSerialization.readValue(json, clazz);
} finally {
try {
is.close();
} catch (IOException ignored) {
}
}
} catch (IOException e) {
throw new HttpClientAdapterException("IO error", e);
}
}
private static void close(HttpResponse response) {
if (response.getEntity() != null) {
try {
response.getEntity().getContent().close();
} catch (IOException e) {
}
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class HttpClientAdapterException extends Exception {
public HttpClientAdapterException(String message) {
super(message);
}
public HttpClientAdapterException(String message, Throwable t) {
super(message, t);
}
}

View file

@ -21,6 +21,7 @@ import org.apache.http.client.HttpClient;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProvider; import org.keycloak.adapters.authentication.ClientCredentialsProvider;
import org.keycloak.adapters.authorization.PolicyEnforcer; import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.adapters.rotation.PublicKeyLocator;
import org.keycloak.constants.ServiceUrlConstants; import org.keycloak.constants.ServiceUrlConstants;
import org.keycloak.common.enums.RelativeUrlsUsed; import org.keycloak.common.enums.RelativeUrlsUsed;
import org.keycloak.common.enums.SslRequired; import org.keycloak.common.enums.SslRequired;
@ -29,7 +30,6 @@ import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.common.util.KeycloakUriBuilder; import org.keycloak.common.util.KeycloakUriBuilder;
import java.net.URI; import java.net.URI;
import java.security.PublicKey;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -43,7 +43,7 @@ public class KeycloakDeployment {
protected RelativeUrlsUsed relativeUrls; protected RelativeUrlsUsed relativeUrls;
protected String realm; protected String realm;
protected volatile PublicKey realmKey; protected PublicKeyLocator publicKeyLocator;
protected String authServerBaseUrl; protected String authServerBaseUrl;
protected String realmInfoUrl; protected String realmInfoUrl;
protected KeycloakUriBuilder authUrl; protected KeycloakUriBuilder authUrl;
@ -52,6 +52,7 @@ public class KeycloakDeployment {
protected String accountUrl; protected String accountUrl;
protected String registerNodeUrl; protected String registerNodeUrl;
protected String unregisterNodeUrl; protected String unregisterNodeUrl;
protected String jwksUrl;
protected String principalAttribute = "sub"; protected String principalAttribute = "sub";
protected String resourceName; protected String resourceName;
@ -79,13 +80,14 @@ public class KeycloakDeployment {
protected volatile int notBefore; protected volatile int notBefore;
protected int tokenMinimumTimeToLive; protected int tokenMinimumTimeToLive;
protected int minTimeBetweenJwksRequests;
private PolicyEnforcer policyEnforcer; private PolicyEnforcer policyEnforcer;
public KeycloakDeployment() { public KeycloakDeployment() {
} }
public boolean isConfigured() { public boolean isConfigured() {
return getRealm() != null && getRealmKey() != null && (isBearerOnly() || getAuthServerBaseUrl() != null); return getRealm() != null && getPublicKeyLocator() != null && (isBearerOnly() || getAuthServerBaseUrl() != null);
} }
public String getResourceName() { public String getResourceName() {
@ -100,12 +102,12 @@ public class KeycloakDeployment {
this.realm = realm; this.realm = realm;
} }
public PublicKey getRealmKey() { public PublicKeyLocator getPublicKeyLocator() {
return realmKey; return publicKeyLocator;
} }
public void setRealmKey(PublicKey realmKey) { public void setPublicKeyLocator(PublicKeyLocator publicKeyLocator) {
this.realmKey = realmKey; this.publicKeyLocator = publicKeyLocator;
} }
public String getAuthServerBaseUrl() { public String getAuthServerBaseUrl() {
@ -147,6 +149,7 @@ public class KeycloakDeployment {
accountUrl = authUrlBuilder.clone().path(ServiceUrlConstants.ACCOUNT_SERVICE_PATH).build(getRealm()).toString(); accountUrl = authUrlBuilder.clone().path(ServiceUrlConstants.ACCOUNT_SERVICE_PATH).build(getRealm()).toString();
registerNodeUrl = authUrlBuilder.clone().path(ServiceUrlConstants.CLIENTS_MANAGEMENT_REGISTER_NODE_PATH).build(getRealm()).toString(); registerNodeUrl = authUrlBuilder.clone().path(ServiceUrlConstants.CLIENTS_MANAGEMENT_REGISTER_NODE_PATH).build(getRealm()).toString();
unregisterNodeUrl = authUrlBuilder.clone().path(ServiceUrlConstants.CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH).build(getRealm()).toString(); unregisterNodeUrl = authUrlBuilder.clone().path(ServiceUrlConstants.CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH).build(getRealm()).toString();
jwksUrl = authUrlBuilder.clone().path(ServiceUrlConstants.JWKS_URL).build(getRealm()).toString();
} }
public RelativeUrlsUsed getRelativeUrls() { public RelativeUrlsUsed getRelativeUrls() {
@ -181,6 +184,10 @@ public class KeycloakDeployment {
return unregisterNodeUrl; return unregisterNodeUrl;
} }
public String getJwksUrl() {
return jwksUrl;
}
public void setResourceName(String resourceName) { public void setResourceName(String resourceName) {
this.resourceName = resourceName; this.resourceName = resourceName;
} }
@ -369,6 +376,14 @@ public class KeycloakDeployment {
this.tokenMinimumTimeToLive = tokenMinimumTimeToLive; this.tokenMinimumTimeToLive = tokenMinimumTimeToLive;
} }
public int getMinTimeBetweenJwksRequests() {
return minTimeBetweenJwksRequests;
}
public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) {
this.minTimeBetweenJwksRequests = minTimeBetweenJwksRequests;
}
public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) { public void setPolicyEnforcer(PolicyEnforcer policyEnforcer) {
this.policyEnforcer = policyEnforcer; this.policyEnforcer = policyEnforcer;
} }

View file

@ -22,6 +22,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils; import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
import org.keycloak.adapters.authorization.PolicyEnforcer; import org.keycloak.adapters.authorization.PolicyEnforcer;
import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator;
import org.keycloak.adapters.rotation.JWKPublicKeyLocator;
import org.keycloak.common.enums.SslRequired; import org.keycloak.common.enums.SslRequired;
import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.PemUtils;
import org.keycloak.enums.TokenStore; import org.keycloak.enums.TokenStore;
@ -59,11 +61,16 @@ public class KeycloakDeploymentBuilder {
PublicKey realmKey; PublicKey realmKey;
try { try {
realmKey = PemUtils.decodePublicKey(realmKeyPem); realmKey = PemUtils.decodePublicKey(realmKeyPem);
HardcodedPublicKeyLocator pkLocator = new HardcodedPublicKeyLocator(realmKey);
deployment.setPublicKeyLocator(pkLocator);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
deployment.setRealmKey(realmKey); } else {
JWKPublicKeyLocator pkLocator = new JWKPublicKeyLocator();
deployment.setPublicKeyLocator(pkLocator);
} }
if (adapterConfig.getSslRequired() != null) { if (adapterConfig.getSslRequired() != null) {
deployment.setSslRequired(SslRequired.valueOf(adapterConfig.getSslRequired().toUpperCase())); deployment.setSslRequired(SslRequired.valueOf(adapterConfig.getSslRequired().toUpperCase()));
} else { } else {
@ -97,6 +104,7 @@ public class KeycloakDeploymentBuilder {
deployment.setRegisterNodeAtStartup(adapterConfig.isRegisterNodeAtStartup()); deployment.setRegisterNodeAtStartup(adapterConfig.isRegisterNodeAtStartup());
deployment.setRegisterNodePeriod(adapterConfig.getRegisterNodePeriod()); deployment.setRegisterNodePeriod(adapterConfig.getRegisterNodePeriod());
deployment.setTokenMinimumTimeToLive(adapterConfig.getTokenMinimumTimeToLive()); deployment.setTokenMinimumTimeToLive(adapterConfig.getTokenMinimumTimeToLive());
deployment.setMinTimeBetweenJwksRequests(adapterConfig.getMinTimeBetweenJwksRequests());
if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) { if (realmKeyPem == null && adapterConfig.isBearerOnly() && adapterConfig.getAuthServerUrl() == null) {
throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url"); throw new IllegalArgumentException("For bearer auth, you must set the realm-public-key or auth-server-url");

View file

@ -20,6 +20,7 @@ package org.keycloak.adapters;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.RSATokenVerifier; import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.AdapterSessionStore; import org.keycloak.adapters.spi.AdapterSessionStore;
import org.keycloak.adapters.spi.AuthChallenge; import org.keycloak.adapters.spi.AuthChallenge;
import org.keycloak.adapters.spi.AuthOutcome; import org.keycloak.adapters.spi.AuthOutcome;
@ -342,7 +343,7 @@ public class OAuthRequestAuthenticator {
refreshToken = tokenResponse.getRefreshToken(); refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken(); idTokenString = tokenResponse.getIdToken();
try { try {
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
if (idTokenString != null) { if (idTokenString != null) {
try { try {
JWSInput input = new JWSInput(idTokenString); JWSInput input = new JWSInput(idTokenString);

View file

@ -17,7 +17,10 @@
package org.keycloak.adapters; package org.keycloak.adapters;
import java.security.PublicKey;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.adapters.spi.UserSessionManagement; import org.keycloak.adapters.spi.UserSessionManagement;
import org.keycloak.jose.jws.JWSInputException; import org.keycloak.jose.jws.JWSInputException;
@ -198,7 +201,8 @@ public class PreAuthActionsHandler {
try { try {
JWSInput input = new JWSInput(token); JWSInput input = new JWSInput(token);
if (RSAProvider.verify(input, deployment.getRealmKey())) { PublicKey publicKey = AdapterRSATokenVerifier.getPublicKey(input, deployment);
if (RSAProvider.verify(input, publicKey)) {
return input; return input;
} }
} catch (JWSInputException ignore) { } catch (JWSInputException ignore) {

View file

@ -21,6 +21,7 @@ import org.jboss.logging.Logger;
import org.keycloak.AuthorizationContext; import org.keycloak.AuthorizationContext;
import org.keycloak.KeycloakSecurityContext; import org.keycloak.KeycloakSecurityContext;
import org.keycloak.RSATokenVerifier; import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.common.util.Time; import org.keycloak.common.util.Time;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
@ -130,7 +131,7 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
String tokenString = response.getToken(); String tokenString = response.getToken();
AccessToken token = null; AccessToken token = null;
try { try {
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
log.debug("Token Verification succeeded!"); log.debug("Token Verification succeeded!");
} catch (VerificationException e) { } catch (VerificationException e) {
log.error("failed verification of token"); log.error("failed verification of token");

View file

@ -21,6 +21,7 @@ import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier; import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.OIDCHttpFacade; import org.keycloak.adapters.OIDCHttpFacade;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.adapters.spi.HttpFacade;
import org.keycloak.authorization.client.AuthorizationDeniedException; import org.keycloak.authorization.client.AuthorizationDeniedException;
import org.keycloak.authorization.client.AuthzClient; import org.keycloak.authorization.client.AuthzClient;
@ -120,7 +121,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
AuthorizationResponse authzResponse = authzClient.authorization(accessToken).authorize(authzRequest); AuthorizationResponse authzResponse = authzClient.authorization(accessToken).authorize(authzRequest);
if (authzResponse != null) { if (authzResponse != null) {
return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl()); return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
} }
return null; return null;
@ -130,7 +131,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
if (token.getAuthorization() == null) { if (token.getAuthorization() == null) {
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getClientId()); EntitlementResponse authzResponse = authzClient.entitlement(accessToken).getAll(authzClient.getConfiguration().getClientId());
return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl()); return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
} else { } else {
EntitlementRequest request = new EntitlementRequest(); EntitlementRequest request = new EntitlementRequest();
PermissionRequest permissionRequest = new PermissionRequest(); PermissionRequest permissionRequest = new PermissionRequest();
@ -139,7 +140,7 @@ public class KeycloakAdapterPolicyEnforcer extends AbstractPolicyEnforcer {
permissionRequest.setScopes(new HashSet<>(pathConfig.getScopes())); permissionRequest.setScopes(new HashSet<>(pathConfig.getScopes()));
request.addPermission(permissionRequest); request.addPermission(permissionRequest);
EntitlementResponse authzResponse = authzClient.entitlement(accessToken).get(authzClient.getConfiguration().getClientId(), request); EntitlementResponse authzResponse = authzClient.entitlement(accessToken).get(authzClient.getConfiguration().getClientId(), request);
return RSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment.getRealmKey(), deployment.getRealmInfoUrl()); return AdapterRSATokenVerifier.verifyToken(authzResponse.getRpt(), deployment);
} }
} }
} catch (AuthorizationDeniedException e) { } catch (AuthorizationDeniedException e) {

View file

@ -43,6 +43,7 @@ import org.keycloak.adapters.AdapterUtils;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder; import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext; import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.common.util.FindFile; import org.keycloak.common.util.FindFile;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
@ -88,9 +89,6 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
try { try {
InputStream is = FindFile.findFile(keycloakConfigFile); InputStream is = FindFile.findFile(keycloakConfigFile);
KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is); KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is);
if (kd.getRealmKey() == null) {
new AdapterDeploymentContext().resolveRealmKey(kd);
}
return kd; return kd;
} catch (RuntimeException e) { } catch (RuntimeException e) {
getLogger().debug("Unable to find or parse file " + keycloakConfigFile + " due to " + e.getMessage(), e); getLogger().debug("Unable to find or parse file " + keycloakConfigFile + " due to " + e.getMessage(), e);
@ -190,7 +188,7 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
protected Auth bearerAuth(String tokenString) throws VerificationException { protected Auth bearerAuth(String tokenString) throws VerificationException {
AccessToken token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); AccessToken token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
boolean verifyCaller; boolean verifyCaller;
if (deployment.isUseResourceRoleMappings()) { if (deployment.isUseResourceRoleMappings()) {

View file

@ -0,0 +1,64 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.rotation;
import java.security.PublicKey;
import org.jboss.logging.Logger;
import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.common.VerificationException;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.representations.AccessToken;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AdapterRSATokenVerifier {
private static final Logger log = Logger.getLogger(AdapterRSATokenVerifier.class);
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment) throws VerificationException {
return verifyToken(tokenString, deployment, true, true);
}
public static PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) throws VerificationException {
PublicKeyLocator pkLocator = deployment.getPublicKeyLocator();
PublicKey publicKey = pkLocator.getPublicKey(input, deployment);
if (publicKey == null) {
log.errorf("Didn't find publicKey for kid: %s", input.getHeader().getKeyId());
throw new VerificationException("Didn't find publicKey for specified kid");
}
return publicKey;
}
public static AccessToken verifyToken(String tokenString, KeycloakDeployment deployment, boolean checkActive, boolean checkTokenType) throws VerificationException {
JWSInput input;
try {
input = new JWSInput(tokenString);
} catch (Exception e) {
throw new VerificationException("Couldn't parse token", e);
}
PublicKey publicKey = getPublicKey(input, deployment);
return RSATokenVerifier.verifyToken(input, publicKey, deployment.getRealmInfoUrl(), checkActive, checkTokenType);
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.rotation;
import java.security.PublicKey;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.jose.jws.JWSInput;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class HardcodedPublicKeyLocator implements PublicKeyLocator {
private PublicKey publicKey;
public HardcodedPublicKeyLocator(PublicKey publicKey) {
this.publicKey = publicKey;
}
@Override
public PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) {
return publicKey;
}
}

View file

@ -0,0 +1,107 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.rotation;
import java.security.PublicKey;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.http.client.methods.HttpGet;
import org.jboss.logging.Logger;
import org.keycloak.adapters.HttpAdapterUtils;
import org.keycloak.adapters.HttpClientAdapterException;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.common.util.Time;
import org.keycloak.jose.jwk.JSONWebKeySet;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jws.JWSInput;
import org.keycloak.util.JWKSUtils;
/**
* When needed, publicKeys are downloaded by sending request to realm's jwks_url
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class JWKPublicKeyLocator implements PublicKeyLocator {
private static final Logger log = Logger.getLogger(JWKPublicKeyLocator.class);
private Map<String, PublicKey> currentKeys = new ConcurrentHashMap<>();
private volatile int lastRequestTime = 0;
@Override
public PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment) {
String kid = input.getHeader().getKeyId();
return getPublicKey(kid, deployment);
}
private PublicKey getPublicKey(String kid, KeycloakDeployment deployment) {
int minTimeBetweenRequests = deployment.getMinTimeBetweenJwksRequests();
// Check if key is in cache.
PublicKey publicKey = currentKeys.get(kid);
if (publicKey != null) {
return publicKey;
}
int currentTime = Time.currentTime();
// Check if we are allowed to send request
if (currentTime > lastRequestTime + minTimeBetweenRequests) {
synchronized (this) {
currentTime = Time.currentTime();
if (currentTime > lastRequestTime + minTimeBetweenRequests) {
sendRequest(deployment);
lastRequestTime = currentTime;
} else {
// TODO: debug
log.infof("Won't send request to realm jwks url. Last request time was %d", lastRequestTime);
}
}
}
return currentKeys.get(kid);
}
private void sendRequest(KeycloakDeployment deployment) {
// Send the request
// TODO: trace or remove?
log.infof("Going to send request to retrieve new set of realm public keys for client %s", deployment.getResourceName());
HttpGet getMethod = new HttpGet(deployment.getJwksUrl());
try {
JSONWebKeySet jwks = HttpAdapterUtils.sendJsonHttpRequest(deployment, getMethod, JSONWebKeySet.class);
Map<String, PublicKey> publicKeys = JWKSUtils.getKeysForUse(jwks, JWK.Use.SIG);
// TODO: Debug with condition
log.infof("Realm public keys successfully retrieved for client %s. New kids: %s", deployment.getResourceName(), publicKeys.keySet().toString());
// Update current keys
currentKeys.clear();
currentKeys.putAll(publicKeys);
} catch (HttpClientAdapterException e) {
log.error("Error when sending request to retrieve realm keys", e);
}
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.adapters.rotation;
import java.security.PublicKey;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.jose.jws.JWSInput;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface PublicKeyLocator {
/**
* @param input
* @param deployment
* @return publicKey, which should be used for verify signature on given "input"
*/
PublicKey getPublicKey(JWSInput input, KeycloakDeployment deployment);
}

View file

@ -21,6 +21,8 @@ import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.junit.Test; import org.junit.Test;
import org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider; import org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider;
import org.keycloak.adapters.authentication.JWTClientCredentialsProvider; import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
import org.keycloak.adapters.rotation.HardcodedPublicKeyLocator;
import org.keycloak.adapters.rotation.JWKPublicKeyLocator;
import org.keycloak.common.enums.RelativeUrlsUsed; import org.keycloak.common.enums.RelativeUrlsUsed;
import org.keycloak.common.enums.SslRequired; import org.keycloak.common.enums.SslRequired;
import org.keycloak.enums.TokenStore; import org.keycloak.enums.TokenStore;
@ -39,7 +41,11 @@ public class KeycloakDeploymentBuilderTest {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak.json")); KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak.json"));
assertEquals("demo", deployment.getRealm()); assertEquals("demo", deployment.getRealm());
assertEquals("customer-portal", deployment.getResourceName()); assertEquals("customer-portal", deployment.getResourceName());
assertEquals(PemUtils.decodePublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"), deployment.getRealmKey());
assertTrue(deployment.getPublicKeyLocator() instanceof HardcodedPublicKeyLocator);
assertEquals(PemUtils.decodePublicKey("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"),
deployment.getPublicKeyLocator().getPublicKey(null, deployment));
assertEquals("https://localhost:8443/auth/realms/demo/protocol/openid-connect/auth", deployment.getAuthUrl().build().toString()); assertEquals("https://localhost:8443/auth/realms/demo/protocol/openid-connect/auth", deployment.getAuthUrl().build().toString());
assertEquals(SslRequired.EXTERNAL, deployment.getSslRequired()); assertEquals(SslRequired.EXTERNAL, deployment.getSslRequired());
assertTrue(deployment.isUseResourceRoleMappings()); assertTrue(deployment.isUseResourceRoleMappings());
@ -62,12 +68,16 @@ public class KeycloakDeploymentBuilderTest {
assertEquals(TokenStore.COOKIE, deployment.getTokenStore()); assertEquals(TokenStore.COOKIE, deployment.getTokenStore());
assertEquals("email", deployment.getPrincipalAttribute()); assertEquals("email", deployment.getPrincipalAttribute());
assertEquals(10, deployment.getTokenMinimumTimeToLive()); assertEquals(10, deployment.getTokenMinimumTimeToLive());
assertEquals(20, deployment.getMinTimeBetweenJwksRequests());
} }
@Test @Test
public void loadNoClientCredentials() throws Exception { public void loadNoClientCredentials() throws Exception {
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak-no-credentials.json")); KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak-no-credentials.json"));
assertEquals(ClientIdAndSecretCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId()); assertEquals(ClientIdAndSecretCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId());
assertTrue(deployment.getPublicKeyLocator() instanceof JWKPublicKeyLocator);
assertEquals(10, deployment.getMinTimeBetweenJwksRequests());
} }
@Test @Test

View file

@ -1,7 +1,6 @@
{ {
"realm": "demo", "realm": "demo",
"resource": "customer-portal", "resource": "customer-portal",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "https://localhost:8443/auth", "auth-server-url": "https://localhost:8443/auth",
"public-client": true, "public-client": true,
"expose-token": true "expose-token": true

View file

@ -29,5 +29,6 @@
"register-node-period": 1000, "register-node-period": 1000,
"token-store": "cookie", "token-store": "cookie",
"principal-attribute": "email", "principal-attribute": "email",
"token-minimum-time-to-live": 10 "token-minimum-time-to-live": 10,
"min-time-between-jwks-requests": 20
} }

View file

@ -20,6 +20,7 @@ package org.keycloak.adapters.installed;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException; import org.keycloak.OAuthErrorException;
import org.keycloak.RSATokenVerifier; import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder; import org.keycloak.adapters.KeycloakDeploymentBuilder;
@ -213,7 +214,7 @@ public class KeycloakInstalled {
refreshToken = tokenResponse.getRefreshToken(); refreshToken = tokenResponse.getRefreshToken();
idTokenString = tokenResponse.getIdToken(); idTokenString = tokenResponse.getIdToken();
token = RSATokenVerifier.verifyToken(tokenString, deployment.getRealmKey(), deployment.getRealmInfoUrl()); token = AdapterRSATokenVerifier.verifyToken(tokenString, deployment);
if (idTokenString != null) { if (idTokenString != null) {
try { try {
JWSInput input = new JWSInput(idTokenString); JWSInput input = new JWSInput(idTokenString);

View file

@ -155,7 +155,6 @@
} else if (initOptions) { } else if (initOptions) {
if (initOptions.token || initOptions.refreshToken) { if (initOptions.token || initOptions.refreshToken) {
setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken); setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
kc.timeSkew = initOptions.timeSkew || 0;
if (loginIframe.enable) { if (loginIframe.enable) {
setupCheckLoginIframe().success(function() { setupCheckLoginIframe().success(function() {
@ -166,6 +165,8 @@
kc.onAuthError && kc.onAuthError(); kc.onAuthError && kc.onAuthError();
if (initOptions.onLoad) { if (initOptions.onLoad) {
onLoad(); onLoad();
} else {
initPromise.setError();
} }
}); });
}); });
@ -177,6 +178,8 @@
kc.onAuthError && kc.onAuthError(); kc.onAuthError && kc.onAuthError();
if (initOptions.onLoad) { if (initOptions.onLoad) {
onLoad(); onLoad();
} else {
initPromise.setError();
} }
}); });
} }
@ -359,11 +362,10 @@
throw 'Not authenticated'; throw 'Not authenticated';
} }
var expiresIn = kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew; var expiresIn = kc.tokenParsed['exp'] - Math.ceil(new Date().getTime() / 1000) + kc.timeSkew;
if (minValidity) { if (minValidity) {
expiresIn -= minValidity; expiresIn -= minValidity;
} }
return expiresIn < 0; return expiresIn < 0;
} }
@ -378,7 +380,20 @@
minValidity = minValidity || 5; minValidity = minValidity || 5;
var exec = function() { var exec = function() {
if (minValidity >= 0 && !kc.isTokenExpired(minValidity)) { var refreshToken = false;
if (kc.timeSkew == -1) {
console.info('Skew ' + kc.timeSkew);
refreshToken = true;
console.info('[KEYCLOAK] Refreshing token: time skew not set');
} else if (minValidity == -1) {
refreshToken = true;
console.info('[KEYCLOAK] Refreshing token: forced refresh');
} else if (kc.isTokenExpired(minValidity)) {
refreshToken = true;
console.info('[KEYCLOAK] Refreshing token: token expired');
}
if (!refreshToken) {
promise.setSuccess(false); promise.setSuccess(false);
} else { } else {
var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken; var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
@ -403,18 +418,21 @@
req.onreadystatechange = function () { req.onreadystatechange = function () {
if (req.readyState == 4) { if (req.readyState == 4) {
if (req.status == 200) { if (req.status == 200) {
console.info('[KEYCLOAK] Token refreshed');
timeLocal = (timeLocal + new Date().getTime()) / 2; timeLocal = (timeLocal + new Date().getTime()) / 2;
var tokenResponse = JSON.parse(req.responseText); var tokenResponse = JSON.parse(req.responseText);
kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat;
setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']); setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], timeLocal);
kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess(); kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) { for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
p.setSuccess(true); p.setSuccess(true);
} }
} else { } else {
console.warn('[KEYCLOAK] Failed to refresh token');
kc.onAuthRefreshError && kc.onAuthRefreshError(); kc.onAuthRefreshError && kc.onAuthRefreshError();
for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) { for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
p.setError(true); p.setError(true);
@ -525,18 +543,16 @@
function authSuccess(accessToken, refreshToken, idToken, fulfillPromise) { function authSuccess(accessToken, refreshToken, idToken, fulfillPromise) {
timeLocal = (timeLocal + new Date().getTime()) / 2; timeLocal = (timeLocal + new Date().getTime()) / 2;
setToken(accessToken, refreshToken, idToken); setToken(accessToken, refreshToken, idToken, timeLocal);
if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) || if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
(kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) || (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
(kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) { (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {
console.log('invalid nonce!'); console.info('[KEYCLOAK] Invalid nonce, clearing token');
kc.clearToken(); kc.clearToken();
promise && promise.setError(); promise && promise.setError();
} else { } else {
kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat;
if (fulfillPromise) { if (fulfillPromise) {
kc.onAuthSuccess && kc.onAuthSuccess(); kc.onAuthSuccess && kc.onAuthSuccess();
promise && promise.setSuccess(); promise && promise.setSuccess();
@ -609,7 +625,7 @@
return promise.promise; return promise.promise;
} }
function setToken(token, refreshToken, idToken) { function setToken(token, refreshToken, idToken, timeLocal) {
if (kc.tokenTimeoutHandle) { if (kc.tokenTimeoutHandle) {
clearTimeout(kc.tokenTimeoutHandle); clearTimeout(kc.tokenTimeoutHandle);
kc.tokenTimeoutHandle = null; kc.tokenTimeoutHandle = null;
@ -628,12 +644,23 @@
kc.realmAccess = kc.tokenParsed.realm_access; kc.realmAccess = kc.tokenParsed.realm_access;
kc.resourceAccess = kc.tokenParsed.resource_access; kc.resourceAccess = kc.tokenParsed.resource_access;
if (timeLocal) {
kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat;
console.info('[KEYCLOAK] Estimated time difference between browser and server is ' + kc.timeSkew + ' seconds');
} else {
kc.timeSkew = -1;
}
if (kc.onTokenExpired) { if (kc.onTokenExpired) {
var expiresIn = (kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew) * 1000; if (kc.timeSkew == -1) {
if (expiresIn <= 0) {
kc.onTokenExpired(); kc.onTokenExpired();
} else { } else {
kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn); var expiresIn = (kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew) * 1000;
if (expiresIn <= 0) {
kc.onTokenExpired();
} else {
kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn);
}
} }
} }

View file

@ -69,6 +69,5 @@ public class HttpSessionManager implements HttpSessionListener, UserSessionManag
session.invalidate(); session.invalidate();
} }
} }
sessions.clear();
} }
} }

View file

@ -0,0 +1,69 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.common;
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Profile {
private enum ProfileValue {
PRODUCT, PREVIEW, COMMUNITY
}
private static ProfileValue value = load();
static ProfileValue load() {
String profile = null;
try {
profile = System.getProperty("keycloak.profile");
if (profile == null) {
String jbossServerConfigDir = System.getProperty("jboss.server.config.dir");
if (jbossServerConfigDir != null) {
File file = new File(jbossServerConfigDir, "profile.properties");
if (file.isFile()) {
Properties props = new Properties();
props.load(new FileInputStream(file));
profile = props.getProperty("profile");
}
}
}
} catch (Exception e) {
}
if (profile == null) {
return ProfileValue.valueOf(Version.DEFAULT_PROFILE.toUpperCase());
} else {
return ProfileValue.valueOf(profile.toUpperCase());
}
}
public static String getName() {
return value.name().toLowerCase();
}
public static boolean isPreviewEnabled() {
return value.ordinal() >= ProfileValue.PREVIEW.ordinal();
}
}

View file

@ -32,6 +32,7 @@ public class Version {
public static String VERSION; public static String VERSION;
public static String RESOURCES_VERSION; public static String RESOURCES_VERSION;
public static String BUILD_TIME; public static String BUILD_TIME;
public static String DEFAULT_PROFILE;
static { static {
Properties props = new Properties(); Properties props = new Properties();
@ -40,6 +41,7 @@ public class Version {
props.load(is); props.load(is);
Version.NAME = props.getProperty("name"); Version.NAME = props.getProperty("name");
Version.NAME_HTML = props.getProperty("name-html"); Version.NAME_HTML = props.getProperty("name-html");
Version.DEFAULT_PROFILE = props.getProperty("default-profile");
Version.VERSION = props.getProperty("version"); Version.VERSION = props.getProperty("version");
Version.BUILD_TIME = props.getProperty("build-time"); Version.BUILD_TIME = props.getProperty("build-time");
Version.RESOURCES_VERSION = Version.VERSION.toLowerCase(); Version.RESOURCES_VERSION = Version.VERSION.toLowerCase();

View file

@ -18,4 +18,5 @@
name=${product.name} name=${product.name}
name-html=${product.name-html} name-html=${product.name-html}
version=${product.version} version=${product.version}
build-time=${product.build-time} build-time=${product.build-time}
default-profile=${product.default-profile}

View file

@ -38,6 +38,12 @@ public class RSATokenVerifier {
public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException { public static AccessToken verifyToken(String tokenString, PublicKey realmKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException {
AccessToken token = toAccessToken(tokenString, realmKey); AccessToken token = toAccessToken(tokenString, realmKey);
tokenVerifications(token, realmUrl, checkActive, checkTokenType);
return token;
}
private static void tokenVerifications(AccessToken token, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException {
String user = token.getSubject(); String user = token.getSubject();
if (user == null) { if (user == null) {
throw new VerificationException("Token user was null."); throw new VerificationException("Token user was null.");
@ -60,9 +66,9 @@ public class RSATokenVerifier {
throw new VerificationException("Token is not active."); throw new VerificationException("Token is not active.");
} }
return token;
} }
public static AccessToken toAccessToken(String tokenString, PublicKey realmKey) throws VerificationException { public static AccessToken toAccessToken(String tokenString, PublicKey realmKey) throws VerificationException {
JWSInput input; JWSInput input;
try { try {
@ -81,6 +87,23 @@ public class RSATokenVerifier {
return token; return token;
} }
public static AccessToken verifyToken(JWSInput input, PublicKey realmKey, String realmUrl, boolean checkActive, boolean checkTokenType) throws VerificationException {
if (!isPublicKeyValid(input, realmKey)) throw new VerificationException("Invalid token signature.");
AccessToken token;
try {
token = input.readJsonContent(AccessToken.class);
} catch (JWSInputException e) {
throw new VerificationException("Couldn't parse token signature", e);
}
tokenVerifications(token, realmUrl, checkActive, checkTokenType);
return token;
}
private static boolean isPublicKeyValid(JWSInput input, PublicKey realmKey) throws VerificationException { private static boolean isPublicKeyValid(JWSInput input, PublicKey realmKey) throws VerificationException {
try { try {
return RSAProvider.verify(input, realmKey); return RSAProvider.verify(input, realmKey);

View file

@ -30,5 +30,6 @@ public interface ServiceUrlConstants {
public static final String REALM_INFO_PATH = "/realms/{realm-name}"; public static final String REALM_INFO_PATH = "/realms/{realm-name}";
public static final String CLIENTS_MANAGEMENT_REGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/register-node"; public static final String CLIENTS_MANAGEMENT_REGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/register-node";
public static final String CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/unregister-node"; public static final String CLIENTS_MANAGEMENT_UNREGISTER_NODE_PATH = "/realms/{realm-name}/clients-managements/unregister-node";
public static final String JWKS_URL = "/realms/{realm-name}/protocol/openid-connect/certs";
} }

View file

@ -21,7 +21,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
import org.keycloak.common.util.Base64Url; import org.keycloak.common.util.Base64Url;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import java.io.InputStream;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.PublicKey; import java.security.PublicKey;
@ -66,8 +65,8 @@ public class JWKParser {
} }
public PublicKey toPublicKey() { public PublicKey toPublicKey() {
String algorithm = jwk.getKeyType(); String keyType = jwk.getKeyType();
if (isAlgorithmSupported(algorithm)) { if (isKeyTypeSupported(keyType)) {
BigInteger modulus = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.MODULUS).toString())); BigInteger modulus = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.MODULUS).toString()));
BigInteger publicExponent = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.PUBLIC_EXPONENT).toString())); BigInteger publicExponent = new BigInteger(1, Base64Url.decode(jwk.getOtherClaims().get(RSAPublicJWK.PUBLIC_EXPONENT).toString()));
@ -77,12 +76,12 @@ public class JWKParser {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} else { } else {
throw new RuntimeException("Unsupported algorithm " + algorithm); throw new RuntimeException("Unsupported keyType " + keyType);
} }
} }
public boolean isAlgorithmSupported(String algorithm) { public boolean isKeyTypeSupported(String keyType) {
return RSAPublicJWK.RSA.equals(algorithm); return RSAPublicJWK.RSA.equals(keyType);
} }
} }

View file

@ -36,7 +36,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
"client-keystore", "client-keystore-password", "client-key-password", "client-keystore", "client-keystore-password", "client-key-password",
"always-refresh-token", "always-refresh-token",
"register-node-at-startup", "register-node-period", "token-store", "principal-attribute", "register-node-at-startup", "register-node-period", "token-store", "principal-attribute",
"proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live", "proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live", "min-time-between-jwks-requests",
"policy-enforcer" "policy-enforcer"
}) })
public class AdapterConfig extends BaseAdapterConfig { public class AdapterConfig extends BaseAdapterConfig {
@ -71,6 +71,8 @@ public class AdapterConfig extends BaseAdapterConfig {
protected Boolean turnOffChangeSessionIdOnLogin; protected Boolean turnOffChangeSessionIdOnLogin;
@JsonProperty("token-minimum-time-to-live") @JsonProperty("token-minimum-time-to-live")
protected int tokenMinimumTimeToLive = 0; protected int tokenMinimumTimeToLive = 0;
@JsonProperty("min-time-between-jwks-requests")
protected int minTimeBetweenJwksRequests = 10;
@JsonProperty("policy-enforcer") @JsonProperty("policy-enforcer")
protected PolicyEnforcerConfig policyEnforcerConfig; protected PolicyEnforcerConfig policyEnforcerConfig;
@ -216,4 +218,11 @@ public class AdapterConfig extends BaseAdapterConfig {
this.tokenMinimumTimeToLive = tokenMinimumTimeToLive; this.tokenMinimumTimeToLive = tokenMinimumTimeToLive;
} }
public int getMinTimeBetweenJwksRequests() {
return minTimeBetweenJwksRequests;
}
public void setMinTimeBetweenJwksRequests(int minTimeBetweenJwksRequests) {
this.minTimeBetweenJwksRequests = minTimeBetweenJwksRequests;
}
} }

View file

@ -123,10 +123,15 @@ public class IdentityProviderRepresentation {
this.updateProfileFirstLoginMode = updateProfileFirstLoginMode; this.updateProfileFirstLoginMode = updateProfileFirstLoginMode;
} }
/**
* @deprecated Replaced by configuration option in identity provider authenticator
*/
@Deprecated
public boolean isAuthenticateByDefault() { public boolean isAuthenticateByDefault() {
return authenticateByDefault; return authenticateByDefault;
} }
@Deprecated
public void setAuthenticateByDefault(boolean authenticateByDefault) { public void setAuthenticateByDefault(boolean authenticateByDefault) {
this.authenticateByDefault = authenticateByDefault; this.authenticateByDefault = authenticateByDefault;
} }

View file

@ -123,6 +123,8 @@ public class RealmRepresentation {
protected String resetCredentialsFlow; protected String resetCredentialsFlow;
protected String clientAuthenticationFlow; protected String clientAuthenticationFlow;
protected Map<String, String> attributes;
protected String keycloakVersion; protected String keycloakVersion;
@Deprecated @Deprecated
@ -864,4 +866,12 @@ public class RealmRepresentation {
return identityProviders != null && !identityProviders.isEmpty(); return identityProviders != null && !identityProviders.isEmpty();
} }
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
public Map<String, String> getAttributes() {
return attributes;
}
} }

View file

@ -123,10 +123,12 @@ public class UserRepresentation {
this.enabled = enabled; this.enabled = enabled;
} }
@Deprecated
public Boolean isTotp() { public Boolean isTotp() {
return totp; return totp;
} }
@Deprecated
public void setTotp(Boolean totp) { public void setTotp(Boolean totp) {
this.totp = totp; this.totp = totp;
} }

View file

@ -0,0 +1,53 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.representations.info;
import org.keycloak.common.Profile;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ProfileInfoRepresentation {
private String name;
private boolean previewEnabled;
public static ProfileInfoRepresentation create() {
ProfileInfoRepresentation info = new ProfileInfoRepresentation();
info.setName(Profile.getName());
info.setPreviewEnabled(Profile.isPreviewEnabled());
return info;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isPreviewEnabled() {
return previewEnabled;
}
public void setPreviewEnabled(boolean previewEnabled) {
this.previewEnabled = previewEnabled;
}
}

View file

@ -32,6 +32,7 @@ public class ServerInfoRepresentation {
private SystemInfoRepresentation systemInfo; private SystemInfoRepresentation systemInfo;
private MemoryInfoRepresentation memoryInfo; private MemoryInfoRepresentation memoryInfo;
private ProfileInfoRepresentation profileInfo;
private Map<String, List<ThemeInfoRepresentation>> themes; private Map<String, List<ThemeInfoRepresentation>> themes;
@ -66,6 +67,14 @@ public class ServerInfoRepresentation {
this.memoryInfo = memoryInfo; this.memoryInfo = memoryInfo;
} }
public ProfileInfoRepresentation getProfileInfo() {
return profileInfo;
}
public void setProfileInfo(ProfileInfoRepresentation profileInfo) {
this.profileInfo = profileInfo;
}
public Map<String, List<ThemeInfoRepresentation>> getThemes() { public Map<String, List<ThemeInfoRepresentation>> getThemes() {
return themes; return themes;
} }

View file

@ -0,0 +1,45 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.util;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
import org.keycloak.jose.jwk.JSONWebKeySet;
import org.keycloak.jose.jwk.JWK;
import org.keycloak.jose.jwk.JWKParser;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class JWKSUtils {
public static Map<String, PublicKey> getKeysForUse(JSONWebKeySet keySet, JWK.Use requestedUse) {
Map<String, PublicKey> result = new HashMap<>();
for (JWK jwk : keySet.getKeys()) {
JWKParser parser = JWKParser.create(jwk);
if (jwk.getPublicKeyUse().equals(requestedUse.asString()) && parser.isKeyTypeSupported(jwk.getKeyType())) {
result.put(jwk.getKeyId(), parser.toPublicKey());
}
}
return result;
}
}

View file

@ -17,17 +17,6 @@
package org.keycloak; package org.keycloak;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
@ -36,6 +25,13 @@ import org.keycloak.representations.adapters.config.AdapterConfig;
import org.keycloak.representations.oidc.OIDCClientRepresentation; import org.keycloak.representations.oidc.OIDCClientRepresentation;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
@ -138,6 +134,18 @@ public class JsonParserTest {
Assert.assertNull(clientRep.getJwks()); Assert.assertNull(clientRep.getJwks());
} }
@Test
public void testReadOIDCClientRepWithPairwise() throws IOException {
String stringRep = "{\"subject_type\": \"pairwise\", \"jwks_uri\": \"https://op.certification.openid.net:60720/export/jwk_60720.json\", \"contacts\": [\"roland.hedberg@umu.se\"], \"application_type\": \"web\", \"grant_types\": [\"authorization_code\"], \"post_logout_redirect_uris\": [\"https://op.certification.openid.net:60720/logout\"], \"redirect_uris\": [\"https://op.certification.openid.net:60720/authz_cb\"], \"response_types\": [\"code\"], \"require_auth_time\": true, \"default_max_age\": 3600}";
OIDCClientRepresentation clientRep = JsonSerialization.readValue(stringRep, OIDCClientRepresentation.class);
Assert.assertEquals("pairwise", clientRep.getSubjectType());
Assert.assertTrue(clientRep.getRequireAuthTime());
Assert.assertEquals(3600, clientRep.getDefaultMaxAge().intValue());
Assert.assertEquals(1, clientRep.getRedirectUris().size());
Assert.assertEquals("https://op.certification.openid.net:60720/authz_cb", clientRep.getRedirectUris().get(0));
Assert.assertNull(clientRep.getJwks());
}
@Test @Test
public void testReadOIDCClientRepWithJWKS() throws IOException { public void testReadOIDCClientRepWithJWKS() throws IOException {
String stringRep = "{\"token_endpoint_auth_method\": \"private_key_jwt\", \"subject_type\": \"public\", \"jwks_uri\": null, \"jwks\": {\"keys\": [{\"use\": \"enc\", \"e\": \"AQAB\", \"d\": \"lZQv0_81euRLeUYU84Aodh0ar7ymDlzWP5NMra4Jklkb-lTBWkI-u4RMsPqGYyW3KHRoL_pgzZXSzQx8RLQfER6timRWb--NxMMKllZubByU3RqH2ooNuocJurspYiXkznPW1Mg9DaNXL0C2hwWPQHTeUVISpjgi5TCOV1ccWVyksFruya_VNL1CIByB-L0GL1rqbKv32cDwi2A3_jJa61cpzfLSIBe-lvCO6tuiDsR4qgJnUwnndQFwEI_4mLmD3iNWXrc8N-poleV8mBfMqBB5fWwy_ZTFCpmQ5AywGmctaik_wNhMoWuA4tUfY6_1LdKld-5Cjq55eLtuJjtvuQ\", \"n\": \"tx3Hjdbc19lkTiohbJrNj4jf2_90MEE122CRrwtFu6saDywKcG7Bi7w2FMAK2oTkuWfqhWRb5BEGmnSXdiCEPO5d-ytqP3nwlZXHaCDYscpP8bB4YLhvCn7R8Efw6gwQle24QPRP3lYoFeuUbDUq7GKA5SfaZUvWoeWjqyLIaBspKQsC26_Umx1E4IXLrMSL6nkRnrYcVZBAXrYCeTP1XtsV38_lZVJfHSaJaUy4PKaj3yvgm93EV2CXybPti7CCMXZ34VqqWiF64pQjZsPu3ZTr7ha_TTQq499-zYRQNDvIVsBDLQQIgrbctuGqj6lrXb31Jj3JIEYqH_4h5X9d0Q\", \"q\": \"1q-r-bmMFbIzrLK2U3elksZq8CqUqZxlSfkGMZuVkxgYMS-e4FPzEp2iirG-eO11aa0cpMMoBdTnVdGJ_ZUR93w0lGf9XnQAJqxP7eOsrUoiW4VWlWH4WfOiLgpO-pFtyTz_JksYYaotc_Z3Zy-Szw6a39IDbuYGy1qL-15oQuc\", \"p\": \"2lrYPppRbcQWu4LtWN6tOVUrtCOPv1eLTKTc7q8vCMcem1Ox5QFB7KnUtNZ5Ni7wnZUeVDfimNebtjNsGvDSrpgIlo9dEnFBQsQIkzZ2SkoYfgmF8hNdi6P-BfRjdgYouy4c6xAnGDgSMTip1YnPRyvbMaoYT9E_tEcBW5wOeoc\", \"kid\": \"a0\", \"kty\": \"RSA\"}, {\"use\": \"sig\", \"e\": \"AQAB\", \"d\": \"DodXDEtkovWWGsMEXYy_nEEMCWyROMOebCnCv0ey3i4M4bh2dmwqgz0e-IKQAFlGiMkidGL1lNbq0uFS04FbuRAR06dYw1cbrNbDdhrWFxKTd1L5D9p-x-gW-YDWhpI8rUGRa76JXkOSxZUbg09_QyUd99CXAHh-FXi_ZkIKD8hK6FrAs68qhLf8MNkUv63DTduw7QgeFfQivdopePxyGuMk5n8veqwsUZsklQkhNlTYQqeM1xb2698ZQcNYkl0OssEsSJKRjXt-LRPowKrdvTuTo2p--HMI0pIEeFs7H_u5OW3jihjvoFClGPynHQhgWmQzlQRvWRXh6FhDVqFeGQ\", \"n\": \"zfZzttF7HmnTYwSMPdxKs5AoczbNS2mOPz-tN1g4ljqI_F1DG8cgQDcN_VDufxoFGRERo2FK6WEN41LhbGEyP6uL6wW6Cy29qE9QZcvY5mXrncndRSOkNcMizvuEJes_fMYrmP_lPiC6kWiqItTk9QBWqJfiYKhCx9cSDXsBmJXn3KWQCVHvj1ANFWW0CWLMKlWN-_NMNLIWJN_pEAocTZMzxSFBK1b5_5J8ZS7hfWRF6MQmjsJcz2jzA21SQZNpre3kwnTGRSwo05sAS-TyeadDqQPWgbqX69UzcGq5irhzN8cpZ_JaTk3Y_uV6owanTZLVvCgdjaAnMYeZhb0KFw\", \"q\": \"5E5XKK5njT-zzRqqTeY2tgP9PJBACeaH_xQRHZ_1ydE7tVd7HdgdaEHfQ1jvKIHFkknWWOBAY1mlBc4YDirLShB_voShD8C-Hx3nF5sne5fleVfU-sZy6Za4B2U75PcE62oZgCPauOTAEm9Xuvrt5aMMovyzR8ecJZhm9bw7naU\", \"p\": \"5vJHCSM3H3q4RltYzENC9RyZZV8EUmpkv9moyguT5t-BUGA-T4W_FGIxzOPXRWOckIplKkoDKhavUeNmTZMCUcue0nkICSJpvNE4Nb2p5PZk_QqSdQNvCasQtdojEG0AmfVD85SU551CYxJdLdDFOqyK2entpMr8lhokem189As\", \"kid\": \"a1\", \"kty\": \"RSA\"}, {\"d\": \"S4_OufhLBgXFMgIDMI1zlVe2uCExpcEAQ80J_lXfS8I\", \"use\": \"sig\", \"crv\": \"P-256\", \"kty\": \"EC\", \"y\": \"DBdNyq30mXmUs_BIvKMqaTTNO7HDhCi0YiC8GciwNYk\", \"x\": \"cYwzBoyjRjxj334bRTqanONf7DUYK-6TgiuN0DixJAk\", \"kid\": \"a2\"}, {\"d\": \"33TnYgdJtWAiVosKqUnz0zSmvWTbsx5-6pceynW6Xck\", \"use\": \"enc\", \"crv\": \"P-256\", \"kty\": \"EC\", \"y\": \"Cula95Eix1Ia77St3OULe6-UKWs5I06nmdfUzhXUQTs\", \"x\": \"wk8HBVxNNzj1gJBxPmmx9XYW1L61ObBGzxpRa6_OqWU\", \"kid\": \"a3\"}]}, \"application_type\": \"web\", \"contacts\": [\"roland.hedberg@umu.se\"], \"post_logout_redirect_uris\": [\"https://op.certification.openid.net:60784/logout\"], \"redirect_uris\": [\"https://op.certification.openid.net:60784/authz_cb\"], \"response_types\": [\"code\"], \"require_auth_time\": true, \"grant_types\": [\"authorization_code\"], \"default_max_age\": 3600}"; String stringRep = "{\"token_endpoint_auth_method\": \"private_key_jwt\", \"subject_type\": \"public\", \"jwks_uri\": null, \"jwks\": {\"keys\": [{\"use\": \"enc\", \"e\": \"AQAB\", \"d\": \"lZQv0_81euRLeUYU84Aodh0ar7ymDlzWP5NMra4Jklkb-lTBWkI-u4RMsPqGYyW3KHRoL_pgzZXSzQx8RLQfER6timRWb--NxMMKllZubByU3RqH2ooNuocJurspYiXkznPW1Mg9DaNXL0C2hwWPQHTeUVISpjgi5TCOV1ccWVyksFruya_VNL1CIByB-L0GL1rqbKv32cDwi2A3_jJa61cpzfLSIBe-lvCO6tuiDsR4qgJnUwnndQFwEI_4mLmD3iNWXrc8N-poleV8mBfMqBB5fWwy_ZTFCpmQ5AywGmctaik_wNhMoWuA4tUfY6_1LdKld-5Cjq55eLtuJjtvuQ\", \"n\": \"tx3Hjdbc19lkTiohbJrNj4jf2_90MEE122CRrwtFu6saDywKcG7Bi7w2FMAK2oTkuWfqhWRb5BEGmnSXdiCEPO5d-ytqP3nwlZXHaCDYscpP8bB4YLhvCn7R8Efw6gwQle24QPRP3lYoFeuUbDUq7GKA5SfaZUvWoeWjqyLIaBspKQsC26_Umx1E4IXLrMSL6nkRnrYcVZBAXrYCeTP1XtsV38_lZVJfHSaJaUy4PKaj3yvgm93EV2CXybPti7CCMXZ34VqqWiF64pQjZsPu3ZTr7ha_TTQq499-zYRQNDvIVsBDLQQIgrbctuGqj6lrXb31Jj3JIEYqH_4h5X9d0Q\", \"q\": \"1q-r-bmMFbIzrLK2U3elksZq8CqUqZxlSfkGMZuVkxgYMS-e4FPzEp2iirG-eO11aa0cpMMoBdTnVdGJ_ZUR93w0lGf9XnQAJqxP7eOsrUoiW4VWlWH4WfOiLgpO-pFtyTz_JksYYaotc_Z3Zy-Szw6a39IDbuYGy1qL-15oQuc\", \"p\": \"2lrYPppRbcQWu4LtWN6tOVUrtCOPv1eLTKTc7q8vCMcem1Ox5QFB7KnUtNZ5Ni7wnZUeVDfimNebtjNsGvDSrpgIlo9dEnFBQsQIkzZ2SkoYfgmF8hNdi6P-BfRjdgYouy4c6xAnGDgSMTip1YnPRyvbMaoYT9E_tEcBW5wOeoc\", \"kid\": \"a0\", \"kty\": \"RSA\"}, {\"use\": \"sig\", \"e\": \"AQAB\", \"d\": \"DodXDEtkovWWGsMEXYy_nEEMCWyROMOebCnCv0ey3i4M4bh2dmwqgz0e-IKQAFlGiMkidGL1lNbq0uFS04FbuRAR06dYw1cbrNbDdhrWFxKTd1L5D9p-x-gW-YDWhpI8rUGRa76JXkOSxZUbg09_QyUd99CXAHh-FXi_ZkIKD8hK6FrAs68qhLf8MNkUv63DTduw7QgeFfQivdopePxyGuMk5n8veqwsUZsklQkhNlTYQqeM1xb2698ZQcNYkl0OssEsSJKRjXt-LRPowKrdvTuTo2p--HMI0pIEeFs7H_u5OW3jihjvoFClGPynHQhgWmQzlQRvWRXh6FhDVqFeGQ\", \"n\": \"zfZzttF7HmnTYwSMPdxKs5AoczbNS2mOPz-tN1g4ljqI_F1DG8cgQDcN_VDufxoFGRERo2FK6WEN41LhbGEyP6uL6wW6Cy29qE9QZcvY5mXrncndRSOkNcMizvuEJes_fMYrmP_lPiC6kWiqItTk9QBWqJfiYKhCx9cSDXsBmJXn3KWQCVHvj1ANFWW0CWLMKlWN-_NMNLIWJN_pEAocTZMzxSFBK1b5_5J8ZS7hfWRF6MQmjsJcz2jzA21SQZNpre3kwnTGRSwo05sAS-TyeadDqQPWgbqX69UzcGq5irhzN8cpZ_JaTk3Y_uV6owanTZLVvCgdjaAnMYeZhb0KFw\", \"q\": \"5E5XKK5njT-zzRqqTeY2tgP9PJBACeaH_xQRHZ_1ydE7tVd7HdgdaEHfQ1jvKIHFkknWWOBAY1mlBc4YDirLShB_voShD8C-Hx3nF5sne5fleVfU-sZy6Za4B2U75PcE62oZgCPauOTAEm9Xuvrt5aMMovyzR8ecJZhm9bw7naU\", \"p\": \"5vJHCSM3H3q4RltYzENC9RyZZV8EUmpkv9moyguT5t-BUGA-T4W_FGIxzOPXRWOckIplKkoDKhavUeNmTZMCUcue0nkICSJpvNE4Nb2p5PZk_QqSdQNvCasQtdojEG0AmfVD85SU551CYxJdLdDFOqyK2entpMr8lhokem189As\", \"kid\": \"a1\", \"kty\": \"RSA\"}, {\"d\": \"S4_OufhLBgXFMgIDMI1zlVe2uCExpcEAQ80J_lXfS8I\", \"use\": \"sig\", \"crv\": \"P-256\", \"kty\": \"EC\", \"y\": \"DBdNyq30mXmUs_BIvKMqaTTNO7HDhCi0YiC8GciwNYk\", \"x\": \"cYwzBoyjRjxj334bRTqanONf7DUYK-6TgiuN0DixJAk\", \"kid\": \"a2\"}, {\"d\": \"33TnYgdJtWAiVosKqUnz0zSmvWTbsx5-6pceynW6Xck\", \"use\": \"enc\", \"crv\": \"P-256\", \"kty\": \"EC\", \"y\": \"Cula95Eix1Ia77St3OULe6-UKWs5I06nmdfUzhXUQTs\", \"x\": \"wk8HBVxNNzj1gJBxPmmx9XYW1L61ObBGzxpRa6_OqWU\", \"kid\": \"a3\"}]}, \"application_type\": \"web\", \"contacts\": [\"roland.hedberg@umu.se\"], \"post_logout_redirect_uris\": [\"https://op.certification.openid.net:60784/logout\"], \"redirect_uris\": [\"https://op.certification.openid.net:60784/authz_cb\"], \"response_types\": [\"code\"], \"require_auth_time\": true, \"grant_types\": [\"authorization_code\"], \"default_max_age\": 3600}";

View file

@ -112,12 +112,6 @@
<type>zip</type> <type>zip</type>
<destFileName>keycloak-examples-${project.version}.zip</destFileName> <destFileName>keycloak-examples-${project.version}.zip</destFileName>
</artifactItem> </artifactItem>
<artifactItem>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-src-dist</artifactId>
<type>zip</type>
<destFileName>keycloak-src-${project.version}.zip</destFileName>
</artifactItem>
</artifactItems> </artifactItems>
<outputDirectory>target/${project.version}</outputDirectory> <outputDirectory>target/${project.version}</outputDirectory>
</configuration> </configuration>

View file

@ -40,7 +40,6 @@
<module>proxy-dist</module> <module>proxy-dist</module>
<module>server-dist</module> <module>server-dist</module>
<module>server-overlay</module> <module>server-overlay</module>
<module>src-dist</module>
<module>feature-packs</module> <module>feature-packs</module>
</modules> </modules>

View file

@ -1,69 +0,0 @@
<!--
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
~ and other contributors as indicated by the @author tags.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>keycloak-distribution-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>2.2.0-SNAPSHOT</version>
</parent>
<artifactId>keycloak-src-dist</artifactId>
<packaging>pom</packaging>
<name>Keycloak Source Distribution</name>
<description/>
<build>
<finalName>keycloak-src-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>assembly.xml</descriptor>
</descriptors>
<outputDirectory>
target
</outputDirectory>
<workDirectory>
target/assembly/work
</workDirectory>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -1,6 +1,5 @@
{ {
"realm": "hello-world-authz", "realm": "hello-world-authz",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth", "auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external", "ssl-required": "external",
"resource": "hello-world-authz-service", "resource": "hello-world-authz-service",

View file

@ -43,7 +43,7 @@ public class AuthorizationClientExample {
} }
private static void introspectRequestingPartyToken() { private static void introspectRequestingPartyToken() {
// create a new instance based on the configuration define at keycloak-authz.json // create a new instance based on the configuration defined in keycloak-authz.json
AuthzClient authzClient = AuthzClient.create(); AuthzClient authzClient = AuthzClient.create();
// query the server for a resource with a given name // query the server for a resource with a given name
@ -51,8 +51,9 @@ public class AuthorizationClientExample {
.resource() .resource()
.findByFilter("name=Default Resource"); .findByFilter("name=Default Resource");
// obtian a Entitlement API Token in order to get access to the Entitlement API. // obtain an Entitlement API Token in order to get access to the Entitlement API.
// this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement // this token is just an access token issued to a client on behalf of an user
// with a scope = kc_entitlement
String eat = getEntitlementAPIToken(authzClient); String eat = getEntitlementAPIToken(authzClient);
// create an entitlement request // create an entitlement request
@ -63,7 +64,8 @@ public class AuthorizationClientExample {
request.addPermission(permission); request.addPermission(permission);
// send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user // send the entitlement request to the server in order to
// obtain an RPT with all permissions granted to the user
EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request); EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request);
String rpt = response.getRpt(); String rpt = response.getRpt();
@ -79,7 +81,7 @@ public class AuthorizationClientExample {
} }
private static void createResource() { private static void createResource() {
// create a new instance based on the configuration define at keycloak-authz.json // create a new instance based on the configuration defined in keycloak-authz.json
AuthzClient authzClient = AuthzClient.create(); AuthzClient authzClient = AuthzClient.create();
// create a new resource representation with the information we want // create a new resource representation with the information we want
@ -111,8 +113,9 @@ public class AuthorizationClientExample {
// create a new instance based on the configuration define at keycloak-authz.json // create a new instance based on the configuration define at keycloak-authz.json
AuthzClient authzClient = AuthzClient.create(); AuthzClient authzClient = AuthzClient.create();
// obtian a Entitlement API Token in order to get access to the Entitlement API. // obtain an Entitlement API Token in order to get access to the Entitlement API.
// this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement // this token is just an access token issued to a client on behalf of an user
// with a scope = kc_entitlement
String eat = getEntitlementAPIToken(authzClient); String eat = getEntitlementAPIToken(authzClient);
// create an entitlement request // create an entitlement request
@ -123,7 +126,8 @@ public class AuthorizationClientExample {
request.addPermission(permission); request.addPermission(permission);
// send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user // send the entitlement request to the server in order to obtain a RPT
// with all permissions granted to the user
EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request); EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request);
String rpt = response.getRpt(); String rpt = response.getRpt();
@ -133,7 +137,7 @@ public class AuthorizationClientExample {
} }
private static void obtainAllEntitlements() { private static void obtainAllEntitlements() {
// create a new instance based on the configuration define at keycloak-authz.json // create a new instance based on the configuration defined in keycloak-authz.json
AuthzClient authzClient = AuthzClient.create(); AuthzClient authzClient = AuthzClient.create();
// obtian a Entitlement API Token in order to get access to the Entitlement API. // obtian a Entitlement API Token in order to get access to the Entitlement API.

View file

@ -1,6 +1,5 @@
{ {
"realm": "photoz", "realm": "photoz",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth", "auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"resource" : "photoz-html5-client", "resource" : "photoz-html5-client",

View file

@ -1,6 +1,5 @@
{ {
"realm": "photoz", "realm": "photoz",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth", "auth-server-url": "http://localhost:8080/auth",
"ssl-required": "external", "ssl-required": "external",
"resource": "photoz-restful-api", "resource": "photoz-restful-api",

View file

@ -1,6 +1,5 @@
{ {
"realm": "servlet-authz", "realm": "servlet-authz",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth", "auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"resource" : "servlet-authz-app", "resource" : "servlet-authz-app",

View file

@ -1,7 +1,6 @@
{ {
"realm" : "basic-auth", "realm" : "basic-auth",
"resource" : "basic-auth-service", "resource" : "basic-auth-service",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"enable-basic-auth" : "true", "enable-basic-auth" : "true",

View file

@ -1,7 +1,6 @@
{ {
"realm" : "facebook-identity-provider-realm", "realm" : "facebook-identity-provider-realm",
"resource" : "facebook-authentication", "resource" : "facebook-authentication",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"public-client" : true "public-client" : true

View file

@ -1,7 +1,6 @@
{ {
"realm" : "google-identity-provider-realm", "realm" : "google-identity-provider-realm",
"resource" : "google-authentication", "resource" : "google-authentication",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"public-client" : true "public-client" : true

View file

@ -1,7 +1,6 @@
{ {
"realm" : "twitter-identity-provider-realm", "realm" : "twitter-identity-provider-realm",
"resource" : "twitter-authentication", "resource" : "twitter-authentication",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"public-client" : true "public-client" : true

View file

@ -1,6 +1,5 @@
{ {
"realm" : "cors", "realm" : "cors",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost-auth:8080/auth", "auth-server-url" : "http://localhost-auth:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"resource" : "angular-cors-product", "resource" : "angular-cors-product",

View file

@ -1,7 +1,6 @@
{ {
"realm" : "cors", "realm" : "cors",
"resource" : "cors-database-service", "resource" : "cors-database-service",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost-auth:8080/auth", "auth-server-url": "http://localhost-auth:8080/auth",
"bearer-only" : true, "bearer-only" : true,
"ssl-required": "external", "ssl-required": "external",

View file

@ -1,6 +1,5 @@
{ {
"realm" : "demo", "realm" : "demo",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "/auth", "auth-server-url" : "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"resource" : "angular-product", "resource" : "angular-product",

View file

@ -21,7 +21,7 @@
<parent> <parent>
<artifactId>keycloak-examples-demo-parent</artifactId> <artifactId>keycloak-examples-demo-parent</artifactId>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<version></version> <version>2.2.0-SNAPSHOT</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View file

@ -0,0 +1,4 @@
app/*.js
app/*.js.map
node_modules
typings

View file

@ -0,0 +1,67 @@
import { Component } from '@angular/core';
import { Http, Headers, RequestOptions, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { KeycloakService } from './keycloak.service';
@Component({
selector: 'my-app',
template: `
<div id="content-area" class="col-md-9" role="main">
<div id="content">
<h1>Angular2 Product (Beta)</h1>
<h2><span>Products</span></h2>
<button type="button" (click)="logout()">Sign Out</button>
<button type="button" (click)="reloadData()">Reload</button>
<table class="table" [hidden]="!products.length">
<thead>
<tr>
<th>Product Listing</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of products">
<td>{{p}}</td>
</tr>
</tbody>
</table>
</div>
</div>
`
})
export class AppComponent {
products: string[] = [];
constructor(private http: Http, private kc: KeycloakService) {}
logout() {
this.kc.logout();
}
reloadData() {
//angular dont have http interceptor yet
this.kc.getToken()
.then(token => {
let headers = new Headers({
'Accept': 'application/json',
'Authorization': 'Bearer ' + token
});
let options = new RequestOptions({ headers });
this.http.get('/database/products', options)
.map(res => res.json())
.subscribe(prods => this.products = prods,
error => console.log(error));
})
.catch(error => console.log(error));
}
private handleError(error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}

View file

@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule } from '@angular/http';
import { KeycloakService } from './keycloak.service';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
HttpModule
],
declarations: [
AppComponent
],
providers: [
KeycloakService,
],
bootstrap: [ AppComponent ]
})
export class AppModule {}

View file

@ -1,78 +0,0 @@
import {Http, Headers,
RequestOptions, Response} from 'angular2/http';
import {Component} from 'angular2/core';
import {Observable} from 'rxjs/Observable';
import {KeycloakService} from './keycloak';
@Component({
selector: 'my-app',
template:
`
<div id="content-area" class="col-md-9" role="main">
<div id="content">
<h1>Angular2 Product (Beta)</h1>
<h2><span>Products</span></h2>
<button type="button" (click)="logout()">Sign Out</button>
<button type="button" (click)="reloadData()">Reload</button>
<table class="table" [hidden]="!products.length">
<thead>
<tr>
<th>Product Listing</th>
</tr>
</thead>
<tbody>
<tr *ngFor="#p of products">
<td>{{p}}</td>
</tr>
</tbody>
</table>
</div>
</div>
`
})
export class AppComponent {
constructor(private _kc:KeycloakService, private http:Http){ }
products : string[] = [];
logout(){
this._kc.logout();
}
reloadData() {
//angular dont have http interceptor yet
this._kc.getToken().then(
token=>{
let headers = new Headers({
'Accept': 'application/json',
'Authorization': 'Bearer ' + token
});
let options = new RequestOptions({ headers: headers });
this.http.get('/database/products', options)
.map(res => <string[]> res.json())
.subscribe(
prods => this.products = prods,
error => console.log(error));
},
error=>{
console.log(error);
}
);
}
private handleError (error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}

View file

@ -0,0 +1,48 @@
import { Injectable } from '@angular/core';
declare var Keycloak: any;
@Injectable()
export class KeycloakService {
static auth: any = {};
static init(): Promise<any> {
let keycloakAuth: any = new Keycloak('keycloak.json');
KeycloakService.auth.loggedIn = false;
return new Promise((resolve, reject) => {
keycloakAuth.init({ onLoad: 'login-required' })
.success(() => {
KeycloakService.auth.loggedIn = true;
KeycloakService.auth.authz = keycloakAuth;
KeycloakService.auth.logoutUrl = keycloakAuth.authServerUrl + "/realms/demo/protocol/openid-connect/logout?redirect_uri=/angular2-product/index.html";
resolve();
})
.error(() => {
reject();
});
});
}
logout() {
console.log('*** LOGOUT');
KeycloakService.auth.loggedIn = false;
KeycloakService.auth.authz = null;
window.location.href = KeycloakService.auth.logoutUrl;
}
getToken(): Promise<string> {
return new Promise<string>((resolve, reject) => {
if (KeycloakService.auth.authz.token) {
KeycloakService.auth.authz.updateToken(5)
.success(() => {
resolve(<string>KeycloakService.auth.authz.token);
})
.error(() => {
reject('Failed to refresh token');
});
}
});
}
}

View file

@ -1,49 +0,0 @@
import {Injectable} from 'angular2/core';
declare var Keycloak: any;
@Injectable()
export class KeycloakService {
static auth : any = {};
static init() : Promise<any>{
let keycloakAuth : any = new Keycloak('keycloak.json');
KeycloakService.auth.loggedIn = false;
return new Promise((resolve,reject)=>{
keycloakAuth.init({ onLoad: 'login-required' })
.success( () => {
KeycloakService.auth.loggedIn = true;
KeycloakService.auth.authz = keycloakAuth;
KeycloakService.auth.logoutUrl = keycloakAuth.authServerUrl + "/realms/demo/tokens/logout?redirect_uri=/angular2-product/index.html";
resolve(null);
})
.error(()=> {
reject(null);
});
});
}
logout(){
console.log('*** LOGOUT');
KeycloakService.auth.loggedIn = false;
KeycloakService.auth.authz = null;
window.location.href = KeycloakService.auth.logoutUrl;
}
getToken(): Promise<string>{
return new Promise<string>((resolve,reject)=>{
if (KeycloakService.auth.authz.token) {
KeycloakService.auth.authz.updateToken(5).success(function() {
resolve(<string>KeycloakService.auth.authz.token);
})
.error(function() {
reject('Failed to refresh token');
});
}
});
}
}

View file

@ -1,14 +1,11 @@
import 'rxjs/Rx'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import {bootstrap} from 'angular2/platform/browser'; import { AppModule } from './app.module';
import {HTTP_BINDINGS} from 'angular2/http';
import {KeycloakService} from './keycloak';
import {AppComponent} from './app';
KeycloakService.init().then( import {KeycloakService} from './keycloak.service';
o=>{
bootstrap(AppComponent,[HTTP_BINDINGS, KeycloakService]); KeycloakService.init()
}, .then(() => {
x=>{ const platform = platformBrowserDynamic();
window.location.reload(); platform.bootstrapModule(AppModule);
} })
); .catch(() => window.location.reload());

View file

@ -2,48 +2,23 @@
<html> <html>
<head> <head>
<title>Angular 2 QuickStart</title> <title>Angular 2 QuickStart</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 1. Load libraries -->
<!-- Polyfill(s) for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="/auth/js/keycloak.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head> </head>
<!-- 3. Display the application --> <!-- 3. Display the application -->
<body> <body>
<my-app>Loading...</my-app> <my-app>Loading...</my-app>
<!-- 1. Load libraries -->
<!-- IE required polyfills, in this exact order -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/http.js"></script>
<script src="/auth/js/keycloak.js"></script>
<!-- 2. Configure SystemJS -->
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/main')
.then(null, console.error.bind(console));
</script>
</body> </body>
</html> </html>

View file

@ -1,6 +1,5 @@
{ {
"realm": "demo", "realm": "demo",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"ssl-required": "external", "ssl-required": "external",
"resource": "angular2-product", "resource": "angular2-product",

View file

@ -2,24 +2,36 @@
"name": "angular2-product-app", "name": "angular2-product-app",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
"lite": "lite-server",
"postinstall": "typings install",
"tsc": "tsc", "tsc": "tsc",
"tsc:w": "tsc -w", "tsc:w": "tsc -w",
"lite": "lite-server", "typings": "typings"
"start": "concurrent \"npm run tsc:w\" \"npm run lite\" "
}, },
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"angular2": "2.0.0-beta.3", "@angular/common": "2.0.0",
"systemjs": "0.19.6", "@angular/compiler": "2.0.0",
"es6-promise": "^3.0.2", "@angular/core": "2.0.0",
"es6-shim": "^0.33.3", "@angular/forms": "2.0.0",
"reflect-metadata": "0.1.2", "@angular/http": "2.0.0",
"rxjs": "5.0.0-beta.0", "@angular/platform-browser": "2.0.0",
"zone.js": "0.5.11" "@angular/platform-browser-dynamic": "2.0.0",
"@angular/router": "3.0.0",
"@angular/upgrade": "2.0.0",
"angular2-in-memory-web-api": "0.0.20",
"bootstrap": "^3.3.6",
"core-js": "^2.4.1",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.12",
"systemjs": "0.19.27",
"zone.js": "^0.6.21"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^1.0.0", "concurrently": "^2.2.0",
"lite-server": "^2.0.1", "lite-server": "^2.2.2",
"typescript": "^1.7.5" "typescript": "^2.0.2",
"typings": "^1.3.2"
} }
} }

View file

@ -0,0 +1,43 @@
/**
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular2-in-memory-web-api': 'npm:angular2-in-memory-web-api',
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
},
'angular2-in-memory-web-api': {
main: './index.js',
defaultExtension: 'js'
}
}
});
})(this);

View file

@ -1,15 +1,12 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"module": "system", "module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"sourceMap": false, "sourceMap": true,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"removeComments": false, "removeComments": false,
"noImplicitAny": false "noImplicitAny": false
}, }
"exclude": [ }
"node_modules"
]
}

View file

@ -0,0 +1,7 @@
{
"globalDependencies": {
"core-js": "registry:dt/core-js#0.0.0+20160725163759",
"jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
"node": "registry:dt/node#6.0.0+20160909174046"
}
}

View file

@ -1,6 +1,5 @@
{ {
"realm" : "demo", "realm" : "demo",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth", "auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"resource" : "customer-portal-cli", "resource" : "customer-portal-cli",

View file

@ -1,7 +1,6 @@
{ {
"realm": "demo", "realm": "demo",
"resource": "customer-portal-filter", "resource": "customer-portal-filter",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"expose-token": true, "expose-token": true,

View file

@ -1,6 +1,5 @@
{ {
"realm" : "demo", "realm" : "demo",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "/auth", "auth-server-url" : "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"resource" : "customer-portal-js", "resource" : "customer-portal-js",

View file

@ -1,7 +1,6 @@
{ {
"realm": "demo", "realm": "demo",
"resource": "customer-portal", "resource": "customer-portal",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"expose-token": true, "expose-token": true,

View file

@ -1,7 +1,6 @@
{ {
"realm" : "demo", "realm" : "demo",
"resource" : "database-service", "resource" : "database-service",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"bearer-only" : true, "bearer-only" : true,
"ssl-required" : "external" "ssl-required" : "external"

View file

@ -1,7 +1,6 @@
{ {
"realm": "demo", "realm": "demo",
"resource": "offline-access-portal", "resource": "offline-access-portal",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"credentials": { "credentials": {

View file

@ -51,6 +51,7 @@
<module>example-ear</module> <module>example-ear</module>
<module>admin-access-app</module> <module>admin-access-app</module>
<module>angular-product-app</module> <module>angular-product-app</module>
<module>angular2-product-app</module>
<module>database-service</module> <module>database-service</module>
<module>third-party</module> <module>third-party</module>
<module>third-party-cdi</module> <module>third-party-cdi</module>

View file

@ -1,7 +1,6 @@
{ {
"realm" : "demo", "realm" : "demo",
"resource" : "product-portal", "resource" : "product-portal",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "/auth", "auth-server-url" : "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"credentials" : { "credentials" : {

View file

@ -41,6 +41,7 @@ import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.RSATokenVerifier; import org.keycloak.RSATokenVerifier;
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.adapters.KeycloakDeployment; import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder; import org.keycloak.adapters.KeycloakDeploymentBuilder;
@ -163,7 +164,7 @@ public abstract class ProductServiceAccountServlet extends HttpServlet {
private void setTokens(HttpServletRequest req, KeycloakDeployment deployment, AccessTokenResponse tokenResponse) throws IOException, VerificationException { private void setTokens(HttpServletRequest req, KeycloakDeployment deployment, AccessTokenResponse tokenResponse) throws IOException, VerificationException {
String token = tokenResponse.getToken(); String token = tokenResponse.getToken();
String refreshToken = tokenResponse.getRefreshToken(); String refreshToken = tokenResponse.getRefreshToken();
AccessToken tokenParsed = RSATokenVerifier.verifyToken(token, deployment.getRealmKey(), deployment.getRealmInfoUrl()); AccessToken tokenParsed = AdapterRSATokenVerifier.verifyToken(token, deployment);
req.getSession().setAttribute(TOKEN, token); req.getSession().setAttribute(TOKEN, token);
req.getSession().setAttribute(REFRESH_TOKEN, refreshToken); req.getSession().setAttribute(REFRESH_TOKEN, refreshToken);
req.getSession().setAttribute(TOKEN_PARSED, tokenParsed); req.getSession().setAttribute(TOKEN_PARSED, tokenParsed);

View file

@ -1,6 +1,5 @@
{ {
"realm" : "demo", "realm" : "demo",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth", "auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"resource" : "product-sa-client", "resource" : "product-sa-client",

View file

@ -1,6 +1,5 @@
{ {
"realm" : "demo", "realm" : "demo",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth", "auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"resource" : "product-sa-client-jwt-auth", "resource" : "product-sa-client-jwt-auth",

View file

@ -26,7 +26,6 @@
<bean id="kcAdapterConfig" class="org.keycloak.representations.adapters.config.AdapterConfig"> <bean id="kcAdapterConfig" class="org.keycloak.representations.adapters.config.AdapterConfig">
<property name="realm" value="demo"/> <property name="realm" value="demo"/>
<property name="resource" value="admin-camel-endpoint"/> <property name="resource" value="admin-camel-endpoint"/>
<property name="realmKey" value="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"/>
<property name="bearerOnly" value="true"/> <property name="bearerOnly" value="true"/>
<property name="authServerUrl" value="http://localhost:8080/auth" /> <property name="authServerUrl" value="http://localhost:8080/auth" />
<property name="sslRequired" value="EXTERNAL"/> <property name="sslRequired" value="EXTERNAL"/>

View file

@ -1,7 +1,6 @@
{ {
"realm": "demo", "realm": "demo",
"resource": "customer-portal", "resource": "customer-portal",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth", "auth-server-url": "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"credentials": { "credentials": {

View file

@ -1,7 +1,6 @@
{ {
"realm": "demo", "realm": "demo",
"resource": "builtin-cxf-app", "resource": "builtin-cxf-app",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth", "auth-server-url": "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"credentials": { "credentials": {

View file

@ -32,7 +32,6 @@
<bean id="kcAdapterConfig" class="org.keycloak.representations.adapters.config.AdapterConfig"> <bean id="kcAdapterConfig" class="org.keycloak.representations.adapters.config.AdapterConfig">
<property name="realm" value="demo"/> <property name="realm" value="demo"/>
<property name="resource" value="custom-cxf-endpoint"/> <property name="resource" value="custom-cxf-endpoint"/>
<property name="realmKey" value="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB"/>
<property name="bearerOnly" value="true"/> <property name="bearerOnly" value="true"/>
<property name="authServerUrl" value="http://localhost:8080/auth" /> <property name="authServerUrl" value="http://localhost:8080/auth" />
<property name="sslRequired" value="EXTERNAL"/> <property name="sslRequired" value="EXTERNAL"/>

View file

@ -1,7 +1,6 @@
{ {
"realm": "demo", "realm": "demo",
"resource": "external-config", "resource": "external-config",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth", "auth-server-url": "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"credentials": { "credentials": {

View file

@ -1,7 +1,6 @@
{ {
"realm": "demo", "realm": "demo",
"resource": "ssh-jmx-admin-client", "resource": "ssh-jmx-admin-client",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"ssl-required" : "external", "ssl-required" : "external",
"auth-server-url" : "http://localhost:8080/auth", "auth-server-url" : "http://localhost:8080/auth",
"credentials": { "credentials": {

View file

@ -1,7 +1,6 @@
{ {
"realm": "demo", "realm": "demo",
"resource": "product-portal", "resource": "product-portal",
"realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "http://localhost:8080/auth", "auth-server-url": "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"credentials": { "credentials": {

View file

@ -1,6 +1,5 @@
{ {
"realm" : "example", "realm" : "example",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "/auth", "auth-server-url" : "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"resource" : "js-console", "resource" : "js-console",

View file

@ -1,7 +1,6 @@
{ {
"realm" : "kerberos-demo", "realm" : "kerberos-demo",
"resource" : "kerberos-app", "resource" : "kerberos-app",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"credentials": { "credentials": {

View file

@ -1,7 +1,6 @@
{ {
"realm" : "ldap-demo", "realm" : "ldap-demo",
"resource" : "ldap-app", "resource" : "ldap-app",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url": "/auth", "auth-server-url": "/auth",
"ssl-required" : "external", "ssl-required" : "external",
"credentials": { "credentials": {

View file

@ -1,7 +1,6 @@
{ {
"realm" : "tenant1", "realm" : "tenant1",
"resource" : "multi-tenant", "resource" : "multi-tenant",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth", "auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"credentials" : { "credentials" : {

View file

@ -1,7 +1,6 @@
{ {
"realm" : "tenant2", "realm" : "tenant2",
"resource" : "multi-tenant", "resource" : "multi-tenant",
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA0oJjgPQJhnVhOo51KauQGfLLreMFu64OJdKXRnfvAQJQTuKNwc5JrR63l/byyW1B6FgclABF818TtLvMCAkn4EuFwQZCZhg3x3+lFGiB/IzC6UAt4Bi0JQrTbdh83/U97GIPegvaDqiqEiQESEkbCZWxM6sh/34hQaAhCaFpMwIDAQAB",
"auth-server-url" : "http://localhost:8080/auth", "auth-server-url" : "http://localhost:8080/auth",
"ssl-required" : "external", "ssl-required" : "external",
"credentials" : { "credentials" : {

View file

@ -6,13 +6,12 @@ of Keycloak. To deploy, build this directory then take the jar and copy it to
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.secret-question --resources=target/authenticator-required-action-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-services,org.jboss.resteasy.resteasy-jaxrs,javax.ws.rs.api" KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.secret-question --resources=target/authenticator-required-action-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,org.keycloak.keycloak-services,org.jboss.resteasy.resteasy-jaxrs,javax.ws.rs.api"
Then registering the provider by editing keycloak-server.json and adding the module to the providers field: Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
"providers": [
....
"module:org.keycloak.examples.secret-question"
],
<providers>
...
<provider>module:org.keycloak.examples.secret-question</provider>
</providers>
You then have to copy the secret-question.ftl and secret-question-config.ftl files to the themes/base/login directory. You then have to copy the secret-question.ftl and secret-question-config.ftl files to the themes/base/login directory.

View file

@ -70,7 +70,7 @@ public class SecretQuestionCredentialProvider implements CredentialProvider, Cre
CredentialModel secret = new CredentialModel(); CredentialModel secret = new CredentialModel();
secret.setType(SECRET_QUESTION); secret.setType(SECRET_QUESTION);
secret.setValue(credInput.getValue()); secret.setValue(credInput.getValue());
secret.setCreatedDate(Time.toMillis(Time.currentTime())); secret.setCreatedDate(Time.currentTimeMillis());
session.userCredentialManager().createCredential(realm ,user, secret); session.userCredentialManager().createCredential(realm ,user, secret);
} else { } else {
creds.get(0).setValue(credInput.getValue()); creds.get(0).setValue(credInput.getValue());

View file

@ -6,12 +6,12 @@ To run, deploy as a module by running:
$KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.domain-extension-example --resources=target/domain-extension-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-model-jpa,org.keycloak.keycloak-server-spi,javax.ws.rs.api,javax.persistence.api,org.hibernate,org.javassist" $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.domain-extension-example --resources=target/domain-extension-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-services,org.keycloak.keycloak-model-jpa,org.keycloak.keycloak-server-spi,javax.ws.rs.api,javax.persistence.api,org.hibernate,org.javassist"
Then registering the provider by editing keycloak-server.json and adding the module to the providers field: Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
"providers": [ <providers>
.... ...
"module:org.keycloak.examples.domain-extension-example" <provider>module:org.keycloak.examples.domain-extension-example</provider>
], </providers>
Then start (or restart) the server. Then start (or restart) the server.

View file

@ -5,23 +5,25 @@ To deploy copy target/event-listener-sysout-example.jar to providers directory.
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi" KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-sysout --resources=target/event-listener-sysout-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
Then registering the provider by editing keycloak-server.json and adding the module to the providers field: Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
"providers": [ <providers>
.... ...
"module:org.keycloak.examples.event-sysout" <provider>module:org.keycloak.examples.event-sysout</provider>
], </providers>
Then start (or restart) the server. Once started open the admin console, select your realm, then click on Events, Then start (or restart) the server. Once started open the admin console, select your realm, then click on Events,
followed by config. Click on Listeners select box, then pick sysout from the dropdown. After this try to logout and followed by config. Click on Listeners select box, then pick sysout from the dropdown. After this try to logout and
login again to see events printed to System.out. login again to see events printed to System.out.
The example event listener can be configured to exclude certain events, for example to exclude REFRESH_TOKEN and The example event listener can be configured to exclude certain events, for example to exclude REFRESH_TOKEN and
CODE_TO_TOKEN events add the following to keycloak-server.json: CODE_TO_TOKEN events add the following to `standalone.xml`:
... ...
"eventsListener": { <spi name="eventsListener">
"sysout": { <provider name="sysout">
"exclude": [ "REFRESH_TOKEN", "CODE_TO_TOKEN" ] <properties>
} <property name="exclude-events" value="[&quot;REFRESH_TOKEN&quot;, &quot;CODE_TO_TOKEN&quot;]"/>
} </properties>
</provider
</spi>

View file

@ -5,24 +5,24 @@ To deploy copy target/event-store-mem-example.jar to providers directory. Altern
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-inmem --resources=target/event-store-mem-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi" KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.event-inmem --resources=target/event-store-mem-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
Then registering the provider by editing keycloak-server.json and adding the module to the providers field: Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
"providers": [ <providers>
.... ...
"module:org.keycloak.examples.event-inmem" <provider>module:org.keycloak.examples.event-inmem</provider>
], </providers>
Then edit standalone/configuration/keycloak-server.json, change: Then edit `standalone/configuration/standalone.xml`, change:
"eventsStore": { <spi name="eventsStore">
"provider": "jpa" <default-provider>jpa</default-provider>
} </spi>
to: to:
"eventsStore": { <spi name="eventsStore">
"provider": "in-mem" <default-provider>in-mem</default-provider>
} </spi>
Then start (or restart)the server. Once started open the admin console, select your realm, then click on Events, Then start (or restart)the server. Once started open the admin console, select your realm, then click on Events,
followed by config. Set the toggle for Enabled to ON. After this try to logout and login again then open the Events tab followed by config. Set the toggle for Enabled to ON. After this try to logout and login again then open the Events tab

View file

@ -6,13 +6,12 @@ key pairs. To deploy, build this directory then take the jar and copy it to pro
KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.userprops --resources=target/federation-properties-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi" KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.userprops --resources=target/federation-properties-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi"
Then registering the provider by editing keycloak-server.json and adding the module to the providers field: Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
"providers": [
....
"module:org.keycloak.examples.userprops"
],
<providers>
...
<provider>module:org.keycloak.examples.userprops</provider>
</providers>
You will then have to restart the authentication server. You will then have to restart the authentication server.

View file

@ -96,7 +96,7 @@ public abstract class BasePropertiesFederationFactory implements UserFederationP
} }
/** /**
* You can import additional plugin configuration from keycloak-server.json here. * You can import additional plugin configuration from standalone.xml here.
* *
* @param config * @param config
*/ */

View file

@ -5,12 +5,12 @@ To deploy copy target/hello-rest-example.jar to providers directory. Alternative
$KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.hello-rest-example --resources=target/hello-rest-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,javax.ws.rs.api" $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.examples.hello-rest-example --resources=target/hello-rest-example.jar --dependencies=org.keycloak.keycloak-core,org.keycloak.keycloak-server-spi,javax.ws.rs.api"
Then registering the provider by editing keycloak-server.json and adding the module to the providers field: Then registering the provider by editing `standalone/configuration/standalone.xml` and adding the module to the providers element:
"providers": [ <providers>
.... ...
"module:org.keycloak.examples.hello-rest-example" <provider>module:org.keycloak.examples.hello-rest-example</provider>
], </providers>
Then start (or restart) the server. Once started open http://localhost:8080/realms/master/hello and you should see the message _Hello master_. Then start (or restart) the server. Once started open http://localhost:8080/realms/master/hello and you should see the message _Hello master_.
You can also invoke the endpoint for other realms by replacing `master` with the realm name in the above url. You can also invoke the endpoint for other realms by replacing `master` with the realm name in the above url.

View file

@ -17,14 +17,14 @@ Alternatively you can deploy as modules. This can be done by first running:
mvn clean install mvn clean install
$KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.example.themes --resources=target/keycloak-example-themes.jar" $KEYCLOAK_HOME/bin/jboss-cli.sh --command="module add --name=org.keycloak.example.themes --resources=target/keycloak-example-themes.jar"
Then open $KEYCLOAK_HOME/standalone/configuration/keycloak-server.json and register the theme module by adding: Then open `standalone/configuration/standalone.xml` and register the theme module by adding:
"theme": {
"module": {
"modules": [ "org.keycloak.example.themes" ]
}
}
<theme>
...
<modules>
<module>org.keycloak.example.themes</module>
</modules>
</theme>
Address Theme Address Theme
------------------- -------------------
@ -45,11 +45,11 @@ Change Logo Theme
To enable the theme open the admin console, select your realm, click on `Theme`. In the dropdowns for `Login Theme`, `Account Theme` and `Admin Console Theme` select `logo-example`. Click `Save` and login to the realm to see the new theme in action. To enable the theme open the admin console, select your realm, click on `Theme`. In the dropdowns for `Login Theme`, `Account Theme` and `Admin Console Theme` select `logo-example`. Click `Save` and login to the realm to see the new theme in action.
To change the theme for the welcome pages open `standalone/configuration/keycloak-server.json` find the config for `theme` and add 'welcomeTheme': To change the theme for the welcome pages open `standalone/configuration/standalone.xml` find the config for `theme` and add 'welcomeTheme':
"theme": { <theme>
... ...
"welcomeTheme": "logo-example" <welcomeTheme>logo-example</welcomeTheme>
}, </theme>
One thing to note is that to change the admin console for the master admin console (`/auth/admin`) you need to change the theme for the master realm. Changing the admin console theme for any other realms will only change the admin console for that specific realm (for example `/auth/admin/myrealm/console`). One thing to note is that to change the admin console for the master admin console (`/auth/admin`) you need to change the theme for the master realm. Changing the admin console theme for any other realms will only change the admin console for that specific realm (for example `/auth/admin/myrealm/console`).

View file

@ -23,6 +23,20 @@
<target>${maven.compiler.target}</target> <target>${maven.compiler.target}</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>

View file

@ -16,22 +16,17 @@
*/ */
package cx.ath.matthew; package cx.ath.matthew;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* @author <a href="mailto:bruno@abstractj.org">Bruno Oliveira</a>. * @author <a href="mailto:bruno@abstractj.org">Bruno Oliveira</a>.
*/ */
public class LibraryLoader { public class LibraryLoader {
private static final Logger LOGGER = Logger.getLogger(LibraryLoader.class.getSimpleName());
private static final String[] PATHS = {"/usr/lib/", "/usr/lib64/", "/usr/local/lib/", "/opt/local/lib/"}; private static final String[] PATHS = {"/usr/lib/", "/usr/lib64/", "/usr/local/lib/", "/opt/local/lib/"};
private static final String LIBRARY_NAME = "libunix_dbus_java"; private static final String LIBRARY_NAME = "libunix_dbus_java";
private static final String VERSION = "0.0.8"; private static final String VERSION = "0.0.8";
private static boolean loadSucceeded; private static boolean loadSucceeded;
public static void load() { public static LibraryLoader load() {
for (String path : PATHS) { for (String path : PATHS) {
try { try {
System.load(String.format("%s/%s.so.%s", path, LIBRARY_NAME, VERSION)); System.load(String.format("%s/%s.so.%s", path, LIBRARY_NAME, VERSION));
@ -40,10 +35,12 @@ public class LibraryLoader {
} catch (UnsatisfiedLinkError e) { } catch (UnsatisfiedLinkError e) {
loadSucceeded = false; loadSucceeded = false;
} }
} }
if (!loadSucceeded) LOGGER.log(Level.WARNING, "libunix_dbus_java not found\n" + return new LibraryLoader();
"Please, make sure you have the package libunix-dbus-java installed."); }
public boolean succeed() {
return loadSucceeded;
} }
} }

Some files were not shown because too many files have changed in this diff Show more