KEYCLOAK-1295 Adapter support. Fixes
This commit is contained in:
1 parent
87f7ec5909
commit
d8d6348f67
64 files changed
+915
-661
No files matched your search
+19
@@ -0,0 +1,19 @@
|
||||
package org.keycloak.example;
|
||||
|
||||
/**
|
||||
* Client authentication with traditional OAuth2 client_id + client_secret
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ProductSAClientSecretServlet extends ProductServiceAccountServlet {
|
||||
|
||||
@Override
|
||||
protected String getAdapterConfigLocation() {
|
||||
return "WEB-INF/keycloak-client-secret.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getClientAuthenticationMethod() {
|
||||
return "secret";
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.keycloak.example;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ProductSAClientSignedJWTServlet extends ProductServiceAccountServlet {
|
||||
|
||||
@Override
|
||||
protected String getAdapterConfigLocation() {
|
||||
return "WEB-INF/keycloak-client-signed-jwt.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getClientAuthenticationMethod() {
|
||||
return "jwt";
|
||||
}
|
||||
}
|
||||
+37
-12
@@ -4,7 +4,9 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
@@ -26,30 +28,46 @@ import org.keycloak.VerificationException;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.adapters.ServerRequest;
|
||||
import org.keycloak.constants.ServiceAccountConstants;
|
||||
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class ProductServiceAccountServlet extends HttpServlet {
|
||||
public abstract class ProductServiceAccountServlet extends HttpServlet {
|
||||
|
||||
public static final String ERROR = "error";
|
||||
public static final String TOKEN = "token";
|
||||
public static final String TOKEN_PARSED = "idTokenParsed";
|
||||
public static final String REFRESH_TOKEN = "refreshToken";
|
||||
public static final String PRODUCTS = "products";
|
||||
public static final String CLIENT_AUTH_METHOD = "clientAuthMethod";
|
||||
|
||||
protected abstract String getAdapterConfigLocation();
|
||||
protected abstract String getClientAuthenticationMethod();
|
||||
|
||||
public static String getLoginUrl(HttpServletRequest request) {
|
||||
return "/service-account-portal/app-" + request.getAttribute(CLIENT_AUTH_METHOD) + "/login";
|
||||
}
|
||||
|
||||
public static String getRefreshUrl(HttpServletRequest request) {
|
||||
return "/service-account-portal/app-" + request.getAttribute(CLIENT_AUTH_METHOD) + "/refresh";
|
||||
}
|
||||
|
||||
public static String getLogoutUrl(HttpServletRequest request) {
|
||||
return "/service-account-portal/app-" + request.getAttribute(CLIENT_AUTH_METHOD) + "/logout";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws ServletException {
|
||||
InputStream config = getServletContext().getResourceAsStream("WEB-INF/keycloak.json");
|
||||
String adapterConfigLocation = getAdapterConfigLocation();
|
||||
InputStream config = getServletContext().getResourceAsStream(adapterConfigLocation);
|
||||
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
getServletContext().setAttribute("deployment-" + getClientAuthenticationMethod(), deployment);
|
||||
|
||||
getServletContext().setAttribute(KeycloakDeployment.class.getName(), deployment);
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
getServletContext().setAttribute(HttpClient.class.getName(), client);
|
||||
}
|
||||
|
||||
@@ -60,6 +78,8 @@ public class ProductServiceAccountServlet extends HttpServlet {
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
req.setAttribute(CLIENT_AUTH_METHOD, getClientAuthenticationMethod());
|
||||
|
||||
String reqUri = req.getRequestURI();
|
||||
if (reqUri.endsWith("/login")) {
|
||||
serviceAccountLogin(req);
|
||||
@@ -81,16 +101,21 @@ public class ProductServiceAccountServlet extends HttpServlet {
|
||||
KeycloakDeployment deployment = getKeycloakDeployment();
|
||||
HttpClient client = getHttpClient();
|
||||
|
||||
String clientId = deployment.getResourceName();
|
||||
String clientSecret = deployment.getResourceCredentials().get("secret");
|
||||
|
||||
try {
|
||||
HttpPost post = new HttpPost(deployment.getTokenUrl());
|
||||
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
|
||||
formparams.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
|
||||
|
||||
String authHeader = BasicAuthHelper.createHeader(clientId, clientSecret);
|
||||
post.addHeader("Authorization", authHeader);
|
||||
// Add client credentials according to the method configured in keycloak.json file
|
||||
Map<String, String> reqHeaders = new HashMap<>();
|
||||
Map<String, String> reqParams = new HashMap<>();
|
||||
ClientCredentialsProviderUtils.setClientCredentials(deployment, reqHeaders, reqParams);
|
||||
for (Map.Entry<String, String> header : reqHeaders.entrySet()) {
|
||||
post.setHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
for (Map.Entry<String, String> param : reqParams.entrySet()) {
|
||||
formparams.add(new BasicNameValuePair(param.getKey(), param.getValue()));
|
||||
}
|
||||
|
||||
UrlEncodedFormEntity form = new UrlEncodedFormEntity(formparams, "UTF-8");
|
||||
post.setEntity(form);
|
||||
@@ -217,7 +242,7 @@ public class ProductServiceAccountServlet extends HttpServlet {
|
||||
}
|
||||
|
||||
private KeycloakDeployment getKeycloakDeployment() {
|
||||
return (KeycloakDeployment) getServletContext().getAttribute(KeycloakDeployment.class.getName());
|
||||
return (KeycloakDeployment) getServletContext().getAttribute("deployment-" + getClientAuthenticationMethod());
|
||||
}
|
||||
|
||||
private HttpClient getHttpClient() {
|
||||
|
||||
Binary file not shown.
File renamed without changes.
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"realm" : "demo",
|
||||
"realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"auth-server-url" : "http://localhost:8080/auth",
|
||||
"ssl-required" : "external",
|
||||
"resource" : "product-sa-client",
|
||||
"credentials": {
|
||||
"jwt": {
|
||||
"client-keystore-file": "classpath:keystore-client.jks",
|
||||
"client-keystore-type": "JKS",
|
||||
"client-keystore-password": "storepass",
|
||||
"client-key-password": "keypass",
|
||||
"client-key-alias": "clientkey",
|
||||
"token-expiration": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,16 @@
|
||||
AccessToken token = (AccessToken) request.getSession().getAttribute(ProductServiceAccountServlet.TOKEN_PARSED);
|
||||
String products = (String) request.getAttribute(ProductServiceAccountServlet.PRODUCTS);
|
||||
String appError = (String) request.getAttribute(ProductServiceAccountServlet.ERROR);
|
||||
String clientAuthMethod = (String) request.getAttribute(ProductServiceAccountServlet.CLIENT_AUTH_METHOD);
|
||||
|
||||
String loginUrl = ProductServiceAccountServlet.getLoginUrl(request);
|
||||
String refreshUrl = ProductServiceAccountServlet.getRefreshUrl(request);
|
||||
String logoutUrl = ProductServiceAccountServlet.getLogoutUrl(request);
|
||||
%>
|
||||
<h1>Service account portal</h1>
|
||||
<p><a href="/service-account-portal/app/login">Login</a> | <a href="/service-account-portal/app/refresh">Refresh token</a> | <a
|
||||
href="/service-account-portal/app/logout">Logout</a></p>
|
||||
<h2>Client authentication method: <%= clientAuthMethod %></h2>
|
||||
<p><a href="<%= loginUrl %>">Login</a> | <a href="<%= refreshUrl %>">Refresh token</a> | <a
|
||||
href="<%= logoutUrl %>">Logout</a></p>
|
||||
<hr />
|
||||
|
||||
<% if (appError != null) { %>
|
||||
|
||||
@@ -7,13 +7,23 @@
|
||||
<module-name>service-account-portal</module-name>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>ServiceAccountExample</servlet-name>
|
||||
<servlet-class>org.keycloak.example.ProductServiceAccountServlet</servlet-class>
|
||||
<servlet-name>ProductSAClientSecretServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.example.ProductSAClientSecretServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>ProductSAClientSignedJWTServlet</servlet-name>
|
||||
<servlet-class>org.keycloak.example.ProductSAClientSignedJWTServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>ServiceAccountExample</servlet-name>
|
||||
<url-pattern>/app/*</url-pattern>
|
||||
<servlet-name>ProductSAClientSecretServlet</servlet-name>
|
||||
<url-pattern>/app-secret/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>ProductSAClientSignedJWTServlet</servlet-name>
|
||||
<url-pattern>/app-jwt/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
||||
@@ -1,5 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Refresh" content="0; URL=app">
|
||||
</head>
|
||||
<head><title>Service account example</title></head>
|
||||
<body>
|
||||
<ul>
|
||||
<li><a href="app-secret">Client authentication with client secret</a><br /><br /></li>
|
||||
<li><a href="app-jwt">Client authentication with JWT signed by client private key</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
@@ -173,7 +173,10 @@
|
||||
"clientId": "product-sa-client",
|
||||
"enabled": true,
|
||||
"secret": "password",
|
||||
"serviceAccountsEnabled": true
|
||||
"serviceAccountsEnabled": true,
|
||||
"attributes": {
|
||||
"jwt.credential.certificate": "MIICnTCCAYUCBgFPPLDaTzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdjbGllbnQxMB4XDTE1MDgxNzE3MjI0N1oXDTI1MDgxNzE3MjQyN1owEjEQMA4GA1UEAwwHY2xpZW50MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIUjjgv+V3s96O+Za9002Lp/trtGuHBeaeVL9dFKMKzO2MPqdRmHB4PqNlDdd28Rwf5Xn6iWdFpyUKOnI/yXDLhdcuFpR0sMNK/C9Lt+hSpPFLuzDqgtPgDotlMxiHIWDOZ7g9/gPYNXbNvjv8nSiyqoguoCQiiafW90bPHsiVLdP7ZIUwCcfi1qQm7FhxRJ1NiW5dvUkuCnnWEf0XR+Wzc5eC9EgB0taLFiPsSEIlWMm5xlahYyXkPdNOqZjiRnrTWm5Y4uk8ZcsD/KbPTf/7t7cQXipVaswgjdYi1kK2/zRwOhg1QwWFX/qmvdd+fLxV0R6VqRDhn7Qep2cxwMxLsCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAKE6OA46sf20bz8LZPoiNsqRwBUDkaMGXfnob7s/hJZIIwDEx0IAQ3uKsG7q9wb+aA6s+v7S340zb2k3IxuhFaHaZpAd4CyR5cn1FHylbzoZ7rI/3ASqHDqpljdJaFqPH+m7nZWtyDvtZf+gkZ8OjsndwsSBK1d/jMZPp29qYbl1+XfO7RCp/jDqro/R3saYFaIFiEZPeKn1hUJn6BO48vxH1xspSu9FmlvDOEAOz4AuM58z4zRMP49GcFdCWr1wkonJUHaSptJaQwmBwLFUkCbE5I1ixGMb7mjEud6Y5jhfzJiZMo2U8RfcjNbrN0diZl3jB6LQIwESnhYSghaTjNQ=="
|
||||
}
|
||||
}
|
||||
],
|
||||
"clientScopeMappings": {
|
||||
|
||||
-1
@@ -41,7 +41,6 @@ public class AppContextListener implements ServletContextListener {
|
||||
}
|
||||
ServletOAuthClientBuilder.build(is, oauthClient);
|
||||
logger.info("OAuth client configured and started");
|
||||
oauthClient.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-2
@@ -27,8 +27,6 @@ public class Bootstrap implements ServletContextListener {
|
||||
ServletContext context = sce.getServletContext();
|
||||
|
||||
configureClient(context);
|
||||
|
||||
client.start();
|
||||
context.setAttribute(ServletOAuthClient.class.getName(), client);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user