From 543375d32c07919f332444bbdb5468c113e45092 Mon Sep 17 00:00:00 2001 From: mposolda Date: Tue, 4 Oct 2016 17:55:53 +0200 Subject: [PATCH] KEYCLOAK-3643 Remove public-key references in keycloak.json . Add more docs about client authentication --- SUMMARY.adoc | 1 + book.json | 8 +++ topics/oidc/java/client-authentication.adoc | 74 +++++++++++++++++++++ topics/oidc/java/fuse/camel.adoc | 1 - topics/oidc/java/fuse/cxf-separate.adoc | 1 - topics/oidc/java/fuse/fuse-admin.adoc | 1 - topics/oidc/java/java-adapter-config.adoc | 14 +++- topics/oidc/java/jboss-adapter.adoc | 2 - topics/oidc/java/jetty9-adapter.adoc | 1 - topics/oidc/java/spring-boot-adapter.adoc | 1 - topics/oidc/nodejs-adapter.adoc | 1 - 11 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 topics/oidc/java/client-authentication.adoc diff --git a/SUMMARY.adoc b/SUMMARY.adoc index 2583642342..f0745d06fe 100644 --- a/SUMMARY.adoc +++ b/SUMMARY.adoc @@ -31,6 +31,7 @@ ... link:topics/oidc/java/adapter_error_handling.adoc[Error Handling] ... link:topics/oidc/java/logout.adoc[Logout] ... link:topics/oidc/java/params_forwarding.adoc[Parameters Forwarding] + ... link:topics/oidc/java/client-authentication.adoc[Client Authentication] ... link:topics/oidc/java/multi-tenancy.adoc[Multi Tenancy] ... link:topics/oidc/java/application-clustering.adoc[Application Clustering] diff --git a/book.json b/book.json index 66067958f1..68d0d08daa 100755 --- a/book.json +++ b/book.json @@ -18,9 +18,17 @@ "community": true, "product": false, "images": "keycloak-images", + "developerguide": { + "name": "Server Developer Guide", + "link": "https://keycloak.gitbooks.io/server-developer-guide/content/" + }, "adminguide": { "name": "Server Administration Guide", "link": "https://keycloak.gitbooks.io/server-adminstration-guide/content/" + }, + "installguide": { + "name": "Server Installation and Configuration Guide", + "link": "https://keycloak.gitbooks.io/server-installation-and-configuration/content/" } } } diff --git a/topics/oidc/java/client-authentication.adoc b/topics/oidc/java/client-authentication.adoc new file mode 100644 index 0000000000..a8b647e40c --- /dev/null +++ b/topics/oidc/java/client-authentication.adoc @@ -0,0 +1,74 @@ +[[_client_authentication_adapter]] +==== Client Authentication + +When confidential OIDC client needs to send backchannel request (eg. exchange code for the token, or refresh token) it needs to authenticate +against {{book.project.name}} server. By default, there are 2 possibilities how to authenticate client: + +===== Client ID and Client Secret + +This is traditional method described in OAuth2 specification. Client has a secret, which needs to be known to both adapter (application) and {{book.project.name}} server. +You can simply generate the secret for particular client in {{book.project.name}} admin console and then paste this secret in the `keycloak.json` file on the application's side: + + +[source] +---- +"credentials": { + "secret": "19666a4f-32dd-4049-b082-684c74115f28" +} +---- + +===== Client authentication with signed JWT + +This is based on the https://tools.ietf.org/html/rfc7523[RFC7523] specification. It works this way: + +* Client must have the private key and certificate. In case of {{book.project.name}} this is available through the traditional `keystore` file, which is either available +on client application's classpath or somewhere on the filesystem. + +* Once the client application is started, it allows to download it's public key in https://self-issued.info/docs/draft-ietf-jose-json-web-key.html[JWKS] format on URL +like http://myhost.com/myapp/k_jwks assuming that http://myhost.com/myapp is the base URL of your client application. This URL can be used by the {{book.project.name}} (see below). + +* During authentication, client generates JWT token and signs it with his private key and sends it to the {{book.project.name}} in +the particular backchannel request (eg. code-to-token request) in the `client_assertion` parameter. + +* {{book.project.name}} must have public key or certificate of the client, so that it can verify the signature on JWT. In {{book.project.name}} you either +need to configure client credentials for your client. First you need to choose `Signed JWT` as the method of authenticating your client in the tab `Credentials` in admin console. +Then you can choose either to: +** Configure the JWKS URL where {{book.project.name}} can download the client's public keys. This can be URL like http://myhost.com/myapp/k_jwks (see details above). This is flexible as +client can rotate it's keys anytime and {{book.project.name}} will then always download new keys when needed without need to change something in it's configuration. More acurately, {{book.project.name}} +will download new keys when it sees the token signed by unknown `kid` (Key ID). +** Upload the client's public key or certificate - either in PEM format, in JWK format or from keystore. With this option, public key is hardcoded and +needs to be changed anytime when client generates new keypair. +You can even generate your own keystore from {{book.project.name}} admin console if you don't have your own ready. +See {{book.adminguide.link}}[{{book.adminguide.name}}] for more details of setup in {{book.project.name}} admin console. + +For setup on adapter's side you need to have something like this in your `keycloak.json` file: + +[source] +---- +"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 + } +} +---- + +With this configuration, the keystore file `keystore-client.jks` must be available on classpath in your WAR. If you don't use prefix `classpath:` +you can point to any file on the filesystem where client application is running. + +{% if book.community %} +For inspiration, you can take a look at the examples distribution into the main demo example into the `product-portal` application. + + +===== Add your own client authentication method + +This is possible. You will need to implement both client's side and server's side providers. See `Authentication SPI` section +in {{book.developerguide.link}}[{{book.developerguide.name}}] for more details. + +{% endif %} + + diff --git a/topics/oidc/java/fuse/camel.adoc b/topics/oidc/java/fuse/camel.adoc index 3e5668b71a..1cd7a21564 100644 --- a/topics/oidc/java/fuse/camel.adoc +++ b/topics/oidc/java/fuse/camel.adoc @@ -20,7 +20,6 @@ The roles, security constraint mappings and {{book.project.name}} adapter config - diff --git a/topics/oidc/java/fuse/cxf-separate.adoc b/topics/oidc/java/fuse/cxf-separate.adoc index 178775b56b..eb10c8d076 100644 --- a/topics/oidc/java/fuse/cxf-separate.adoc +++ b/topics/oidc/java/fuse/cxf-separate.adoc @@ -26,7 +26,6 @@ injected `KeycloakJettyAuthenticator` inside. The configuration may look like th - diff --git a/topics/oidc/java/fuse/fuse-admin.adoc b/topics/oidc/java/fuse/fuse-admin.adoc index 48410203c4..e90849f598 100644 --- a/topics/oidc/java/fuse/fuse-admin.adoc +++ b/topics/oidc/java/fuse/fuse-admin.adoc @@ -27,7 +27,6 @@ sshRealm=keycloak { "realm": "demo", "resource": "ssh-jmx-admin-client", - "realm-public-key": "MIGfMA...", "ssl-required" : "external", "auth-server-url" : "http://localhost:8080/auth", "credentials": { diff --git a/topics/oidc/java/java-adapter-config.adoc b/topics/oidc/java/java-adapter-config.adoc index e2c0e6489f..f9ce2073b2 100644 --- a/topics/oidc/java/java-adapter-config.adoc +++ b/topics/oidc/java/java-adapter-config.adoc @@ -32,7 +32,8 @@ This is what one might look like: "client-keystore" : "path/to/client-keystore.jks", "client-keystore-password" : "geheim", "client-key-password" : "geheim", - "token-minimum-time-to-live" : 10 + "token-minimum-time-to-live" : 10, + "min-time-between-jwks-requests" : 10 } ---- @@ -54,7 +55,9 @@ resource:: realm-public-key:: PEM format of the realm public key. You can obtain this from the administration console. - This is _OPTIONAL_. If not set the adapter will download this from {{book.project.name}}. + This is _OPTIONAL_ and it's not recommended to set it. If not set, the adapter will download this from {{book.project.name}} and + it will always re-download it when needed (eg. {{book.project.name}} rotate it's keys). However if realm-public-key is set, then adapter + will never download new keys from {{book.project.name}}, so when {{book.project.name}} rotate it's keys, adapter will break. auth-server-url:: The base URL of the {{book.project.name}} server. All other {{book.project.name}} pages and REST service endpoints are derived from this. It is usually of the form `$$https://host:port/auth$$`. @@ -198,3 +201,10 @@ token-minimum-time-to-live:: This is especially useful when the access token is sent to another REST client where it could expire before being evaluated. This value should never exceed the realm's access token lifespan. This is _OPTIONAL_. The default value is `0` seconds, so adapter will refresh access token just if it's expired. + +min-time-between-jwks-requests:: + Amount of time, in seconds, specifying minimum interval between two requests to {{book.project.name}} to retrieve new public keys. + It is 10 seconds by default. + Adapter will always try to download new public key when it recognize token with unknown `kid` . However it won't try it more + than once per 10 seconds (by default). This is to avoid DoS when attacker sends lots of tokens with bad `kid` forcing adapter + to send lots of requests to {{book.project.name}}. diff --git a/topics/oidc/java/jboss-adapter.adoc b/topics/oidc/java/jboss-adapter.adoc index e9036004d0..0686c1c51b 100644 --- a/topics/oidc/java/jboss-adapter.adoc +++ b/topics/oidc/java/jboss-adapter.adoc @@ -260,7 +260,6 @@ This metadata is instead defined within server configuration (i.e. `standalone.x demo - MIGfMA0GCSqGSIb3DQEBAQUAA http://localhost:8081/auth external customer-portal @@ -284,7 +283,6 @@ If you have multiple deployments secured by the same realm you can share the rea ---- - MIGfMA0GCSqGSIb3DQEBA... http://localhost:8080/auth external diff --git a/topics/oidc/java/jetty9-adapter.adoc b/topics/oidc/java/jetty9-adapter.adoc index 70946313a6..7f0bd8bf8d 100755 --- a/topics/oidc/java/jetty9-adapter.adoc +++ b/topics/oidc/java/jetty9-adapter.adoc @@ -92,7 +92,6 @@ You'll just have to figure out how the json settings match to the `org.keycloak. - MIGfMA0GCSqGSIb3DQEBAQUAA4 diff --git a/topics/oidc/java/spring-boot-adapter.adoc b/topics/oidc/java/spring-boot-adapter.adoc index 01027cbc01..6cce21170d 100755 --- a/topics/oidc/java/spring-boot-adapter.adoc +++ b/topics/oidc/java/spring-boot-adapter.adoc @@ -41,7 +41,6 @@ For example: keycloak.realm = demorealm -keycloak.realmKey = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLCWYuxXmsmfV+Xc9Ik8QET8lD4wuHrJAXbbutS2O/eMjQQLNK7QDX/k/XhOkhxP0YBEypqeXeGaeQJjCxDhFjJXQuewUEMlmSja3IpoJ9/hFn4Cns4m7NGO+rtvnfnwgVfsEOS5EmZhRddp+40KBPPJfTH6Vgu6KjQwuFPj6DTwIDAQAB keycloak.auth-server-url = http://127.0.0.1:8080/auth keycloak.ssl-required = external keycloak.resource = demoapp diff --git a/topics/oidc/nodejs-adapter.adoc b/topics/oidc/nodejs-adapter.adoc index 3fe80de868..e09e124b12 100644 --- a/topics/oidc/nodejs-adapter.adoc +++ b/topics/oidc/nodejs-adapter.adoc @@ -18,7 +18,6 @@ admin console when we provisioned this app. { "realm": "example-realm", - "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB", "auth-server-url": "http://localhost:8080/auth", "ssl-required": "external", "resource": "example-app",