Securing Applications and Services Guide Red Hat build of Keycloak 26.0 Red Hat Customer Content Services

Commands:
/realms/{realm-name}/.well-known/openid-configuration
/realms/{realm-name}/protocol/openid-connect/auth
/realms/{realm-name}/protocol/openid-connect/token
/realms/{realm-name}/protocol/openid-connect/userinfo
/realms/{realm-name}/protocol/openid-connect/logout
/realms/{realm-name}/protocol/openid-connect/certs
/realms/{realm-name}/protocol/openid-connect/token/introspect
/realms/{realm-name}/clients-registrations/openid-connect
/realms/{realm-name}/protocol/openid-connect/revoke
/realms/{realm-name}/protocol/openid-connect/auth/device
/realms/{realm-name}/protocol/openid-connect/ext/ciba/auth
curl -d "client_id=myclient" -d "client_secret=40cc097b-2a57-4c17-b36a-8fdf3fc2d578" -d "username=user" -d "password=password" -d "grant_type=password" "http://localhost:8080/realms/master/protocol/openid-connect/token"
npm install keycloak-js
import Keycloak from 'keycloak-js'; const keycloak = new Keycloak({ url: "http://keycloak-server", realm: "my-realm", clientId: "my-app" }); try { const authenticated = await keycloak.init(); if (authenticated) { console.log('User is authenticated'); } else { console.log('User is not authenticated'); } } catch (error) { console.error('Failed to initialize adapter:', error); }
await keycloak.init({ onLoad: 'check-sso', silentCheckSsoRedirectUri: `USD{location.origin}/silent-check-sso.html` });
<!doctype html> <html> <body> <script> parent.postMessage(location.href, location.origin); </script> </body> </html>
await keycloak.init({ onLoad: 'login-required' });
async function fetchUsers() { const response = await fetch('/api/users', { headers: { accept: 'application/json', authorization: `Bearer USD{keycloak.token}` } }); return response.json(); }
try { await keycloak.updateToken(30); } catch (error) { console.error('Failed to refresh token:', error); } const users = await fetchUsers();
await keycloak.init({ flow: 'implicit' })
await keycloak.init({ flow: 'hybrid' });
await keycloak.init({ adapter: 'cordova-native' });
<preference name="AndroidLaunchMode" value="singleTask" />
import Keycloak from 'keycloak-js'; import KeycloakCapacitorAdapter from 'keycloak-capacitor-adapter'; const keycloak = new Keycloak({ url: "http://keycloak-server", realm: "my-realm", clientId: "my-app" }); await keycloak.init({ adapter: KeycloakCapacitorAdapter, });
import Keycloak, { KeycloakAdapter } from 'keycloak-js'; // Implement the 'KeycloakAdapter' interface so that all required methods are guaranteed to be present. const MyCustomAdapter: KeycloakAdapter = { async login(options) { // Write your own implementation here. } // The other methods go here }; const keycloak = new Keycloak({ url: "http://keycloak-server", realm: "my-realm", clientId: "my-app" }); await keycloak.init({ adapter: MyCustomAdapter, });
// Recommended way to initialize the adapter. new Keycloak({ url: "http://keycloak-server", realm: "my-realm", clientId: "my-app" }); // Alternatively a string to the path of the `keycloak.json` file. // Has some performance implications, as it will load the keycloak.json file from the server. // This version might also change in the future and is therefore not recommended. new Keycloak("http://keycloak-server/keycloak.json");
try { const profile = await keycloak.loadUserProfile(); console.log('Retrieved user profile:', profile); } catch (error) { console.error('Failed to load user profile:', error); }
try { const refreshed = await keycloak.updateToken(5); console.log(refreshed ? 'Token was refreshed' : 'Token is still valid'); } catch (error) { console.error('Failed to refresh the token:', error); }
keycloak.onAuthSuccess = () => console.log('Authenticated!');
mkdir myapp && cd myapp
"dependencies": { "keycloak-connect": "file:keycloak-connect-26.0.10.tgz" }
const session = require('express-session'); const Keycloak = require('keycloak-connect'); const memoryStore = new session.MemoryStore(); const keycloak = new Keycloak({ store: memoryStore });
npm install express-session
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" },
npm run start
const kcConfig = { clientId: 'myclient', bearerOnly: true, serverUrl: 'http://localhost:8080', realm: 'myrealm', realmPublicKey: 'MIIBIjANB...' }; const keycloak = new Keycloak({ store: memoryStore }, kcConfig);
const keycloak = new Keycloak({ store: memoryStore, idpHint: myIdP }, kcConfig);
const session = require('express-session'); const memoryStore = new session.MemoryStore(); // Configure session app.use( session({ secret: 'mySecret', resave: false, saveUninitialized: true, store: memoryStore, }) ); const keycloak = new Keycloak({ store: memoryStore });
const keycloak = new Keycloak({ scope: 'offline_access' });
npm install express
const express = require('express'); const app = express();
app.use( keycloak.middleware() );
app.listen(3000, function () { console.log('App listening on port 3000'); });
const app = express(); app.set( 'trust proxy', true ); app.use( keycloak.middleware() );
app.get( '/complain', keycloak.protect(), complaintHandler );
app.get( '/special', keycloak.protect('special'), specialHandler );
app.get( '/extra-special', keycloak.protect('other-app:special'), extraSpecialHandler );
app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
app.get('/apis/me', keycloak.enforcer('user:profile'), userProfileHandler);
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), userProfileHandler);
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), function (req, res) { const token = req.kauth.grant.access_token.content; const permissions = token.authorization ? token.authorization.permissions : undefined; // show user profile });
app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'permissions'}), function (req, res) { const permissions = req.permissions; // show user profile });
keycloak.enforcer('user:profile', {resource_server_id: 'my-apiserver'})
app.get('/protected/resource', keycloak.enforcer(['resource:view', 'resource:write'], { claims: function(request) { return { "http.uri": ["/protected/resource"], "user.agent": // get user agent from request } } }), function (req, res) { // access granted
function protectBySection(token, request) { return token.hasRole( request.params.section ); } app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );
Keycloak.prototype.redirectToLogin = function(req) { const apiReqMatcher = /\/api\//i; return !apiReqMatcher.test(req.originalUrl || req.url); };
app.use( keycloak.middleware( { logout: '/logoff' } ));
https://example.com/logoff?redirect_url=https%3A%2F%2Fexample.com%3A3000%2Flogged%2Fout
app.use( keycloak.middleware( { admin: '/callbacks' } );
LoadModule auth_openidc_module modules/mod_auth_openidc.so ServerName USD{HOSTIP} <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /var/www/html #this is required by mod_auth_openidc OIDCCryptoPassphrase a-random-secret-used-by-apache-oidc-and-balancer OIDCProviderMetadataURL USD{KC_ADDR}/realms/USD{KC_REALM}/.well-known/openid-configuration OIDCClientID USD{CLIENT_ID} OIDCClientSecret USD{CLIENT_SECRET} OIDCRedirectURI http://USD{HOSTIP}/USD{CLIENT_APP_NAME}/redirect_uri # maps the preferred_username claim to the REMOTE_USER environment variable OIDCRemoteUserClaim preferred_username <Location /USD{CLIENT_APP_NAME}/> AuthType openid-connect Require valid-user </Location> </VirtualHost>
<plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-maven-plugin</artifactId> <version>5.0.0.Final</version> <configuration> <feature-packs> <feature-pack> <location>wildfly@maven(org.jboss.universe:community-universe)#32.0.1.Final</location> </feature-pack> <feature-pack> <groupId>org.keycloak</groupId> <artifactId>keycloak-saml-adapter-galleon-pack</artifactId> <version>26.0.10</version> </feature-pack> </feature-packs> <layers> <layer>core-server</layer> <layer>web-server</layer> <layer>jaxrs-server</layer> <layer>datasources-web-server</layer> <layer>webservices</layer> <layer>keycloak-saml</layer> <layer>keycloak-client-saml</layer> <layer>keycloak-client-saml-ejb</layer> </layers> </configuration> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin>
<plugin> <groupId>org.wildfly.plugins</groupId> <artifactId>wildfly-jar-maven-plugin</artifactId> <version>11.0.2.Final</version> <configuration> <feature-packs> <feature-pack> <location>wildfly@maven(org.jboss.universe:community-universe)#32.0.1.Final</location> </feature-pack> <feature-pack> <groupId>org.keycloak</groupId> <artifactId>keycloak-saml-adapter-galleon-pack</artifactId> <version>26.0.10</version> </feature-pack> </feature-packs> <layers> <layer>core-server</layer> <layer>web-server</layer> <layer>jaxrs-server</layer> <layer>datasources-web-server</layer> <layer>webservices</layer> <layer>keycloak-saml</layer> <layer>keycloak-client-saml</layer> <layer>keycloak-client-saml-ejb</layer> </layers> </configuration> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin>
<plugin> <groupId>org.jboss.eap.plugins</groupId> <artifactId>eap-maven-plugin</artifactId> <version>1.0.0.Final-redhat-00014</version> <configuration> <channels> <channel> <manifest> <groupId>org.jboss.eap.channels</groupId> <artifactId>eap-8.0</artifactId> </manifest> </channel> </channels> <feature-packs> <feature-pack> <location>org.keycloak:keycloak-saml-adapter-galleon-pack</location> </feature-pack> </feature-packs> <layers> <layer>core-server</layer> <layer>web-server</layer> <layer>jaxrs-server</layer> <layer>datasources-web-server</layer> <layer>webservices</layer> <layer>keycloak-saml</layer> <layer>keycloak-client-saml</layer> <layer>keycloak-client-saml-ejb</layer> </layers> </configuration> <executions> <execution> <goals> <goal>package</goal> </goals> </execution> </executions> </plugin>
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:keycloak:saml:adapter https://www.keycloak.org/schema/keycloak_saml_adapter_1_10.xsd"> <SP entityID="http://localhost:8081/sales-post-sig/" sslPolicy="EXTERNAL" nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" logoutPage="/logout.jsp" forceAuthentication="false" isPassive="false" turnOffChangeSessionIdOnLogin="false" autodetectBearerOnly="false"> <Keys> <Key signing="true" > <KeyStore resource="/WEB-INF/keystore.jks" password="store123"> <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/> <Certificate alias="http://localhost:8080/sales-post-sig/"/> </KeyStore> </Key> </Keys> <PrincipalNameMapping policy="FROM_NAME_ID"/> <RoleIdentifiers> <Attribute name="Role"/> </RoleIdentifiers> <RoleMappingsProvider id="properties-based-role-mapper"> <Property name="properties.resource.location" value="/WEB-INF/role-mappings.properties"/> </RoleMappingsProvider> <IDP entityID="idp" signaturesRequired="true"> <SingleSignOnService requestBinding="POST" bindingUrl="http://localhost:8081/realms/demo/protocol/saml" /> <SingleLogoutService requestBinding="POST" responseBinding="POST" postBindingUrl="http://localhost:8081/realms/demo/protocol/saml" redirectBindingUrl="http://localhost:8081/realms/demo/protocol/saml" /> <Keys> <Key signing="true"> <KeyStore resource="/WEB-INF/keystore.jks" password="store123"> <Certificate alias="demo"/> </KeyStore> </Key> </Keys> </IDP> </SP> </keycloak-saml-adapter>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd" version="6.0"> <module-name>customer-portal</module-name> <security-constraint> <web-resource-collection> <web-resource-name>Admins</web-resource-name> <url-pattern>/admin/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>Customers</web-resource-name> <url-pattern>/customers/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>user</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> <login-config> <auth-method>KEYCLOAK-SAML</auth-method> <realm-name>this is ignored currently</realm-name> </login-config> <security-role> <role-name>admin</role-name> </security-role> <security-role> <role-name>user</role-name> </security-role> </web-app>
<extensions> <extension module="org.keycloak.keycloak-saml-adapter-subsystem"/> </extensions> <profile> <subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1"> <secure-deployment name="WAR MODULE NAME.war"> <SP entityID="APPLICATION URL"> </SP> </secure-deployment> </subsystem> </profile>
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1"> <secure-deployment name="saml-post-encryption.war"> <SP entityID="http://localhost:8080/sales-post-enc/" sslPolicy="EXTERNAL" nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" logoutPage="/logout.jsp" forceAuthentication="false"> <Keys> <Key signing="true" encryption="true"> <KeyStore resource="/WEB-INF/keystore.jks" password="store123"> <PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/> <Certificate alias="http://localhost:8080/sales-post-enc/"/> </KeyStore> </Key> </Keys> <PrincipalNameMapping policy="FROM_NAME_ID"/> <RoleIdentifiers> <Attribute name="Role"/> </RoleIdentifiers> <IDP entityID="idp"> <SingleSignOnService signRequest="true" validateResponseSignature="true" requestBinding="POST" bindingUrl="http://localhost:8080/realms/saml-demo/protocol/saml"/> <SingleLogoutService validateRequestSignature="true" validateResponseSignature="true" signRequest="true" signResponse="true" requestBinding="POST" responseBinding="POST" postBindingUrl="http://localhost:8080/realms/saml-demo/protocol/saml" redirectBindingUrl="http://localhost:8080/realms/saml-demo/protocol/saml"/> <Keys> <Key signing="true" > <KeyStore resource="/WEB-INF/keystore.jks" password="store123"> <Certificate alias="saml-demo"/> </KeyStore> </Key> </Keys> </IDP> </SP> </secure-deployment> </subsystem>
samesite-cookie(mode=None, cookie-pattern=JSESSIONID)
<context-param> <param-name>keycloak.sessionIdMapperUpdater.classes</param-name> <param-value>org.keycloak.adapters.saml.wildfly.infinispan.InfinispanSessionCacheIdMapperUpdater</param-value> </context-param>
package org.keycloak.adapters.saml; public class SamlPrincipal implements Serializable, Principal { /** * Get full saml assertion * * @return */ public AssertionType getAssertion() { } /** * Get SAML subject sent in assertion * * @return */ public String getSamlSubject() { } /** * Subject nameID format * * @return */ public String getNameIDFormat() { } @Override public String getName() { } /** * Convenience function that gets Attribute value by attribute name * * @param name * @return */ public List<String> getAttributes(String name) { } /** * Convenience function that gets Attribute value by attribute friendly name * * @param friendlyName * @return */ public List<String> getFriendlyAttributes(String friendlyName) { } /** * Convenience function that gets first value of an attribute by attribute name * * @param name * @return */ public String getAttribute(String name) { } /** * Convenience function that gets first value of an attribute by attribute name * * * @param friendlyName * @return */ public String getFriendlyAttribute(String friendlyName) { } /** * Get set of all assertion attribute names * * @return */ public Set<String> getAttributeNames() { } /** * Get set of all assertion friendly attribute names * * @return */ public Set<String> getFriendlyNames() { } }
<error-page> <error-code>403</error-code> <location>/ErrorHandler</location> </error-page>
public class SamlAuthenticationError implements AuthenticationError { public static enum Reason { EXTRACTION_FAILURE, INVALID_SIGNATURE, ERROR_STATUS } public Reason getReason() { return reason; } public StatusResponseType getStatus() { return status; } }
package example; import java.io.InputStream; import org.keycloak.adapters.saml.SamlConfigResolver; import org.keycloak.adapters.saml.SamlDeployment; import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder; import org.keycloak.adapters.saml.config.parsers.ResourceLoader; import org.keycloak.adapters.spi.HttpFacade; import org.keycloak.saml.common.exceptions.ParsingException; public class SamlMultiTenantResolver implements SamlConfigResolver { @Override public SamlDeployment resolve(HttpFacade.Request request) { String host = request.getHeader("Host"); String realm = null; if (host.contains("tenant1")) { realm = "tenant1"; } else if (host.contains("tenant2")) { realm = "tenant2"; } else { throw new IllegalStateException("Not able to guess the keycloak-saml.xml to load"); } InputStream is = getClass().getResourceAsStream("/" + realm + "-keycloak-saml.xml"); if (is == null) { throw new IllegalStateException("Not able to find the file /" + realm + "-keycloak-saml.xml"); } ResourceLoader loader = new ResourceLoader() { @Override public InputStream getResourceAsStream(String path) { return getClass().getResourceAsStream(path); } }; try { return new DeploymentBuilder().build(is, loader); } catch (ParsingException e) { throw new IllegalStateException("Cannot load SAML deployment", e); } } }
<web-app> <context-param> <param-name>keycloak.config.resolver</param-name> <param-value>example.SamlMultiTenantResolver</param-value> </context-param> </web-app>
<samlp:Status> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Responder"> <samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"/> </samlp:StatusCode> <samlp:StatusMessage>authentication_expired</samlp:StatusMessage> </samlp:Status>
<SP entityID="sp" sslPolicy="ssl" nameIDPolicyFormat="format" forceAuthentication="true" isPassive="false" keepDOMAssertion="true" autodetectBearerOnly="false"> </SP>
<Keys> <Key signing="true" > </Key> </Keys>
<Keys> <Key signing="true" > <KeyStore resource="/WEB-INF/keystore.jks" password="store123"> <PrivateKey alias="myPrivate" password="test123"/> <Certificate alias="myCertAlias"/> </KeyStore> </Key> </Keys>
<Keys> <Key signing="true"> <PrivateKeyPem> 2341251234AB31234==231BB998311222423522334 </PrivateKeyPem> <CertificatePem> 211111341251234AB31234==231BB998311222423522334 </CertificatePem> </Key> </Keys>
<SP ...> <PrincipalNameMapping policy="FROM_NAME_ID"/> </SP> <SP ...> <PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="email" /> </SP>
<RoleIdentifiers> <Attribute name="Role"/> <Attribute name="member"/> <Attribute name="memberOf"/> </RoleIdentifiers>
<RoleIdentifiers> </RoleIdentifiers> <RoleMappingsProvider id="properties-based-role-mapper"> <Property name="properties.resource.location" value="/WEB-INF/role-mappings.properties"/> </RoleMappingsProvider> <IDP> </IDP>
<RoleMappingsProvider id="properties-based-role-mapper"> <Property name="properties.file.location" value="/opt/mappers/roles.properties"/> </RoleMappingsProvider>
<RoleMappingsProvider id="properties-based-role-mapper"> <Property name="properties.resource.location" value="/WEB-INF/conf/roles.properties"/> </RoleMappingsProvider>
roleA=roleX,roleY roleB= kc_user=roleZ
role\u0020A=roleX,roleY
<IDP entityID="idp" signaturesRequired="true" signatureAlgorithm="RSA_SHA1" signatureCanonicalizationMethod="http://www.w3.org/2001/10/xml-exc-c14n#"> </IDP>
<AllowedClockSkew unit="MILLISECONDS">3500</AllowedClockSkew>
<SingleSignOnService signRequest="true" validateResponseSignature="true" requestBinding="post" bindingUrl="url"/>
<SingleLogoutService validateRequestSignature="true" validateResponseSignature="true" signRequest="true" signResponse="true" requestBinding="redirect" responseBinding="post" postBindingUrl="posturl" redirectBindingUrl="redirecturl">
<IDP entityID="idp"> <Keys> <Key signing="true"> <KeyStore resource="/WEB-INF/keystore.jks" password="store123"> <Certificate alias="demo"/> </KeyStore> </Key> </Keys> </IDP>
<HttpClient connectionPoolSize="10" disableTrustManager="false" allowAnyHostname="false" clientKeystore="classpath:keystore.jks" clientKeystorePassword="pwd" truststore="classpath:truststore.jks" truststorePassword="pwd" proxyUrl="http://proxy/" socketTimeout="5000" connectionTimeout="6000" connectionTtl="500" />
install httpd mod_auth_mellon mod_ssl openssl
mkdir /etc/httpd/saml2
<Location / > MellonEnable info MellonEndpointPath /mellon/ MellonSPMetadataFile /etc/httpd/saml2/mellon_metadata.xml MellonSPPrivateKeyFile /etc/httpd/saml2/mellon.key MellonSPCertFile /etc/httpd/saml2/mellon.crt MellonIdPMetadataFile /etc/httpd/saml2/idp_metadata.xml </Location> <Location /private > AuthType Mellon MellonEnable auth Require valid-user </Location>
MellonSecureCookie On MellonCookieSameSite none
fqdn=`hostname` mellon_endpoint_url="https://USD{fqdn}/mellon" mellon_entity_id="USD{mellon_endpoint_url}/metadata" file_prefix="USD(echo "USDmellon_entity_id" | sed 's/[^A-Za-z.]/_/g' | sed 's/__*/_/g')"
/usr/libexec/mod_auth_mellon/mellon_create_metadata.sh USDmellon_entity_id USDmellon_endpoint_url
mv USD{file_prefix}.cert /etc/httpd/saml2/mellon.crt mv USD{file_prefix}.key /etc/httpd/saml2/mellon.key mv USD{file_prefix}.xml /etc/httpd/saml2/mellon_metadata.xml
curl -k -o /etc/httpd/saml2/idp_metadata.xml https://USDidp_host/realms/test_realm/protocol/saml/descriptor
apachectl configtest
systemctl restart httpd.service
auth: token: realm: http://localhost:8080/realms/master/protocol/docker-v2/auth service: docker-test issuer: http://localhost:8080/realms/master
REGISTRY_AUTH_TOKEN_REALM: http://localhost:8080/realms/master/protocol/docker-v2/auth REGISTRY_AUTH_TOKEN_SERVICE: docker-test REGISTRY_AUTH_TOKEN_ISSUER: http://localhost:8080/realms/master
docker login localhost:5000 -u USDusername Password: ******* Login Succeeded
Authorization: bearer eyJhbGciOiJSUz
Authorization: basic BASE64(client-id + ':' + client-secret)
curl -X POST -d '{ "clientId": "myclient" }' -H "Content-Type:application/json" -H "Authorization: bearer eyJhbGciOiJSUz..." http://localhost:8080/realms/master/clients-registrations/default
String token = "eyJhbGciOiJSUz..."; ClientRepresentation client = new ClientRepresentation(); client.setClientId(CLIENT_ID); ClientRegistration reg = ClientRegistration.create() .url("http://localhost:8080", "myrealm") .build(); reg.auth(Auth.token(token)); client = reg.create(client); String registrationAccessToken = client.getRegistrationAccessToken();
export PATH=USDPATH:USDKEYCLOAK_HOME/bin kcreg.sh
c:\> set PATH=%PATH%;%KEYCLOAK_HOME%\bin c:\> kcreg
kcreg.sh config credentials --server http://localhost:8080 --realm demo --user user --client reg-cli kcreg.sh create -s clientId=my_client -s 'redirectUris=["http://localhost:8980/myapp/*"]' kcreg.sh get my_client
c:\> kcreg config credentials --server http://localhost:8080 --realm demo --user user --client reg-cli c:\> kcreg create -s clientId=my_client -s "redirectUris=[\"http://localhost:8980/myapp/*\"]" c:\> kcreg get my_client
kcreg.sh config truststore --trustpass USDPASSWORD ~/.keycloak/truststore.jks
c:\> kcreg config truststore --trustpass %PASSWORD% %HOMEPATH%\.keycloak\truststore.jks
kcreg.sh help
c:\> kcreg help
kcreg.sh config initial-token USDTOKEN kcreg.sh create -s clientId=myclient
kcreg.sh create -s clientId=myclient -t USDTOKEN
c:\> kcreg config initial-token %TOKEN% c:\> kcreg create -s clientId=myclient
c:\> kcreg create -s clientId=myclient -t %TOKEN%
kcreg.sh create -f client-template.json -s clientId=myclient -s baseUrl=/myclient -s 'redirectUris=["/myclient/*"]' -o
C:\> kcreg create -f client-template.json -s clientId=myclient -s baseUrl=/myclient -s "redirectUris=[\"/myclient/*\"]" -o
kcreg.sh get myclient
C:\> kcreg get myclient
kcreg.sh get myclient -e install > keycloak.json
C:\> kcreg get myclient -e install > keycloak.json
kcreg.sh get myclient > myclient.json vi myclient.json kcreg.sh update myclient -f myclient.json
C:\> kcreg get myclient > myclient.json C:\> notepad myclient.json C:\> kcreg update myclient -f myclient.json
kcreg.sh update myclient -s enabled=false -d redirectUris
C:\> kcreg update myclient -s enabled=false -d redirectUris
kcreg.sh update myclient --merge -d redirectUris -f mychanges.json
C:\> kcreg update myclient --merge -d redirectUris -f mychanges.json
kcreg.sh delete myclient
C:\> kcreg delete myclient
/realms/{realm}/protocol/openid-connect/token
access_token: .....
refresh_token: .....
expires_in: ....
{ "error" : "...." "error_description" : "...." }
curl -X POST -d "client_id=starting-client" -d "client_secret=the client secret" --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" -d "subject_token=...." --data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:refresh_token" -d "audience=target-client" http://localhost:8080/realms/myrealm/protocol/openid-connect/token
access_token: ....
refresh_token: ....
expires_in: 3600
curl -X POST -d "client_id=starting-client" -d "client_secret=the client secret" --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" -d "subject_token=...." --data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:access_token" -d "requested_issuer=google" http://localhost:8080/realms/myrealm/protocol/openid-connect/token
{ "access_token" : "....", "expires_in" : 3600 "account-link-url" : "https://...." }
{ "error" : "....", "error_description" : "..." "account-link-url" : "https://...." }
curl -X POST -d "client_id=starting-client" -d "client_secret=the client secret" --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" -d "subject_token=...." -d "subject_issuer=myOidcProvider" --data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:access_token" -d "audience=target-client" http://localhost:8080/realms/myrealm/protocol/openid-connect/token
access_token: ....
refresh_token: ....
expires_in: 3600
curl -X POST -d "client_id=starting-client" -d "client_secret=the client secret" --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" -d "subject_token=...." --data-urlencode "requested_token_type=urn:ietf:params:oauth:token-type:access_token" -d "audience=target-client" -d "requested_subject=wburke" http://localhost:8080/realms/myrealm/protocol/openid-connect/token
curl -X POST -d "client_id=starting-client" -d "client_secret=the client secret" --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" -d "requested_subject=wburke" http://localhost:8080/realms/myrealm/protocol/openid-connect/token
<dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-admin-client</artifactId> <version>999.0.0-SNAPSHOT</version> </dependency>
import org.keycloak.admin.client.Keycloak; import org.keycloak.representations.idm.RealmRepresentation; Keycloak keycloak = Keycloak.getInstance( "http://localhost:8080", "master", "admin", "password", "admin-cli"); RealmRepresentation realm = keycloak.realm("master").toRepresentation();
<dependencies> <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-authz-client</artifactId> <version>999.0.0-SNAPSHOT</version> </dependency> </dependencies>
{ "realm": "hello-world-authz", "auth-server-url" : "http://localhost:8080", "resource" : "hello-world-authz-service", "credentials": { "secret": "secret" } }
// create a new instance based on the configuration defined in a keycloak.json located in your classpath AuthzClient authzClient = AuthzClient.create();
// create a new instance based on the configuration defined in keycloak.json AuthzClient authzClient = AuthzClient.create(); // create an authorization request AuthorizationRequest request = new AuthorizationRequest(); // send the entitlement request to the server in order to // obtain an RPT with all permissions granted to the user AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize(request); String rpt = response.getToken(); System.out.println("You got an RPT: " + rpt); // now you can use the RPT to access protected resources on the resource server
// create a new instance based on the configuration defined in keycloak.json AuthzClient authzClient = AuthzClient.create(); // create an authorization request AuthorizationRequest request = new AuthorizationRequest(); // add permissions to the request based on the resources and scopes you want to check access request.addPermission("Default Resource"); // send the entitlement request to the server in order to // obtain an RPT with permissions for a single resource AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize(request); String rpt = response.getToken(); System.out.println("You got an RPT: " + rpt); // now you can use the RPT to access protected resources on the resource server
// create a new instance based on the configuration defined in keycloak.json AuthzClient authzClient = AuthzClient.create(); // create a new resource representation with the information we want ResourceRepresentation newResource = new ResourceRepresentation(); newResource.setName("New Resource"); newResource.setType("urn:hello-world-authz:resources:example"); newResource.addScope(new ScopeRepresentation("urn:hello-world-authz:scopes:view")); ProtectedResource resourceClient = authzClient.protection().resource(); ResourceRepresentation existingResource = resourceClient.findByName(newResource.getName()); if (existingResource != null) { resourceClient.delete(existingResource.getId()); } // create the resource on the server ResourceRepresentation response = resourceClient.create(newResource); String resourceId = response.getId(); // query the resource using its newly generated id ResourceRepresentation resource = resourceClient.findById(resourceId); System.out.println(resource);
// create a new instance based on the configuration defined in keycloak.json AuthzClient authzClient = AuthzClient.create(); // send the authorization request to the server in order to // obtain an RPT with all permissions granted to the user AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize(); String rpt = response.getToken(); // introspect the token TokenIntrospectionResponse requestingPartyToken = authzClient.protection().introspectRequestingPartyToken(rpt); System.out.println("Token status is: " + requestingPartyToken.getActive()); System.out.println("Permissions granted by the server: "); for (Permission granted : requestingPartyToken.getPermissions()) { System.out.println(granted); }
"credentials": { "secret": "19666a4f-32dd-4049-b082-684c74115f28" }
"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 } }
"credentials": { "secret-jwt": { "secret": "19666a4f-32dd-4049-b082-684c74115f28", "algorithm": "HS512" } }
<dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-policy-enforcer</artifactId> <version>999.0.0-SNAPSHOT</version> </dependency>
{ "enforcement-mode" : "ENFORCING", "paths": [ { "path" : "/users/*", "methods" : [ { "method": "GET", "scopes" : ["urn:app.com:scopes:view"] }, { "method": "POST", "scopes" : ["urn:app.com:scopes:create"] } ] } ] }
{ "paths": [ { "path": "/protected/resource", "claim-information-point": { "claims": { "claim-from-request-parameter": "{request.parameter['a']}", "claim-from-header": "{request.header['b']}", "claim-from-cookie": "{request.cookie['c']}", "claim-from-remoteAddr": "{request.remoteAddr}", "claim-from-method": "{request.method}", "claim-from-uri": "{request.uri}", "claim-from-relativePath": "{request.relativePath}", "claim-from-secure": "{request.secure}", "claim-from-json-body-object": "{request.body['/a/b/c']}", "claim-from-json-body-array": "{request.body['/d/1']}", "claim-from-body": "{request.body}", "claim-from-static-value": "static value", "claim-from-multiple-static-value": ["static", "value"], "param-replace-multiple-placeholder": "Test {keycloak.access_token['/custom_claim/0']} and {request.parameter['a']}" } } } ] }
{ "paths": [ { "path": "/protected/resource", "claim-information-point": { "http": { "claims": { "claim-a": "/a", "claim-d": "/d", "claim-d0": "/d/0", "claim-d-all": [ "/d/0", "/d/1" ] }, "url": "http://mycompany/claim-provider", "method": "POST", "headers": { "Content-Type": "application/x-www-form-urlencoded", "header-b": [ "header-b-value1", "header-b-value2" ], "Authorization": "Bearer {keycloak.access_token}" }, "parameters": { "param-a": [ "param-a-value1", "param-a-value2" ], "param-subject": "{keycloak.access_token['/sub']}", "param-user-name": "{keycloak.access_token['/preferred_username']}", "param-other-claims": "{keycloak.access_token['/custom_claim']}" } } } } ] }
{ "paths": [ { "path": "/protected/resource", "claim-information-point": { "claims": { "claim-from-static-value": "static value", "claim-from-multiple-static-value": ["static", "value"] } } } ] }
public class MyClaimInformationPointProviderFactory implements ClaimInformationPointProviderFactory<MyClaimInformationPointProvider> { @Override public String getName() { return "my-claims"; } @Override public void init(PolicyEnforcer policyEnforcer) { } @Override public MyClaimInformationPointProvider create(Map<String, Object> config) { return new MyClaimInformationPointProvider(config); } }
public class MyClaimInformationPointProvider implements ClaimInformationPointProvider { private final Map<String, Object> config; public MyClaimInformationPointProvider(Map<String, Object> config) { this.config = config; } @Override public Map<String, List<String>> resolve(HttpFacade httpFacade) { Map<String, List<String>> claims = new HashMap<>(); // put whatever claim you want into the map return claims; } }
HttpServletRequest request = // obtain javax.servlet.http.HttpServletRequest AuthorizationContext authzContext = (AuthorizationContext) request.getAttribute(AuthorizationContext.class.getName());
if (authzContext.hasResourcePermission("Project Resource")) { // user can access the Project Resource } if (authzContext.hasResourcePermission("Admin Resource")) { // user can access administration resources } if (authzContext.hasScopePermission("urn:project.com:project:create")) { // user can create new projects }
if (User.hasRole('user')) { // user can access the Project Resource } if (User.hasRole('admin')) { // user can access administration resources } if (User.hasRole('project-manager')) { // user can create new projects }
ClientAuthorizationContext clientContext = ClientAuthorizationContext.class.cast(authzContext); AuthzClient authzClient = clientContext.getClient();
truststore: path_to_your_trust_store
truststore-password: trust_store_password