Skip to content

Commit c9afe8f

Browse files
committed
Merge pull request keycloak#1553 from mposolda/master
KEYCLOAK-1295 Adapter support. Fixes
2 parents b9d0219 + d8d6348 commit c9afe8f

File tree

68 files changed

+952
-667
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+952
-667
lines changed

connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,23 @@
2727
*/
2828
public class DefaultMongoConnectionFactoryProvider implements MongoConnectionProviderFactory {
2929

30-
// TODO Make configurable
30+
// TODO Make it dynamic
3131
private String[] entities = new String[]{
3232
"org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity",
3333
"org.keycloak.models.mongo.keycloak.entities.MongoUserEntity",
3434
"org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity",
35+
"org.keycloak.models.mongo.keycloak.entities.MongoClientEntity",
36+
"org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity",
37+
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity",
3538
"org.keycloak.models.entities.IdentityProviderEntity",
3639
"org.keycloak.models.entities.ClientIdentityProviderMappingEntity",
3740
"org.keycloak.models.entities.RequiredCredentialEntity",
3841
"org.keycloak.models.entities.CredentialEntity",
3942
"org.keycloak.models.entities.FederatedIdentityEntity",
40-
"org.keycloak.models.mongo.keycloak.entities.MongoClientEntity",
41-
"org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity",
42-
"org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity",
43-
"org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity",
4443
"org.keycloak.models.entities.UserFederationProviderEntity",
4544
"org.keycloak.models.entities.UserFederationMapperEntity",
4645
"org.keycloak.models.entities.ProtocolMapperEntity",
4746
"org.keycloak.models.entities.IdentityProviderMapperEntity",
48-
"org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity",
49-
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity",
5047
"org.keycloak.models.entities.AuthenticationExecutionEntity",
5148
"org.keycloak.models.entities.AuthenticationFlowEntity",
5249
"org.keycloak.models.entities.AuthenticatorConfigEntity",

core/src/main/java/org/keycloak/AbstractOAuthClient.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class AbstractOAuthClient {
1616
private final AtomicLong counter = new AtomicLong();
1717

1818
protected String clientId;
19-
protected Map<String, String> credentials;
19+
protected Map<String, Object> credentials;
2020
protected String authUrl;
2121
protected String tokenUrl;
2222
protected RelativeUrlsUsed relativeUrlsUsed;
@@ -37,11 +37,11 @@ public void setClientId(String clientId) {
3737
this.clientId = clientId;
3838
}
3939

40-
public Map<String, String> getCredentials() {
40+
public Map<String, Object> getCredentials() {
4141
return credentials;
4242
}
4343

44-
public void setCredentials(Map<String, String> credentials) {
44+
public void setCredentials(Map<String, Object> credentials) {
4545
this.credentials = credentials;
4646
}
4747

core/src/main/java/org/keycloak/representations/adapters/config/BaseAdapterConfig.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class BaseAdapterConfig extends BaseRealmConfig {
3939
@JsonProperty("public-client")
4040
protected boolean publicClient;
4141
@JsonProperty("credentials")
42-
protected Map<String, String> credentials = new HashMap<String, String>();
42+
protected Map<String, Object> credentials = new HashMap<>();
4343

4444

4545
public boolean isUseResourceRoleMappings() {
@@ -114,11 +114,11 @@ public void setEnableBasicAuth(boolean enableBasicAuth) {
114114
this.enableBasicAuth = enableBasicAuth;
115115
}
116116

117-
public Map<String, String> getCredentials() {
117+
public Map<String, Object> getCredentials() {
118118
return credentials;
119119
}
120120

121-
public void setCredentials(Map<String, String> credentials) {
121+
public void setCredentials(Map<String, Object> credentials) {
122122
this.credentials = credentials;
123123
}
124124

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.keycloak.example;
2+
3+
/**
4+
* Client authentication with traditional OAuth2 client_id + client_secret
5+
*
6+
* @author <a href="mailto:[email protected]">Marek Posolda</a>
7+
*/
8+
public class ProductSAClientSecretServlet extends ProductServiceAccountServlet {
9+
10+
@Override
11+
protected String getAdapterConfigLocation() {
12+
return "WEB-INF/keycloak-client-secret.json";
13+
}
14+
15+
@Override
16+
protected String getClientAuthenticationMethod() {
17+
return "secret";
18+
}
19+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.keycloak.example;
2+
3+
/**
4+
* @author <a href="mailto:[email protected]">Marek Posolda</a>
5+
*/
6+
public class ProductSAClientSignedJWTServlet extends ProductServiceAccountServlet {
7+
8+
@Override
9+
protected String getAdapterConfigLocation() {
10+
return "WEB-INF/keycloak-client-signed-jwt.json";
11+
}
12+
13+
@Override
14+
protected String getClientAuthenticationMethod() {
15+
return "jwt";
16+
}
17+
}

examples/demo-template/service-account/src/main/java/org/keycloak/example/ProductServiceAccountServlet.java

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import java.io.IOException;
55
import java.io.InputStream;
66
import java.util.ArrayList;
7+
import java.util.HashMap;
78
import java.util.List;
9+
import java.util.Map;
810

911
import javax.servlet.ServletException;
1012
import javax.servlet.http.HttpServlet;
@@ -26,30 +28,46 @@
2628
import org.keycloak.adapters.KeycloakDeployment;
2729
import org.keycloak.adapters.KeycloakDeploymentBuilder;
2830
import org.keycloak.adapters.ServerRequest;
29-
import org.keycloak.constants.ServiceAccountConstants;
31+
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
3032
import org.keycloak.representations.AccessToken;
3133
import org.keycloak.representations.AccessTokenResponse;
32-
import org.keycloak.util.BasicAuthHelper;
3334
import org.keycloak.util.JsonSerialization;
3435

3536
/**
3637
* @author <a href="mailto:[email protected]">Marek Posolda</a>
3738
*/
38-
public class ProductServiceAccountServlet extends HttpServlet {
39+
public abstract class ProductServiceAccountServlet extends HttpServlet {
3940

4041
public static final String ERROR = "error";
4142
public static final String TOKEN = "token";
4243
public static final String TOKEN_PARSED = "idTokenParsed";
4344
public static final String REFRESH_TOKEN = "refreshToken";
4445
public static final String PRODUCTS = "products";
46+
public static final String CLIENT_AUTH_METHOD = "clientAuthMethod";
47+
48+
protected abstract String getAdapterConfigLocation();
49+
protected abstract String getClientAuthenticationMethod();
50+
51+
public static String getLoginUrl(HttpServletRequest request) {
52+
return "/service-account-portal/app-" + request.getAttribute(CLIENT_AUTH_METHOD) + "/login";
53+
}
54+
55+
public static String getRefreshUrl(HttpServletRequest request) {
56+
return "/service-account-portal/app-" + request.getAttribute(CLIENT_AUTH_METHOD) + "/refresh";
57+
}
58+
59+
public static String getLogoutUrl(HttpServletRequest request) {
60+
return "/service-account-portal/app-" + request.getAttribute(CLIENT_AUTH_METHOD) + "/logout";
61+
}
4562

4663
@Override
4764
public void init() throws ServletException {
48-
InputStream config = getServletContext().getResourceAsStream("WEB-INF/keycloak.json");
65+
String adapterConfigLocation = getAdapterConfigLocation();
66+
InputStream config = getServletContext().getResourceAsStream(adapterConfigLocation);
4967
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
50-
HttpClient client = new DefaultHttpClient();
68+
getServletContext().setAttribute("deployment-" + getClientAuthenticationMethod(), deployment);
5169

52-
getServletContext().setAttribute(KeycloakDeployment.class.getName(), deployment);
70+
HttpClient client = new DefaultHttpClient();
5371
getServletContext().setAttribute(HttpClient.class.getName(), client);
5472
}
5573

@@ -60,6 +78,8 @@ public void destroy() {
6078

6179
@Override
6280
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
81+
req.setAttribute(CLIENT_AUTH_METHOD, getClientAuthenticationMethod());
82+
6383
String reqUri = req.getRequestURI();
6484
if (reqUri.endsWith("/login")) {
6585
serviceAccountLogin(req);
@@ -81,16 +101,21 @@ private void serviceAccountLogin(HttpServletRequest req) {
81101
KeycloakDeployment deployment = getKeycloakDeployment();
82102
HttpClient client = getHttpClient();
83103

84-
String clientId = deployment.getResourceName();
85-
String clientSecret = deployment.getResourceCredentials().get("secret");
86-
87104
try {
88105
HttpPost post = new HttpPost(deployment.getTokenUrl());
89106
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
90107
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
91108

92-
String authHeader = BasicAuthHelper.createHeader(clientId, clientSecret);
93-
post.addHeader("Authorization", authHeader);
109+
// Add client credentials according to the method configured in keycloak.json file
110+
Map<String, String> reqHeaders = new HashMap<>();
111+
Map<String, String> reqParams = new HashMap<>();
112+
ClientCredentialsProviderUtils.setClientCredentials(deployment, reqHeaders, reqParams);
113+
for (Map.Entry<String, String> header : reqHeaders.entrySet()) {
114+
post.setHeader(header.getKey(), header.getValue());
115+
}
116+
for (Map.Entry<String, String> param : reqParams.entrySet()) {
117+
formparams.add(new BasicNameValuePair(param.getKey(), param.getValue()));
118+
}
94119

95120
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
96121
post.setEntity(form);
@@ -217,7 +242,7 @@ private String getContent(HttpEntity entity) throws IOException {
217242
}
218243

219244
private KeycloakDeployment getKeycloakDeployment() {
220-
return (KeycloakDeployment) getServletContext().getAttribute(KeycloakDeployment.class.getName());
245+
return (KeycloakDeployment) getServletContext().getAttribute("deployment-" + getClientAuthenticationMethod());
221246
}
222247

223248
private HttpClient getHttpClient() {
Binary file not shown.

examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak.json renamed to examples/demo-template/service-account/src/main/webapp/WEB-INF/keycloak-client-secret.json

File renamed without changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"realm" : "demo",
3+
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
4+
"auth-server-url" : "http://localhost:8080/auth",
5+
"ssl-required" : "external",
6+
"resource" : "product-sa-client",
7+
"credentials": {
8+
"jwt": {
9+
"client-keystore-file": "classpath:keystore-client.jks",
10+
"client-keystore-type": "JKS",
11+
"client-keystore-password": "storepass",
12+
"client-key-password": "keypass",
13+
"client-key-alias": "clientkey",
14+
"token-expiration": 10
15+
}
16+
}
17+
}

examples/demo-template/service-account/src/main/webapp/WEB-INF/page.jsp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,16 @@
1313
AccessToken token = (AccessToken) request.getSession().getAttribute(ProductServiceAccountServlet.TOKEN_PARSED);
1414
String products = (String) request.getAttribute(ProductServiceAccountServlet.PRODUCTS);
1515
String appError = (String) request.getAttribute(ProductServiceAccountServlet.ERROR);
16+
String clientAuthMethod = (String) request.getAttribute(ProductServiceAccountServlet.CLIENT_AUTH_METHOD);
17+
18+
String loginUrl = ProductServiceAccountServlet.getLoginUrl(request);
19+
String refreshUrl = ProductServiceAccountServlet.getRefreshUrl(request);
20+
String logoutUrl = ProductServiceAccountServlet.getLogoutUrl(request);
1621
%>
1722
<h1>Service account portal</h1>
18-
<p><a href="/service-account-portal/app/login">Login</a> | <a href="/service-account-portal/app/refresh">Refresh token</a> | <a
19-
href="/service-account-portal/app/logout">Logout</a></p>
23+
<h2>Client authentication method: <%= clientAuthMethod %></h2>
24+
<p><a href="<%= loginUrl %>">Login</a> | <a href="<%= refreshUrl %>">Refresh token</a> | <a
25+
href="<%= logoutUrl %>">Logout</a></p>
2026
<hr />
2127

2228
<% if (appError != null) { %>

0 commit comments

Comments
 (0)