KEYCLOAK-3564 Update demo examples with public key rotation
This commit is contained in:
parent
f8c236afa8
commit
bc916a1909
15 changed files with 78 additions and 209 deletions
|
@ -28,7 +28,7 @@ _This demo is meant to run on the same server instance as the Keycloak Server!_
|
|||
|
||||
Step 1: Make sure you've set up the Keycloak Server
|
||||
--------------------------------------
|
||||
The Keycloak Appliance Distribution comes with a preconfigured Keycloak server (based on Wildfly). You can use it out of
|
||||
The Keycloak Demo Distribution comes with a preconfigured Keycloak server (based on Wildfly). You can use it out of
|
||||
the box to run these demos. So, if you're using this, you can head to Step 2.
|
||||
|
||||
Alternatively, you can install the Keycloak Server onto any EAP 6.x, or Wildfly 8.x server, but there is
|
||||
|
@ -157,8 +157,8 @@ are still happening, but the auth-server knows you are already logged in so the
|
|||
|
||||
If you click on the logout link of either of the product or customer app, you'll be logged out of all the applications.
|
||||
|
||||
If you click on [http://localhost:8080/customer-portal-js](http://localhost:8080/customer-portal-js) you can invoke
|
||||
on the pure HTML/Javascript application.
|
||||
The example also shows different methods of client authentication. The customer-portal example is using traditional authentication with client_id and client_secret,
|
||||
but the product-portal example is using client authentication with JWT signed by client private key, which is retrieved from the keystore file inside the product-portal WAR.
|
||||
|
||||
Step 6: Traditional OAuth2 Example
|
||||
----------------------------------
|
||||
|
@ -240,9 +240,6 @@ An example for retrieve service account dedicated to the Client Application itse
|
|||
|
||||
Client authentication is done with OAuth2 Client Credentials Grant in out-of-bound request (Not Keycloak login screen displayed) .
|
||||
|
||||
The example also shows different methods of client authentication. There is ProductSAClientSecretServlet using traditional authentication with clientId and client_secret,
|
||||
but there is also ProductSAClientSignedJWTServlet using client authentication with JWT signed by client private key.
|
||||
|
||||
Step 13: Offline Access Example
|
||||
===============================
|
||||
An example for retrieve offline token, which is then saved to the database and can be used by application anytime later. Offline token
|
||||
|
|
|
@ -44,9 +44,8 @@ You also need to add realm config to the same file. Add a new child-element to `
|
|||
|
||||
<profile>
|
||||
....
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak:1.0">
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
|
||||
<realm name="demo">
|
||||
<realm-public-key>REALM PUBLIC KEY</realm-public-key>
|
||||
<auth-server-url>KEYCLOAK URL</auth-server-url>
|
||||
<ssl-required>external</ssl-required>
|
||||
</realm>
|
||||
|
@ -55,7 +54,6 @@ You also need to add realm config to the same file. Add a new child-element to `
|
|||
|
||||
In the above snippet replace the following:
|
||||
|
||||
* `REALM PUBLIC KEY` - replace with the public key for the realm. You can find this in the admin console by selecting the realm, then clicking on `Keys`
|
||||
* `KEYCLOAK URL` - replace with the base url of Keycloak (for example http://localhost:8080/auth or http://keycloak.example.org/auth)
|
||||
|
||||
Don't start the WildFly server until you've configured and deployed the demo applications.
|
||||
|
@ -70,7 +68,7 @@ Run the following to deploy it:
|
|||
# mvn install
|
||||
# cp target/database.war <WILDFLY HOME>/standalone/deployments
|
||||
|
||||
Next add the configuration for it to the Keycloak subsystem. Edit `<WILDFLY HOME>/standalone/configuration/standalone.xml` to `<subsystem xmlns="urn:jboss:domain:keycloak:1.0">` add:
|
||||
Next add the configuration for it to the Keycloak subsystem. Edit `<WILDFLY HOME>/standalone/configuration/standalone.xml` to `<subsystem xmlns="urn:jboss:domain:keycloak:1.1">` add:
|
||||
|
||||
<secure-deployment name="database.war">
|
||||
<realm>demo</realm>
|
||||
|
@ -88,12 +86,17 @@ Run the following to deploy it:
|
|||
# mvn install
|
||||
# cp target/customer-portal.war <WILDFLY HOME>/standalone/deployments
|
||||
|
||||
Then open the Keycloak admin console to add a configuration for it. Navigate to the realm and click on `Applications` then `Add Application`. Fill in the form with:
|
||||
Then open the Keycloak admin console to add a configuration for it. Navigate to the realm and click on `Clients` then `Add Client`. Fill in the form with:
|
||||
|
||||
* Name - `customer-portal`
|
||||
* Redirect URI - `http://localhost:8080/customer-portal/*` (click `Add` after filling in the field)
|
||||
* Client ID - `customer-portal`
|
||||
|
||||
Then click on `Save`. As it's a confidential (non-public) application you need the secret for it. Click on `Credentials` and note the value of the `Secret` field.
|
||||
Then click on `Save`. You will see more possibilities to setup client now, so you can add the following:
|
||||
`Access Type` - `confidential`
|
||||
`Valid Redirect URIs` - `http://localhost:8080/customer-portal/*` (click `Add` after filling in the field)
|
||||
|
||||
Then click on `Save` again so that client is updated.
|
||||
|
||||
As it's a confidential (non-public) application you need the secret for it. Click on `Credentials` and note the value of the `Secret` field.
|
||||
|
||||
Then edit `<WILDFLY HOME>/standalone/configuration/standalone.xml` and add the following to `<subsystem xmlns="urn:jboss:domain:keycloak:1.0">`:
|
||||
|
||||
|
@ -117,21 +120,39 @@ Run the following to deploy it:
|
|||
# mvn install
|
||||
# cp target/product-portal.war <WILDFLY HOME>/standalone/deployments
|
||||
|
||||
Then open the Keycloak admin console to add a configuration for it. Navigate to the realm and click on `Applications` then `Add Application`. Fill in the form with:
|
||||
Then open the Keycloak admin console to add a configuration for it. Navigate to the realm and click on `Clients` then `Add Client`. Fill in the form with:
|
||||
|
||||
* Name - `product-portal`
|
||||
* Redirect URI - `http://localhost:8080/product-portal/*` (click `Add` after filling in the field)
|
||||
* Client ID - `product-portal`
|
||||
|
||||
Then click on `Save`. As it's a confidential (non-public) application you need the secret for it. Click on `Credentials` and note the value of the `Secret` field.
|
||||
Then click on `Save`. You will see more possibilities to setup client now, so you can add the following:
|
||||
|
||||
`Access Type` - `confidential`
|
||||
`Valid Redirect URIs` - `http://localhost:8080/product-portal/*` (click `Add` after filling in the field)
|
||||
|
||||
Then click on `Save` again so that client is updated.
|
||||
|
||||
It's a confidential (non-public) application, so we again need client credentials for it. But for product-portal, we will use authentication with signed JWT instead of traditional OAuth2 client secret.
|
||||
Click on `Credentials` and fill the following values:
|
||||
|
||||
`Client Authenticator` - `Signed JWT`
|
||||
`Use JWKS URL` - `ON`
|
||||
`JWKS URL` - `/product-portal/k_jwks`
|
||||
|
||||
Then edit `<WILDFLY HOME>/standalone/configuration/standalone.xml` and add the following to `<subsystem xmlns="urn:jboss:domain:keycloak:1.0">`:
|
||||
|
||||
<secure-deployment name="product-portal.war">
|
||||
<realm>demo</realm>
|
||||
<resource>product-portal</resource>
|
||||
<credential name="secret">APPLICATION SECRET</credential>
|
||||
<credential name="jwt">
|
||||
<client-keystore-file>classpath:keystore-client.jks</client-keystore-file>
|
||||
<client-keystore-type>JKS</client-keystore-type>
|
||||
<client-keystore-password>storepass</client-keystore-password>
|
||||
<client-key-password>keypass</client-key-password>
|
||||
<client-key-alias>clientkey</client-key-alias>
|
||||
<token-expiration>10</token-expiration>
|
||||
</credential>
|
||||
</secure-deployment>
|
||||
|
||||
In the above snippet replace the following:
|
||||
|
||||
* `APPLICATION SECRET` - replace with the applications secret you just noted from the Keycloak admin console
|
||||
With this configuration, the product-portal application will authenticate with JWT token signed by the private key from the file `keystore-client.jks`, which is available
|
||||
inside the application WAR. If you don't use `classpath:` prefix in the configuration, you can use any keystore file from filesystem. If you want to generate your own keystore file,
|
||||
you can either use `keytool` tool, but you can also generate the one inside Keycloak admin console and then save it locally.
|
|
@ -3,7 +3,14 @@
|
|||
"resource" : "product-portal",
|
||||
"auth-server-url" : "/auth",
|
||||
"ssl-required" : "external",
|
||||
"credentials" : {
|
||||
"secret": "password"
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +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.
|
||||
*/
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
|
@ -1,37 +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.
|
||||
*/
|
||||
|
||||
package org.keycloak.example;
|
||||
|
||||
/**
|
||||
* Client authentication based on JWT signed by client private key .
|
||||
* See Keycloak documentation and <a href="https://tools.ietf.org/html/rfc7519">specs</a> for more details.
|
||||
*
|
||||
* @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";
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import org.keycloak.adapters.ServerRequest;
|
|||
import org.keycloak.adapters.authentication.ClientCredentialsProviderUtils;
|
||||
import org.keycloak.adapters.rotation.AdapterRSATokenVerifier;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.StreamUtil;
|
||||
import org.keycloak.common.util.UriUtils;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
|
@ -53,36 +54,28 @@ import java.util.Map;
|
|||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class ProductServiceAccountServlet extends HttpServlet {
|
||||
public 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";
|
||||
return "/service-account-portal/app/login";
|
||||
}
|
||||
|
||||
public static String getLogoutUrl(HttpServletRequest request) {
|
||||
return "/service-account-portal/app-" + request.getAttribute(CLIENT_AUTH_METHOD) + "/logout";
|
||||
return "/service-account-portal/app/logout";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws ServletException {
|
||||
String adapterConfigLocation = getAdapterConfigLocation();
|
||||
String adapterConfigLocation = "/WEB-INF/keycloak.json";
|
||||
InputStream config = getServletContext().getResourceAsStream(adapterConfigLocation);
|
||||
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(config);
|
||||
getServletContext().setAttribute("deployment-" + getClientAuthenticationMethod(), deployment);
|
||||
getServletContext().setAttribute(KeycloakDeployment.class.getName(), deployment);
|
||||
|
||||
HttpClient client = new DefaultHttpClient();
|
||||
getServletContext().setAttribute(HttpClient.class.getName(), client);
|
||||
|
@ -95,13 +88,10 @@ public abstract 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);
|
||||
} else if (reqUri.endsWith("/refresh")) {
|
||||
refreshToken(req);
|
||||
} else if (reqUri.endsWith("/logout")){
|
||||
logout(req);
|
||||
}
|
||||
|
@ -197,25 +187,6 @@ public abstract class ProductServiceAccountServlet extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
private void refreshToken(HttpServletRequest req) {
|
||||
KeycloakDeployment deployment = getKeycloakDeployment();
|
||||
String refreshToken = (String) req.getSession().getAttribute(REFRESH_TOKEN);
|
||||
if (refreshToken == null) {
|
||||
req.setAttribute(ERROR, "No refresh token available. Please login first");
|
||||
} else {
|
||||
try {
|
||||
AccessTokenResponse tokenResponse = ServerRequest.invokeRefresh(deployment, refreshToken);
|
||||
setTokens(req, deployment, tokenResponse);
|
||||
} catch (ServerRequest.HttpFailure hfe) {
|
||||
hfe.printStackTrace();
|
||||
req.setAttribute(ERROR, "Failed refresh token. See server.log for details. Status was: " + hfe.getStatus() + ", Error is: " + hfe.getError());
|
||||
} catch (Exception ioe) {
|
||||
ioe.printStackTrace();
|
||||
req.setAttribute(ERROR, "Failed refresh token. See server.log for details. Message is: " + ioe.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logout(HttpServletRequest req) {
|
||||
KeycloakDeployment deployment = getKeycloakDeployment();
|
||||
String refreshToken = (String) req.getSession().getAttribute(REFRESH_TOKEN);
|
||||
|
@ -240,27 +211,11 @@ public abstract class ProductServiceAccountServlet extends HttpServlet {
|
|||
private String getContent(HttpEntity entity) throws IOException {
|
||||
if (entity == null) return null;
|
||||
InputStream is = entity.getContent();
|
||||
try {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
int c;
|
||||
while ((c = is.read()) != -1) {
|
||||
os.write(c);
|
||||
}
|
||||
byte[] bytes = os.toByteArray();
|
||||
String data = new String(bytes);
|
||||
return data;
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return StreamUtil.readString(is);
|
||||
}
|
||||
|
||||
private KeycloakDeployment getKeycloakDeployment() {
|
||||
return (KeycloakDeployment) getServletContext().getAttribute("deployment-" + getClientAuthenticationMethod());
|
||||
return (KeycloakDeployment) getServletContext().getAttribute(KeycloakDeployment.class.getName());
|
||||
}
|
||||
|
||||
private HttpClient getHttpClient() {
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"realm" : "demo",
|
||||
"auth-server-url" : "http://localhost:8080/auth",
|
||||
"ssl-required" : "external",
|
||||
"resource" : "product-sa-client-jwt-auth",
|
||||
"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,16 +13,12 @@
|
|||
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>
|
||||
<h2>Client authentication method: <%= clientAuthMethod %></h2>
|
||||
<p><a href="<%= loginUrl %>">Login</a> | <a href="<%= refreshUrl %>">Refresh token</a> | <a
|
||||
href="<%= logoutUrl %>">Logout</a></p>
|
||||
<p><a href="<%= loginUrl %>">Login</a> | <a href="<%= logoutUrl %>">Logout</a></p>
|
||||
<hr />
|
||||
|
||||
<% if (appError != null) { %>
|
||||
|
|
|
@ -25,22 +25,12 @@
|
|||
|
||||
<servlet>
|
||||
<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-class>org.keycloak.example.ProductServiceAccountServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<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>
|
||||
<url-pattern>/app/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
|
@ -16,11 +16,7 @@
|
|||
-->
|
||||
|
||||
<html>
|
||||
<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>
|
||||
<head>
|
||||
<meta http-equiv="Refresh" content="0; URL=app">
|
||||
</head>
|
||||
</html>
|
|
@ -18,7 +18,6 @@
|
|||
<!-- works with keycloak.json that comes with example -->
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak:1.0">
|
||||
<realm name="demo">
|
||||
<realm-public-key>MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</realm-public-key>
|
||||
<auth-server-url>/auth</auth-server-url>
|
||||
<ssl-required>external</ssl-required>
|
||||
</realm>
|
||||
|
@ -30,7 +29,14 @@
|
|||
<secure-deployment name="product-portal.war">
|
||||
<realm>demo</realm>
|
||||
<resource>product-portal</resource>
|
||||
<credential name="secret">password</credential>
|
||||
<credential name="jwt">
|
||||
<client-keystore-file>classpath:keystore-client.jks</client-keystore-file>
|
||||
<client-keystore-type>JKS</client-keystore-type>
|
||||
<client-keystore-password>storepass</client-keystore-password>
|
||||
<client-key-password>keypass</client-key-password>
|
||||
<client-key-alias>clientkey</client-key-alias>
|
||||
<token-expiration>10</token-expiration>
|
||||
</credential>
|
||||
</secure-deployment>
|
||||
<secure-deployment name="database.war">
|
||||
<realm>demo</realm>
|
||||
|
|
|
@ -79,13 +79,6 @@
|
|||
"email" : "service-account-product-sa-client@placeholder.org",
|
||||
"serviceAccountClientId": "product-sa-client",
|
||||
"realmRoles": [ "user" ]
|
||||
},
|
||||
{
|
||||
"username" : "service-account-product-sa-client-jwt-auth",
|
||||
"enabled": true,
|
||||
"email" : "service-account-product-sa-client-jwt-auth@placeholder.org",
|
||||
"serviceAccountClientId": "product-sa-client-jwt-auth",
|
||||
"realmRoles": [ "user" ]
|
||||
}
|
||||
],
|
||||
"roles" : {
|
||||
|
@ -175,7 +168,11 @@
|
|||
"redirectUris": [
|
||||
"/product-portal/*"
|
||||
],
|
||||
"secret": "password"
|
||||
"clientAuthenticatorType": "client-jwt",
|
||||
"attributes": {
|
||||
"use.jwks.url": "true",
|
||||
"jwks.url": "/product-portal/k_jwks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"clientId": "database-service",
|
||||
|
@ -207,15 +204,6 @@
|
|||
"secret": "password",
|
||||
"serviceAccountsEnabled": true
|
||||
},
|
||||
{
|
||||
"clientId": "product-sa-client-jwt-auth",
|
||||
"enabled": true,
|
||||
"serviceAccountsEnabled": true,
|
||||
"clientAuthenticatorType": "client-jwt",
|
||||
"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=="
|
||||
}
|
||||
},
|
||||
{
|
||||
"clientId": "offline-access-portal",
|
||||
"enabled": true,
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.keycloak.protocol.oidc.utils.JWKSHttpUtils;
|
|||
import org.keycloak.representations.idm.CertificateRepresentation;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.util.CertificateInfoHelper;
|
||||
import org.keycloak.services.util.ResolveRelative;
|
||||
import org.keycloak.util.JWKSUtils;
|
||||
|
||||
/**
|
||||
|
@ -59,6 +60,7 @@ public class ClientPublicKeyLoader implements PublicKeyLoader {
|
|||
OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientModel(client);
|
||||
if (config.isUseJwksUrl()) {
|
||||
String jwksUrl = config.getJwksUrl();
|
||||
jwksUrl = ResolveRelative.resolveRelativeUri(session.getContext().getUri().getRequestUri(), client.getRootUrl(), jwksUrl);
|
||||
JSONWebKeySet jwks = JWKSHttpUtils.sendJwksRequest(session, jwksUrl);
|
||||
return JWKSUtils.getKeysForUse(jwks, JWK.Use.SIG);
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue