- Что, зачем и почему?
- 1. Java adapters
- 1.1. Java adapter configuration
- 1.2. JBoss EAP/WildFly adapter
- 1.6. Spring Security adapter
- 1.7. Java servlet filter adapter
- 1.9. CLI / Desktop Applications
- 1.12. Logout
- 1.14. Client authentication
- 1.16. Application clustering
- 1.1. Java adapter configuration
- 1.14. Client authentication
- 2.7. JavaScript Adapter Reference
- 3.6. Protecting resources
- 5. Other OpenID Connect libraries
- 5.1. Endpoints
- 5.3. Flows
- Methods
- Авторизация вызовов сервисов с использованием keycloak
- Взаимодействие фронта с микросервисами. axios
- Импорт и экспорт в keycloak
- Интеграция с vue.js
- Интеграция со spring boot
- Как работают права в kubernetes.
- Настройка клиента
- Приватная сеть с поддержкой ip multicast
- Связь
Что, зачем и почему?
Как-то сидя на карантине захотелось мне написать pet-проект, да не простой, а с использованием микросервисной архитектуры (ну или около того). На начальном этапе одного сервиса для фронта и одного для бэка, в принципе, будет достаточно. Если вдруг в будущем понадобятся ещё сервисы, то будем добавлять их по мере необходимости.
Начнём с авторизации. В качестве протокола авторизации будем использовать OAuth2, т.к. стильно, модно, молодёжно, да и использовать токены для получения доступа к сервисам, одно удовольствие, особенно в микросервисной архитектуре.
Для авторизации пусть будет отдельный сервис. И раз уж мы решили использовать Spring Boot, то сможет ли он нам чем-то помочь в создании этого сервиса? Например, каким-нибудь готовым решением, таким как Authorization Server? Правильно, не сможет.
Проект Spring Security OAuth в котором находился Authorization Server задеприкейтили, а сам проект Authorization Server стал эксперементальным и на данный момент находится в активной разработке. Что делать? Как быть?
Можно написать свой сервис авторизации. Если подсматривать в исходники задеприкейченого Authorization Server, то, вероятно, задача будет не такой уж и страшной. Правда, при этом возможны ситуации когда реализацию каких-то интересных фич будет негде подсмотреть и решать вопросы о том «быть или не быть», «правильно ли так делать или чё-то фигня какая-то» придётся исходя из собственного опыта, что может привести к получению на выходе большого количества неприглядных костылей.
Что делать, если городить собственные велосипеды не хочется или хочется, но на данный момент это кажется весьма долгим процессом, а результат нужен уже вчера? Есть ли какие-то готовые решения, способные решить данную проблему? Да, есть. Давайте одно из таких решений и рассмотрим.
1. Java adapters
Keycloak comes with a range of different adapters for Java application. Selecting the correct adapter depends on the target platform.
All Java adapters share a set of common configuration options described in the Java Adapters Config chapter.
1.1. Java adapter configuration
Each Java adapter supported by Keycloak can be configured by a simple JSON file.
This is what one might look like:
{
"realm" : "demo",
"resource" : "customer-portal",
"realm-public-key" : "MIGfMA0GCSqGSIb3D...31LwIDAQAB",
"auth-server-url" : "https://localhost:8443",
"ssl-required" : "external",
"use-resource-role-mappings" : false,
"enable-cors" : true,
"cors-max-age" : 1000,
"cors-allowed-methods" : "POST, PUT, DELETE, GET",
"cors-exposed-headers" : "WWW-Authenticate, My-custom-exposed-Header",
"bearer-only" : false,
"enable-basic-auth" : false,
"expose-token" : true,
"verify-token-audience" : true,
"credentials" : {
"secret" : "234234-234234-234234"
},
"connection-pool-size" : 20,
"socket-timeout-millis" : 5000,
"connection-timeout-millis" : 6000,
"connection-ttl-millis" : 500,
"disable-trust-manager" : false,
"allow-any-hostname" : false,
"truststore" : "path/to/truststore.jks",
"truststore-password" : "geheim",
"client-keystore" : "path/to/client-keystore.jks",
"client-keystore-password" : "geheim",
"client-key-password" : "geheim",
"token-minimum-time-to-live" : 10,
"min-time-between-jwks-requests" : 10,
"public-key-cache-ttl" : 86400,
"redirect-rewrite-rules" : {
"^/wsmaster/api/(.*)$" : "/api/$1"
}
}
You can use ${…}
enclosure for system property replacement. For example ${jboss.server.config.dir}
would be replaced by /path/to/Keycloak
.
Replacement of environment variables is also supported via the env
prefix, for example ${env.MY_ENVIRONMENT_VARIABLE}
.
The initial config file can be obtained from the admin console. This can be done by opening the admin console, select Clients
from the menu and clicking
on the corresponding client. Once the page for the client is opened click on the Installation
tab and select Keycloak OIDC JSON
.
Here is a description of each configuration option:
- realm
REQUIRED.
Name of the realm.- resource
REQUIRED. The client-id of the application. Each application has a client-id that is used to identify the application.
- realm-public-key
OPTIONAL and it’s not recommended to set it. PEM format of the realm public key. You can obtain this from the Admin Console.
If not set, the adapter will download this from Keycloak and
it will always re-download it when needed (eg. Keycloak rotates its keys). However if realm-public-key is set, then adapter
will never download new keys from Keycloak, so when Keycloak rotate it’s keys, adapter will break.- auth-server-url
REQUIRED. The base URL of the Keycloak server. All other Keycloak pages and REST service endpoints are derived from this. It is usually of the form
https://host:port
.- ssl-required
OPTIONAL. Ensures that all communication to and from the Keycloak server is over HTTPS.
In production this should be set toall
.- confidential-port
OPTIONAL. The confidential port used by the Keycloak server for secure connections over SSL/TLS.
The default value is 8443.- use-resource-role-mappings
OPTIONAL.
If set to true, the adapter will look inside the token for application level role mappings for the user. If false, it will look at the realm level for user role mappings.
The default value is false.- public-client
OPTIONAL. If set to true, the adapter will not send credentials for the client to Keycloak.
The default value is false.- enable-cors
OPTIONAL. This enables CORS support. It will handle CORS preflight requests. It will also look into the access token to determine valid origins.
The default value is false.- cors-max-age
OPTIONAL.
If CORS is enabled, this sets the value of theAccess-Control-Max-Age
header.
If not set, this header is not returned in CORS responses.- cors-allowed-methods
OPTIONAL.
If CORS is enabled, this sets the value of theAccess-Control-Allow-Methods
header.
This should be a comma-separated string.
If not set, this header is not returned in CORS responses.- cors-allowed-headers
OPTIONAL.
If CORS is enabled, this sets the value of theAccess-Control-Allow-Headers
header.
This should be a comma-separated string.
If not set, this header is not returned in CORS responses.- cors-exposed-headers
OPTIONAL.
If CORS is enabled, this sets the value of theAccess-Control-Expose-Headers
header.
This should be a comma-separated string.
If not set, this header is not returned in CORS responses.- bearer-only
OPTIONAL.
This should be set to true for services. If enabled the adapter will not attempt to authenticate users, but only verify bearer tokens.
The default value is false.- autodetect-bearer-only
This should be set to true if your application serves both a web application and web services (for example SOAP or REST).
It allows you to redirect unauthenticated users of the web application to the Keycloak login page,
but send an HTTP401
status code to unauthenticated SOAP or REST clients instead as they would not understand a redirect to the login page.
Keycloak auto-detects SOAP or REST clients based on typical headers likeX-Requested-With
,SOAPAction
orAccept
.
The default value is false.- enable-basic-auth
OPTIONAL.
This tells the adapter to also support basic authentication. If this option is enabled, then secret must also be provided.
The default value is false.- expose-token
OPTIONAL.
Iftrue
, an authenticated browser client (via a JavaScript HTTP invocation) can obtain the signed access token via the URLroot/k_query_bearer_token
.
The default value is false.- credentials
REQUIRED only for clients with ‘Confidential’ access type. Specify the credentials of the application. This is an object notation where the key is the credential type and the value is the value of the credential type.
Currently password and jwt is supported. T- connection-pool-size
OPTIONAL.
This config option defines how many connections to the Keycloak server should be pooled.
The default value is20
.- socket-timeout-millis
OPTIONAL.
Timeout for socket waiting for data after establishing the connection in milliseconds.
Maximum time of inactivity between two data packets.
A timeout value of zero is interpreted as an infinite timeout.
A negative value is interpreted as undefined (system default if applicable).
The default value is-1
.- onnection-timeout-millis
Timeout for establishing the connection with the remote host in milliseconds.
A timeout value of zero is interpreted as an infinite timeout.
A negative value is interpreted as undefined (system default if applicable).
The default value is-1
.- connection-ttl-millis
OPTIONAL.
Connection time-to-live for client in milliseconds.
A value less than or equal to zero is interpreted as an infinite value.
The default value is-1
.- disable-trust-manager
OPTIONAL.
If the Keycloak server requires HTTPS and this config option is set totrue
you do not have to specify a truststore.
This setting should only be used during development and never in production as it will disable verification of SSL certificates.
The default value isfalse
.- allow-any-hostname
OPTIONAL.
If the Keycloak server requires HTTPS and this config option is set totrue
the Keycloak server’s certificate is validated via the truststore,
but host name validation is not done.
This setting should only be used during development and never in production as it will disable verification of SSL certificates.
This setting may be useful in test environments This is OPTIONAL.
The default value isfalse
.- proxy-url
The URL for the HTTP proxy if one is used.
- truststore
The value is the file path to a truststore file.
If you prefix the path withclasspath:
, then the truststore will be obtained from the deployment’s classpath instead.
Used for outgoing HTTPS communications to the Keycloak server.
Client making HTTPS requests need a way to verify the host of the server they are talking to.
This is what the trustore does.
The keystore contains one or more trusted host certificates or certificate authorities.
You can create this truststore by extracting the public certificate of the Keycloak server’s SSL keystore.
REQUIRED unlessssl-required
isnone
ordisable-trust-manager
istrue
.- truststore-password
Password for the truststore.
REQUIRED iftruststore
is set and the truststore requires a password.- client-keystore
OPTIONAL.
This is the file path to a keystore file.
This keystore contains client certificate for two-way SSL when the adapter makes HTTPS requests to the Keycloak server.- client-keystore-password
REQUIRED if
client-keystore
is set.
Password for the client keystore.- client-key-password
REQUIRED if
client-keystore
is set.
Password for the client’s key.- always-refresh-token
If true, the adapter will refresh token in every request.
Warning — when enabled this will result in a request to Keycloak for every request to your application.- register-node-at-startup
If true, then adapter will send registration request to Keycloak.
It’s false by default and useful only when application is clustered.
See Application Clustering for details- register-node-period
Period for re-registration adapter to Keycloak.
Useful when application is clustered.
See Application Clustering for details- token-store
Possible values are session and cookie.
Default is session, which means that adapter stores account info in HTTP Session.
Alternative cookie means storage of info in cookie.
See Application Clustering for details- token-cookie-path
When using a cookie store, this option sets the path of the cookie used to store account info. If it’s a relative path,
then it is assumed that the application is running in a context root, and is interpreted relative to that context root.
If it’s an absolute path, then the absolute path is used to set the cookie path. Defaults to use paths relative to the context root.- principal-attribute
OpenID Connect ID Token attribute to populate the UserPrincipal name with.
If token attribute is null, defaults tosub
.
Possible values aresub
,preferred_username
,email
,name
,nickname
,given_name
,family_name
.- turn-off-change-session-id-on-login
OPTIONAL. The session id is changed by default on a successful login on some platforms to plug a security attack vector. Change this to true if you want to turn this off
The default value is false.- token-minimum-time-to-live
OPTIONAL.
Amount of time, in seconds, to preemptively refresh an active access token with the Keycloak server before it expires.
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.
The default value is0
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 Keycloak 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 unknownkid
. 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 badkid
forcing adapter
to send lots of requests to Keycloak.- public-key-cache-ttl
Amount of time, in seconds, specifying maximum interval between two requests to Keycloak to retrieve new public keys.
It is 86400 seconds (1 day) by default.
Adapter will always try to download new public key when it recognize token with unknownkid
. If it recognize token with knownkid
, it will
just use the public key downloaded previously. However at least once per this configured interval (1 day by default) will be new
public key always downloaded even if thekid
of token is already known.- ignore-oauth-query-parameter
Defaults to
false
, if set totrue
will turn off processing of theaccess_token
query parameter for bearer token processing. Users will not be able to authenticate
if they only pass in anaccess_token
- redirect-rewrite-rules
If needed, specify the Redirect URI rewrite rule. This is an object notation where the key is the regular expression to which the Redirect URI is to be matched and the value is the replacement String.
$
character can be used for backreferences in the replacement String.- verify-token-audience
If set to
true
, then during authentication with the bearer token, the adapter will verify whether the token contains this
client name (resource) as an audience. The option is especially useful for services, which primarily serve requests authenticated
by the bearer token. This is set tofalse
by default, however for improved security, it is recommended to enable this.
See Audience Support for more details about audience
1.2. JBoss EAP/WildFly adapter
To be able to secure WAR apps deployed on JBoss EAP, WildFly or JBoss AS, you must install and configure the
Keycloak adapter subsystem. You then have two options to secure your WARs.
You can provide an adapter config file in your WAR and change the auth-method to KEYCLOAK within web.xml.
Alternatively, you do not have to modify your WAR at all and you can secure it via the Keycloak adapter subsystem configuration in the configuration file, such as
standalone.xml
.
Both methods are described in this section.
Adapters are available as a separate archive depending on what server version you are using.
1.6. Spring Security adapter
To secure an application with Spring Security and Keycloak, add this adapter as a dependency to your project.
You then have to provide some extra beans in your Spring Security configuration file and add the Keycloak security filter to your pipeline.
Unlike the other Keycloak Adapters, you should not configure your security in web.xml.
However, keycloak.json is still required.
In order for Single Sign Out to work properly you have to define a session listener.
as a Spring bean (in Spring Boot environments using Spring Security adapter)
1.7. Java servlet filter adapter
If you are deploying your Java Servlet application on a platform where there is no Keycloak adapter you opt to use the servlet filter adapter.
This adapter works a bit differently than the other adapters. You do not define security constraints in web.xml.
Instead you define a filter mapping using the Keycloak servlet filter adapter to secure the url patterns you want to secure.
In the snippet above there are two url-patterns.
/protected/* are the files we want protected, while the /keycloak/* url-pattern handles callbacks from the Keycloak server.
If you need to exclude some paths beneath the configured url-patterns
you can use the Filter init-param keycloak.config.skipPattern
to configure
a regular expression that describes a path-pattern for which the keycloak filter should immediately delegate to the filter-chain.
By default no skipPattern is configured.
Patterns are matched against the requestURI
without the context-path
. Given the context-path /myapp
a request for /myapp/index.html
will be matched with /index.html
against the skip pattern.
Note that you should configure your client in the Keycloak Admin Console with an Admin URL that points to a secured section covered by the filter’s url-pattern.
The Admin URL will make callbacks to the Admin URL to do things like backchannel logout.
So, the Admin URL in this example should be http[s]://hostname/{context-root}/keycloak
.
If you need to customize the session ID mapper, you can configure the fully qualified name of the class in the Filter init-param keycloak.config.idMapper. Session ID mapper is a mapper that is used to map user IDs and session IDs. By default org.keycloak.adapters.spi.InMemorySessionIdMapper is configured.
The Keycloak filter has the same configuration parameters as the other adapters except you must define them as filter init params instead of context params.
To use this filter, include this maven artifact in your WAR poms:
Using on osgi
The servlet filter adapter is packaged as an OSGi bundle, and thus is usable in a generic OSGi environment (R6 and above) with HTTP Service and HTTP Whiteboard.
Configuration
First, the adapter needs to be registered as a servlet filter with the OSGi HTTP Service. The most common ways to do this are programmatic (for example via bundle activator) and declarative (using OSGi annotations).
We recommend using the latter since it simplifies the process of dynamically registering and un-registering the filter:
The above snippet uses OSGi declarative service specification to expose the filter as an OSGI service under javax.servlet.Filter
class.
Once the class is published in the OSGi service registry, it is going to be picked up by OSGi HTTP Service implementation and used for filtering requests for the specified servlet context. This will trigger Keycloak adapter for every request that matches servlet context path filter path.
Since the component is put under the control of OSGi Configuration Admin Service, it’s properties can be configured dynamically.
To do that, either create a mypackage.KeycloakFilter.cfg
file under the standard config location for your OSGi runtime:
or use interactive console, if your runtime allows for that:
If you need more control, for example, providing custom KeycloakConfigResolver
to implement multi tenancy, you can register the filter programmatically:
Please refer to Apache Felix HTTP Service for more info on programmatic registration.
1.9. CLI / Desktop Applications
Keycloak supports securing desktop
(for example Swing, JavaFX) or CLI applications via the
KeycloakInstalled
adapter by performing the authentication step via the system browser.
The KeycloakInstalled
adapter supports a desktop
and a manual
variant. The desktop variant uses the system browser
to gather the user credentials. The manual variant
reads the user credentials from STDIN
.
1.12. Logout
You can log out of a web application in multiple ways.
For Jakarta EE servlet containers, you can call HttpServletRequest.logout()
. For other browser applications, you can redirect the browser to
http://auth-server/realms/{realm-name}/protocol/openid-connect/logout
, which logs the user out if that user has an SSO session with his browser. The actual logout is done once
the user confirms the logout. You can optionally include parameters such as id_token_hint
, post_logout_redirect_uri
, client_id
and others as described in the
OpenID Connect RP-Initiated Logout. As a result, that logout does not need to be explicitly confirmed
by the user if you include the id_token_hint
parameter. After logout, the user will be automatically redirected to the specified post_logout_redirect_uri
as long as it is provided.
Note that you need to include either the client_id
or id_token_hint
parameter in case that post_logout_redirect_uri
is included.
If you want to avoid logging out of an external identity provider as part of the logout process, you can supply the parameter initiating_idp
, with the value being
the identity (alias) of the identity provider in question. This parameter is useful when the logout endpoint is invoked as part of single logout initiated by the external identity provider.
The parameter initiating_idp
is the supported parameter of the Keycloak logout endpoint in addition to the parameters described in the RP-Initiated Logout specification.
When using the HttpServletRequest.logout()
option the adapter executes a back-channel POST call against the Keycloak server passing the refresh token.
If the method is executed from an unprotected page (a page that does not check for a valid token) the refresh token can be unavailable and, in that case,
the adapter skips the call. For this reason, using a protected page to execute HttpServletRequest.logout()
is recommended so that current tokens are always
taken into account and an interaction with the Keycloak server is performed if needed.
1.14. Client authentication
When a confidential OIDC client needs to send a backchannel request (for example, to exchange code for the token, or to refresh the token) it needs to authenticate against the Keycloak server. By default, there are three ways to authenticate the client: client ID and client secret, client authentication with signed JWT, or client authentication with signed JWT using client secret.
Client authentication with signed jwt
This is based on the RFC7523 specification. It works this way:
For set up on the adapter side you need to have something like this in your keycloak.json
file:
With this configuration, the keystore file keystore-client.jks
must be available on classpath in your WAR. If you do not use the prefix classpath:
you can point to any file on the file system where the client application is running.
For inspiration, you can take a look at the examples distribution into the main demo example into the product-portal
application.
1.16. Application clustering
This chapter is related to supporting clustered applications deployed to JBoss EAP, WildFly and JBoss AS.
There are a few options available depending on whether your application is:
Dealing with clustering is not quite as simple as for a regular application. Mainly due to the fact that both the browser and the server-side application
sends requests to Keycloak, so it’s not as simple as enabling sticky sessions on your load balancer.
Registration of application nodes
The previous section describes how Keycloak can send logout request to node associated with a specific HTTP session.
However, in some cases admin may want to propagate admin tasks to all registered cluster nodes, not just one of them.
For example to push a new not before policy to the application or to logout all users from the application.
In this case Keycloak needs to be aware of all application cluster nodes, so it can send the event to all of them.
To achieve this, we support auto-discovery mechanism:
Sending startup registrations and periodic re-registration is disabled by default as it’s only required for some clustered applications.
To enable the feature edit the WEB-INF/keycloak.json
file for your application and add:
This means the adapter will send the registration request on startup and re-register every 10 minutes.
In the Keycloak Admin Console you can specify the maximum node re-registration timeout (should be larger than register-node-period from
the adapter configuration). You can also manually add and remove cluster nodes in through the Admin Console, which is useful if you don’t want to rely
on the automatic registration feature or if you want to remove stale application nodes in the event your not using the automatic unregistration feature.
1.1. Java adapter configuration
Each Java adapter supported by Keycloak can be configured by a simple JSON file.
This is what one might look like:
{
"realm" : "demo",
"resource" : "customer-portal",
"realm-public-key" : "MIGfMA0GCSqGSIb3D...31LwIDAQAB",
"auth-server-url" : "https://localhost:8443",
"ssl-required" : "external",
"use-resource-role-mappings" : false,
"enable-cors" : true,
"cors-max-age" : 1000,
"cors-allowed-methods" : "POST, PUT, DELETE, GET",
"cors-exposed-headers" : "WWW-Authenticate, My-custom-exposed-Header",
"bearer-only" : false,
"enable-basic-auth" : false,
"expose-token" : true,
"verify-token-audience" : true,
"credentials" : {
"secret" : "234234-234234-234234"
},
"connection-pool-size" : 20,
"socket-timeout-millis" : 5000,
"connection-timeout-millis" : 6000,
"connection-ttl-millis" : 500,
"disable-trust-manager" : false,
"allow-any-hostname" : false,
"truststore" : "path/to/truststore.jks",
"truststore-password" : "geheim",
"client-keystore" : "path/to/client-keystore.jks",
"client-keystore-password" : "geheim",
"client-key-password" : "geheim",
"token-minimum-time-to-live" : 10,
"min-time-between-jwks-requests" : 10,
"public-key-cache-ttl" : 86400,
"redirect-rewrite-rules" : {
"^/wsmaster/api/(.*)$" : "/api/$1"
}
}
You can use ${…} enclosure for system property replacement. For example ${jboss.server.config.dir} would be replaced by /path/to/Keycloak.
Replacement of environment variables is also supported via the env prefix, for example ${env.MY_ENVIRONMENT_VARIABLE}.
1.14. Client authentication
When a confidential OIDC client needs to send a backchannel request (for example, to exchange code for the token, or to refresh the token) it needs to authenticate against the Keycloak server. By default, there are three ways to authenticate the client:
Client authentication with signed jwt
This is based on the RFC7523 specification. It works this way:
For set up on the adapter side you need to have something like this in your keycloak.json
file:
With this configuration, the keystore file keystore-client.jks
must be available on classpath in your WAR. If you do not use the prefix classpath:
you can point to any file on the file system where the client application is running.
For inspiration, you can take a look at the examples distribution into the main demo example into the product-portal
application.
2.7. JavaScript Adapter Reference
Methods
init(options)
Called to initialize the adapter.
Options is an Object, where:
Returns a promise that resolves when initialization completes.
login(options)
Redirects to login form.
Options is an optional Object, where:
redirectUri — Specifies the uri to redirect to after login.
prompt — This parameter allows to slightly customize the login flow on the Keycloak server side.
For example enforce displaying the login screen in case of valuelogin
. See Parameters Forwarding Section
for the details and all the possible values of theprompt
parameter.maxAge — Used just if user is already authenticated. Specifies maximum time since the authentication of user happened. If user is already authenticated for longer time than
maxAge
, the SSO is ignored and he will need to re-authenticate again.loginHint — Used to pre-fill the username/email field on the login form.
scope — Used to forward the scope parameter to the Keycloak login endpoint. Use a space-delimited list of scopes. Those typically
reference Client scopes defined on particular client. Note that the scopeopenid
will be
always be added to the list of scopes by the adapter. For example, if you enter the scope optionsaddress phone
, then the request
to Keycloak will contain the scope parameterscope=openid address phone
.idpHint — Used to tell Keycloak to skip showing the login page and automatically redirect to the specified identity
provider instead. More info in the Identity Provider documentation.acr — Contains the information about
acr
claim, which will be sent insideclaims
parameter to the Keycloak server. Typical usage
is for step-up authentication. Example of use{ values: ["silver", "gold"], essential: true }
. See OpenID Connect specification
and Step-up authentication documentation for more details.action — If value is
register
then user is redirected to registration page, if the value isUPDATE_PASSWORD
then the user will redirected to the reset password page (if not authenticated will send user to login page first and redirect after authenticated), otherwise to login page.locale — Sets the ‘ui_locales’ query param in compliance with section 3.1.2.1 of the OIDC 1.0 specification.
cordovaOptions — Specifies the arguments that are passed to the Cordova in-app-browser (if applicable). Options
hidden
andlocation
are not affected by these arguments. All available options are defined at https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/. Example of use:{ zoom: "no", hardwareback: "yes" }
;
createLoginUrl(options)
Returns the URL to login form.
Options is an optional Object, which supports same options as the function login
.
logout(options)
Redirects to logout.
Options is an Object, where:
redirectUri — Specifies the uri to redirect to after logout.
createLogoutUrl(options)
Returns the URL to logout the user.
Options is an Object, where:
redirectUri — Specifies the uri to redirect to after logout.
register(options)
Redirects to registration form. Shortcut for login with option action = ‘register’
Options are same as for the login method but ‘action’ is set to ‘register’
createRegisterUrl(options)
Returns the url to registration page. Shortcut for createLoginUrl with option action = ‘register’
Options are same as for the createLoginUrl method but ‘action’ is set to ‘register’
accountManagement()
Redirects to the Account Management Console.
createAccountUrl(options)
Returns the URL to the Account Management Console.
Options is an Object, where:
redirectUri — Specifies the uri to redirect to when redirecting back to the application.
hasRealmRole(role)
Returns true if the token has the given realm role.
hasResourceRole(role, resource)
Returns true if the token has the given role for the resource (resource is optional, if not specified clientId is used).
loadUserProfile()
Loads the users profile.
Returns a promise that resolves with the profile.
For example:
isTokenExpired(minValidity)
Returns true if the token has less than minValidity seconds left before it expires (minValidity is optional, if not specified 0 is used).
updateToken(minValidity)
If the token expires within minValidity seconds (minValidity is optional, if not specified 5 is used) the token is refreshed.
If the session status iframe is enabled, the session status is also checked.
Returns a promise that resolves with a boolean indicating whether or not the token has been refreshed.
For example:
clearToken()
Clear authentication state, including tokens.
This can be useful if application has detected the session was expired, for example if updating token fails.
Invoking this results in onAuthLogout callback listener being invoked.
3.6. Protecting resources
To secure a resource with an application role for a different app:
To secure a resource with a realm role:
The keycloak-enforcer method operates in two modes, depending on the value of the response_mode configuration option.
If response_mode is set to token, permissions are obtained from the server on behalf of the subject represented by the bearer token that was sent to your application. In this case, a new access token is issued by Keycloak with the permissions granted by the server.
Prefer this mode when your application is using sessions and you want to cache previous decisions from the server, as well automatically handle refresh tokens. This mode is especially useful for applications acting as a client and resource server.
If response_mode is set to permissions (default mode), the server only returns the list of granted permissions, without issuing a new access token. In addition to not issuing a new token, this method exposes the permissions granted by the server through the request as follows:
Regardless of the response_mode in use, the keycloak.enforcer method will first try to check the permissions within the bearer token that was sent to your application. If the bearer token already carries the expected permissions, there is no need
to interact with the server to obtain a decision.
This is specially useful when your clients are capable of obtaining access tokens from the server with the expected permissions before accessing a protected resource, so they can use some capabilities provided by Keycloak Authorization Services such as incremental authorization and avoid additional requests to the server when keycloak.enforcer is enforcing access to the resource.
By default, the policy enforcer will use the client_id defined to the application (for instance, via keycloak.json) to
reference a client in Keycloak that supports Keycloak Authorization Services. In this case, the client can not be public given
that it is actually a resource server.
5. Other OpenID Connect libraries
Keycloak can be secured by supplied adapters that are usually easier to use and provide better integration with Keycloak. However, if an adapter is not available for your programming language, framework, or platform you might opt to use a generic OpenID Connect Relying Party (RP) library instead.
5.1. Endpoints
The most important endpoint to understand is the well-known
configuration endpoint. It lists endpoints and other configuration options relevant to the OpenID Connect implementation in Keycloak. The endpoint is:
To obtain the full URL, add the base URL for Keycloak and replace {realm-name}
with the name of your realm. For example:
http://localhost:8080/realms/master/.well-known/openid-configuration
Some RP libraries retrieve all required endpoints from this endpoint, but for others you might need to list the endpoints individually.
5.3. Flows
Methods
init(options)
Called to initialize the adapter.
Options is an Object, where:
Returns a promise that resolves when initialization completes.
login(options)
Redirects to login form.
Options is an optional Object, where:
redirectUri — Specifies the uri to redirect to after login.
prompt — This parameter allows to slightly customize the login flow on the Keycloak server side.
For example enforce displaying the login screen in case of valuelogin
. See Parameters Forwarding Section
for the details and all the possible values of theprompt
parameter.maxAge — Used just if user is already authenticated. Specifies maximum time since the authentication of user happened. If user is already authenticated for longer time than
maxAge
, the SSO is ignored and he will need to re-authenticate again.loginHint — Used to pre-fill the username/email field on the login form.
scope — Used to forward the scope parameter to the Keycloak login endpoint. Use a space-delimited list of scopes. Those typically
reference Client scopes defined on particular client. Note that the scopeopenid
will be
always be added to the list of scopes by the adapter. For example, if you enter the scope optionsaddress phone
, then the request
to Keycloak will contain the scope parameterscope=openid address phone
.idpHint — Used to tell Keycloak to skip showing the login page and automatically redirect to the specified identity
provider instead. More info in the Identity Provider documentation.acr — Contains the information about
acr
claim, which will be sent insideclaims
parameter to the Keycloak server. Typical usage
is for step-up authentication. Example of use{ values: ["silver", "gold"], essential: true }
. See OpenID Connect specification
and Step-up authentication documentation for more details.action — If value is
register
then user is redirected to registration page, if the value isUPDATE_PASSWORD
then the user will redirected to the reset password page (if not authenticated will send user to login page first and redirect after authenticated), otherwise to login page.locale — Sets the ‘ui_locales’ query param in compliance with section 3.1.2.1 of the OIDC 1.0 specification.
cordovaOptions — Specifies the arguments that are passed to the Cordova in-app-browser (if applicable). Options
hidden
andlocation
are not affected by these arguments. All available options are defined at https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-inappbrowser/. Example of use:{ zoom: "no", hardwareback: "yes" }
;
createLoginUrl(options)
Returns the URL to login form.
Options is an optional Object, which supports same options as the function login .
logout(options)
Redirects to logout.
Options is an Object, where:
redirectUri — Specifies the uri to redirect to after logout.
createLogoutUrl(options)
Авторизация вызовов сервисов с использованием keycloak
При работе с микросервисной архитектурой иногда возникают требования авторизованных вызовов между сервисами. В случаях, когда инициатором взаимодействия является какой-то внутренний процесс или служба, нам где-то нужно брать токен доступа. В качестве решения данного вопроса мы можем использовать Client Credentials Flow, чтобы получить токен из keycloak (исходный код примера доступен по ссылке).
Для начала создадим нового клиента, под которым будут авторизоваться наши сервисы:
Для возможности авторизации сервиса нам нужно изменить тип доступа («Access Type») на «confidential» и включить флаг «Service accounts Enabled». В остальном конфигурация не отличается от конфигурации по умолчанию:
Если нам необходимо, чтобы у сервисов, авторизованных под данным клиентом, была своя роль, добавим ее в роли:
Далее эту роль необходимо добавить клиенту. На вкладке «Service Account Roles» выбираем необходимую роль — в нашем случае роль «SERVICE»:
Сохраняем client_id и client_secret для дальнейшего использования в сервисах для авторизации:
Взаимодействие фронта с микросервисами. axios
Для взаимодействия с микросервисами будем использовать библиотеку AXIOS.
Поскольку у нас будут 2 типа запросов:авторизированные (для работы с микросервисами) и неавторизированные (для самой авторизации и refresh токена) — для каждого типа запроса будем использовать отдельный инстанс axios.
Итак, создадим утилитарный класс с нашими axios – AxiosInstance.js и добавим в него экземпляр axios для авторизированных запросов:
export const axiosInstance = axios.create({
headers: { Authorization: `Bearer ${getToken()}` }
});
Здесь мы добавили в заголовок параметр авторизации со значением – bearer-token. Он будет посылаться в каждом запросе.
Кроме того, перед каждым запросом с фронта нам придется проверять валидность токена и, в случае если он невалиден (истек срок действия например) — делать его refresh. После этого в запросе уйдет уже обновленный валидный token.
Итак, добавим вызов метода verifyToken в axios request interceptor:
axiosInstance.interceptors.request.use(request => {
verifyToken().catch(() => refreshToken(getRefreshToken()));
return request;
});
В случае, если токен невалиден, метод кидает исключение, в обработке которого вызываем обновление токена методом refreshToken, а в качестве его аргумента передаем refresh_token который хранится в нашем localStorage и доступный через утилитарный метод getRefreshToken.
Теперь осталось только проверить статус ответа в авторизированных запросах. Сделаем это через axios response interceptors:
axiosInstance.interceptors.response.use(response => {
if (response?.headers?.authorization) {
setToken(response.headers.authorization);
}
return response;
}, error => {
if (error?.response?.status === 401) {
logout();
}
return Promise.reject(error);
});
Импорт и экспорт в keycloak
В Keycloak есть возможность импортировать и экспортировать конфигурации ваших realm’ов. Это можно использовать, например, для переноса конфигураций между различными инстансами Keycloak. Или, что более вероятно, для того чтобы можно было запускать Keycloak локально с уже готовой конфигурацией и использовать его для разработки.
Для того чтобы экспортировать конфигурацию из Keycloak, нужно перейти на страницу Export, выбрать данные, которые нужно экспортировать и нажать Export.
После этого выгрузится файл realm-export.json с конфигурацией того realm в котором мы сейчас находимся. При этом различные пароли и секреты в этом файле будут в виде **********, поэтому, прежде чем куда-то импортировать этот файл, нужно заменить все такие значения на корректные. Либо сделать это после импорта через адиминку.
Импортировать данные можно на странице Import. Либо в yml-файле Docker Compose, если вы его используете. Для этого нужно указать в переменной окружения KEYCLOAK_IMPORT путь до ранее экспортированного файла и примонтировать этот файл в контейнер с помощью volumes. Итоговый файл приведен ниже.
Интеграция с vue.js
Начнём с создания проекта. Создать проект можно, например, с помощью Vue CLI.
vue create list-keep-front
После ввода данной команды необходимо выбрать версию Vue. Т.к. в проекте будет использоваться библиотека Vuetify, которая на данный момент не поддерживает Vue 3, нужно выбрать Vue 2.
После этого нужно перейти в проект и добавить Vuetify.
vue add vuetify
После добавления Vuetify вместе с самой библиотекой в проект будут добавлены каталоги components и assets. В components будет компонент HelloWorld, с примером страницы на Vuetify, а в assets ресурсы, использующиеся в компоненте HelloWorld. Эти каталоги нам не пригодятся, поэтому можно их удалить.
Для удобства разработки сконфигурируем devServer следующим образом: запускать приложение будем на порту 8081, все запросы, которые начинаются с /api/ будем проксировать на адрес, на котором запущенно приложение на Spring Boot.
Интеграция со spring boot
В первую очередь давайте создадим проект на Spring Boot. Сделать это можно, например, с помощью Spring Initializr. В качестве системы автоматической сборки проекта будем использовать Gradle. В качестве языка пусть будет Java 15. Никаких дополнительных зависимостей в соответствующем блоке Dependencies добавлять не требуется.
Для того чтобы в Spring Boot проекте появилась поддержка Keycloak, необходимо добавить в него Spring Boot Adapter и добавить в конфиг приложения конфигурацию для Keycloak.
Для того чтобы добавить Spring Boot Adapter, необходимо в проект подключить зависимость org.keycloak:keycloak-spring-boot-starter и сам adapter org.keycloak.bom:keycloak-adapter-bom. Сделать это можно изменив файл build.gradle следующим образом:
...
dependencyManagement {
imports {
mavenBom 'org.keycloak.bom:keycloak-adapter-bom:12.0.3'
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.keycloak:keycloak-spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
...
Проблемы в Java 14
Если запустить Spring Boot приложение на Java 14 или выше, то при обращении к вашим методам API, закрытым ролями кейклока, будут возникать ошибки видаjava.lang.NoClassDefFoundError: java/security/acl/Group
. Связано это с тем, что в Java 9 этот, а так же другие классы из этого пакета были задеприкейчины и удалены в Java 14. Исправить данную проблему, вроде как, собираются в 13-й версии Keycloak. Чтобы решить её сейчас, можно использовать Java 13 или ниже, либо, вместо сервера приложений Tomcat, который используется в Spring Boot по умолчанию, использовать, например, Undertow. Для того чтобы подключить в Spring Boot приложение Undertow, нужно добавить в build.gradle
зависимость org.springframework.boot:spring-boot-starter-undertow
и исключить зависимоситьspring-boot-starter-tomcat
.
...
dependencies {
implementation('org.springframework.boot:spring-boot-starter-web') {
exclude module: 'spring-boot-starter-tomcat'
}
implementation ('org.keycloak:keycloak-spring-boot-starter') {
exclude module: 'spring-boot-starter-tomcat'
}
implementation 'org.springframework.boot:spring-boot-starter-undertow'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
...
Теперь перейдём к конфигурации приложения. Вместо properties файла конфигурации давайте будем использовать более удобный (на мой взгляд, конечно же) yml. А так же, чтобы подчеркнуть, что данный конфиг предназначен для разработки, профильdev. Т.е. полное название файла конфигурации будет application-dev.yml.
Как работают права в kubernetes.
Управлять правами пользователя/групп мы можем с помощью RBAC, об этом создано уже кучу статей, не буду подробно на этом останавливаться. Проблема в том что вы можете используя RBAC для того что бы ограничить права пользователя, но Kubernetes не чего не знает о пользователях.
Подготовка
Подробно останавливаться на том как создавать самоподписанные сертификат я не буду, надо создать 2 сертификата, это корневой(Центр сертификации) и wildcard клиентский для домена *.example.org
После того как вы получите/выпишите сертификаты, клиентский надо добавить в Kubernetes, для этого создаем для него secret:
kubectl create secret tls tls-keycloak --cert=example.org.crt --key=example.org.pem
Далее мы будет его использовать для нашего Ingress контроллера
Настройка клиента
Надо создать клиента, в понятиях Keycloak это приложение которое будет у него авторизовываться. Важные пункты выделю на скриншоте красным.
Clients —> Create
Создадим scope для групп:
Client Scopes —> Create
И настроим mapper для них:
Client Scopes —> groups —> Mappers —> Create
Добавляем маппинг наших групп в Default Client Scopes:
Clients —> kubernetes —> Client Scopes —> Default Client ScopesВыбираем groups в Available Client Scopes, нажимаем Add selected
Получаем secret(и записываем его куда нить) который мы будем использовать для авторизации в Keycloak:
Clients —> kubernetes —> Credentials —> SecretНа этом настройка окончена, но у меня возникла ошибка когда после успешной авторизации я получал ошибку 403. Баг репорт.
Фикс:
Client Scopes —> roles —> Mappers —> Create
Приватная сеть с поддержкой ip multicast
Если применяете Weavenet в качестве CNI, multicast будет работать сразу же — и ваши узлы Keycloak будут видеть друг друга, как только будут запущены.
Если у вас нет поддержки ip multicast в кластере Kubernetes, можно настроить JGroups на работу с другими протоколами для поиска узлов.
Первый вариант — испольльзование KUBE_DNS, который использует headless service для поиска узлов Keycloak, вы просто передаете JGroups имя сервиса, которое будет использовано для поиска узлов.
Еще один вариант — применение метода KUBE_PING, который работает с API для поиска узлов (надо настроить serviceAccount с правами list и get, после чего настроить поды для работы с этой serviceAccount).
Способ поиска узлов для JGroups настраивается путем выставления переменных окружения JGROUPS_DISCOVERY_PROTOCOL и JGROUPS_DISCOVERY_PROPERTIES. Для KUBE_PING надо выбрать поды задавая namespace и labels.
️ Если используете multicast и запускаете два и больше кластеров Keycloak в одном кластере Kubernetes (допустим один в namespace production
, второй — staging
) — узлы одного кластера Keycloak могут присоединиться к другому кластеру. Обязательно используйте уникальный multicast адрес для каждого кластера путем установки переменныхjboss.default.multicast.address
и jboss.modcluster.multicast.address
в JAVA_OPTS
.
Связь
Keycloak использует множественные отдельные кластера кэшей Infinispan для каждого датацентра, где расположены кластера Keycloack, составленные из узлов Keycloak. Но при этом нет разницы между узлами Keycloak в разных датацентрах.
Узлы Keycloak используют внешнюю Java Data Grid (сервера Infinispan) для связи между датацентрами. Связь работает по протоколу Infinispan HotRod.
Кэши Infinispan должны быть настроены с атрибутом remoteStore, для того, чтобы данные могли сохраняться в удаленных (в другом датацентре, прим. переводчика) кэшах. Есть отдельные кластера infinispan среди JDG серверов, так что данные, сохраняемые на JDG1 на площадке site1 будут реплицированы на JDG2 на площадке site2.
Ну и наконец, принимающий сервер JDG оповещает сервера Keycloak своего кластера через клиентские соединения, что является особенностью протокола HotRod. Узлы Keycloak на site2 обновляют свои кэши Infinispan, и конкретная пользовательская сессия становится также доступной на узлах Keycloak на site2.
Для некоторых кэшей также возможно не делать резервные копии и полностью отказаться от записи данных через сервер Infinispan. Для этого надо убрать настройку remote-store конкретному кэшу Infinispan (в файле standalone-ha.xml), после чего некоторый конкретный replicated-cache также перестанет быть нужным на стороне Infinispan сервера.