Search

Chapter 2. Using OpenID Connect to secure applications and services

download PDF

This section describes how you can secure applications and services with OpenID Connect using Red Hat build of Keycloak.

2.1. Available Endpoints

As a fully-compliant OpenID Connect Provider implementation, Red Hat build of Keycloak exposes a set of endpoints that applications and services can use to authenticate and authorize their users.

This section describes some of the key endpoints that your application and service should use when interacting with Red Hat build of Keycloak.

2.1.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 Red Hat build of Keycloak. The endpoint is:

/realms/{realm-name}/.well-known/openid-configuration

To obtain the full URL, add the base URL for Red Hat build of 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.

2.1.1.1. Authorization endpoint

/realms/{realm-name}/protocol/openid-connect/auth

The authorization endpoint performs authentication of the end-user. This authentication is done by redirecting the user agent to this endpoint.

For more details see the Authorization Endpoint section in the OpenID Connect specification.

2.1.1.2. Token endpoint

/realms/{realm-name}/protocol/openid-connect/token

The token endpoint is used to obtain tokens. Tokens can either be obtained by exchanging an authorization code or by supplying credentials directly depending on what flow is used. The token endpoint is also used to obtain new access tokens when they expire.

For more details, see the Token Endpoint section in the OpenID Connect specification.

2.1.1.3. Userinfo endpoint

/realms/{realm-name}/protocol/openid-connect/userinfo

The userinfo endpoint returns standard claims about the authenticated user; this endpoint is protected by a bearer token.

For more details, see the Userinfo Endpoint section in the OpenID Connect specification.

2.1.1.4. Logout endpoint

/realms/{realm-name}/protocol/openid-connect/logout

The logout endpoint logs out the authenticated user.

The user agent can be redirected to the endpoint, which causes the active user session to be logged out. The user agent is then redirected back to the application.

The endpoint can also be invoked directly by the application. To invoke this endpoint directly, the refresh token needs to be included as well as the credentials required to authenticate the client.

2.1.1.5. Certificate endpoint

/realms/{realm-name}/protocol/openid-connect/certs

The certificate endpoint returns the public keys enabled by the realm, encoded as a JSON Web Key (JWK). Depending on the realm settings, one or more keys can be enabled for verifying tokens. For more information, see the Server Administration Guide and the JSON Web Key specification.

2.1.1.6. Introspection endpoint

/realms/{realm-name}/protocol/openid-connect/token/introspect

The introspection endpoint is used to retrieve the active state of a token. In other words, you can use it to validate an access or refresh token. This endpoint can only be invoked by confidential clients.

For more details on how to invoke on this endpoint, see OAuth 2.0 Token Introspection specification.

2.1.1.7. Dynamic Client Registration endpoint

/realms/{realm-name}/clients-registrations/openid-connect

The dynamic client registration endpoint is used to dynamically register clients.

For more details, see the Client Registration chapter and the OpenID Connect Dynamic Client Registration specification.

2.1.1.8. Token Revocation endpoint

/realms/{realm-name}/protocol/openid-connect/revoke

The token revocation endpoint is used to revoke tokens. Both refresh tokens and access tokens are supported by this endpoint. When revoking a refresh token, the user consent for the corresponding client is also revoked.

For more details on how to invoke on this endpoint, see OAuth 2.0 Token Revocation specification.

2.1.1.9. Device Authorization endpoint

/realms/{realm-name}/protocol/openid-connect/auth/device

The device authorization endpoint is used to obtain a device code and a user code. It can be invoked by confidential or public clients.

For more details on how to invoke on this endpoint, see OAuth 2.0 Device Authorization Grant specification.

2.1.1.10. Backchannel Authentication endpoint

/realms/{realm-name}/protocol/openid-connect/ext/ciba/auth

The backchannel authentication endpoint is used to obtain an auth_req_id that identifies the authentication request made by the client. It can only be invoked by confidential clients.

For more details on how to invoke on this endpoint, see OpenID Connect Client Initiated Backchannel Authentication Flow specification.

Also refer to other places of Red Hat build of Keycloak documentation like Client Initiated Backchannel Authentication Grant section of this guide and Client Initiated Backchannel Authentication Grant section of Server Administration Guide.

2.2. Supported Grant Types

This section describes the different grant types available to relaying parties.

2.2.1. Authorization code

The Authorization Code flow redirects the user agent to Red Hat build of Keycloak. Once the user has successfully authenticated with Red Hat build of Keycloak, an Authorization Code is created and the user agent is redirected back to the application. The application then uses the authorization code along with its credentials to obtain an Access Token, Refresh Token and ID Token from Red Hat build of Keycloak.

The flow is targeted towards web applications, but is also recommended for native applications, including mobile applications, where it is possible to embed a user agent.

For more details refer to the Authorization Code Flow in the OpenID Connect specification.

2.2.2. Implicit

The Implicit flow works similarly to the Authorization Code flow, but instead of returning an Authorization Code, the Access Token and ID Token is returned. This approach reduces the need for the extra invocation to exchange the Authorization Code for an Access Token. However, it does not include a Refresh Token. This results in the need to permit Access Tokens with a long expiration; however, that approach is not practical because it is very hard to invalidate these tokens. Alternatively, you can require a new redirect to obtain a new Access Token once the initial Access Token has expired. The Implicit flow is useful if the application only wants to authenticate the user and deals with logout itself.

You can instead use a Hybrid flow where both the Access Token and an Authorization Code are returned.

One thing to note is that both the Implicit flow and Hybrid flow have potential security risks as the Access Token may be leaked through web server logs and browser history. You can somewhat mitigate this problem by using short expiration for Access Tokens.

For more details, see the Implicit Flow in the OpenID Connect specification.

Per current OAuth 2.0 Security Best Current Practice, this flow should not be used. This flow is removed from the future OAuth 2.1 specification.

2.2.3. Resource Owner Password Credentials

Resource Owner Password Credentials, referred to as Direct Grant in Red Hat build of Keycloak, allows exchanging user credentials for tokens. Per current OAuth 2.0 Security Best Practices, this flow should not be used, preferring alternative methods such as Section 2.2.5, “Device Authorization Grant” or Section 2.2.1, “Authorization code”.

The limitations of using this flow include:

  • User credentials are exposed to the application
  • Applications need login pages
  • Application needs to be aware of the authentication scheme
  • Changes to authentication flow requires changes to application
  • No support for identity brokering or social login
  • Flows are not supported (user self-registration, required actions, and so on.)

Security concerns with this flow include:

  • Involving more than Red Hat build of Keycloak in handling of credentials
  • Increased vulnerable surface area where credential leaks can happen
  • Creating an ecosystem where users trust another application for entering their credentials and not Red Hat build of Keycloak

For a client to be permitted to use the Resource Owner Password Credentials grant, the client has to have the Direct Access Grants Enabled option enabled.

This flow is not included in OpenID Connect, but is a part of the OAuth 2.0 specification. It is removed from the future OAuth 2.1 specification.

For more details, see the Resource Owner Password Credentials Grant chapter in the OAuth 2.0 specification.

2.2.3.1. Example using CURL

The following example shows how to obtain an access token for a user in the realm master with username user and password password. The example is using the confidential client myclient:

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"

2.2.4. Client credentials

Client Credentials are used when clients (applications and services) want to obtain access on behalf of themselves rather than on behalf of a user. For example, these credentials can be useful for background services that apply changes to the system in general rather than for a specific user.

Red Hat build of Keycloak provides support for clients to authenticate either with a secret or with public/private keys.

This flow is not included in OpenID Connect, but is a part of the OAuth 2.0 specification.

For more details, see the Client Credentials Grant chapter in the OAuth 2.0 specification.

2.2.5. Device Authorization Grant

Device Authorization Grant is used by clients running on internet-connected devices that have limited input capabilities or lack a suitable browser.

  1. The application requests that Red Hat build of Keycloak provide a device code and a user code.
  2. Red Hat build of Keycloak creates a device code and a user code.
  3. Red Hat build of Keycloak returns a response including the device code and the user code to the application.
  4. The application provides the user with the user code and the verification URI. The user accesses a verification URI to be authenticated by using another browser.
  5. The application repeatedly polls Red Hat build of Keycloak until Red Hat build of Keycloak completes the user authorization.
  6. If user authentication is complete, the application obtains the device code.
  7. The application uses the device code along with its credentials to obtain an Access Token, Refresh Token and ID Token from Red Hat build of Keycloak.

For more details, see the OAuth 2.0 Device Authorization Grant specification.

2.2.6. Client Initiated Backchannel Authentication Grant

Client Initiated Backchannel Authentication Grant is used by clients who want to initiate the authentication flow by communicating with the OpenID Provider directly without redirect through the user’s browser like OAuth 2.0’s authorization code grant.

The client requests from Red Hat build of Keycloak an auth_req_id that identifies the authentication request made by the client. Red Hat build of Keycloak creates the auth_req_id.

After receiving this auth_req_id, this client repeatedly needs to poll Red Hat build of Keycloak to obtain an Access Token, Refresh Token, and ID Token from Red Hat build of Keycloak in return for the auth_req_id until the user is authenticated.

In case that client uses ping mode, it does not need to repeatedly poll the token endpoint, but it can wait for the notification sent by Red Hat build of Keycloak to the specified Client Notification Endpoint. The Client Notification Endpoint can be configured in the Red Hat build of Keycloak Admin Console. The details of the contract for Client Notification Endpoint are described in the CIBA specification.

For more details, see OpenID Connect Client Initiated Backchannel Authentication Flow specification.

Also refer to other places of Red Hat build of Keycloak documentation such as Backchannel Authentication Endpoint of this guide and Client Initiated Backchannel Authentication Grant section of Server Administration Guide. For the details about FAPI CIBA compliance, see the FAPI section of this guide.

2.3. Red Hat build of Keycloak Java adapters

2.3.1. Red Hat JBoss Enterprise Application Platform

Red Hat build of Keycloak does not include any adapters for Red Hat JBoss Enterprise Application Platform. However, there are alternatives for existing applications deployed to Red Hat JBoss Enterprise Application Platform.

2.3.1.1. 8.0 Beta

Red Hat Enterprise Application Platform 8.0 Beta provides a native OpenID Connect client through the Elytron OIDC client subsystem.

For more information, see the Red Hat JBoss Enterprise Application Platform documentation.

2.3.1.2. 6.4 and 7.x

Existing applications deployed to Red Hat JBoss Enterprise Application Platform 6.4 and 7.x can leverage adapters from Red Hat Single Sign-On 7.6 in combination with the Red Hat build of Keycloak server.

For more information, see the Red Hat Single Sign-On documentation.

2.3.2. Spring Boot adapter

Red Hat build of Keycloak does not include any adapters for Spring Boot. However, there are alternatives for existing applications built with Spring Boot.

Spring Security provides comprehensive support for OAuth 2 and OpenID Connect. For more information, see the Spring Security documentation.

Alternatively, for Spring Boot 2.x the Spring Boot adapter from Red Hat Single Sign-On 7.6 can be used in combination with the Red Hat build of Keycloak server. For more information, see the Red Hat Single Sign-On documentation.

2.4. Red Hat build of Keycloak JavaScript adapter

Red Hat build of Keycloak comes with a client-side JavaScript library called keycloak-js that can be used to secure web applications. The adapter also comes with built-in support for Cordova applications.

2.4.1. Installation

The adapter is distributed in several ways, but we recommend that you install the keycloak-js package from NPM:

npm install keycloak-js

Alternatively, the library can be retrieved directly from the Red Hat build of Keycloak server at /js/keycloak.js and is also distributed as a ZIP archive. We are however considering the inclusion of the adapter directly from the Keycloak server as deprecated, and this functionality might be removed in the future.

2.4.2. Red Hat build of Keycloak server configuration

One important thing to consider about using client-side applications is that the client has to be a public client as there is no secure way to store client credentials in a client-side application. This consideration makes it very important to make sure the redirect URIs you have configured for the client are correct and as specific as possible.

To use the adapter, create a client for your application in the Red Hat build of Keycloak Admin Console. Make the client public by toggling Client authentication to Off on the Capability config page.

You also need to configure Valid Redirect URIs and Web Origins. Be as specific as possible as failing to do so may result in a security vulnerability.

2.4.3. Using the adapter

The following example shows how to initialize the adapter. Make sure that you replace the options passed to the Keycloak constructor with those of the client you have configured.

import Keycloak from 'keycloak-js';

const keycloak = new Keycloak({
    url: 'http://keycloak-server${kc_base_path}',
    realm: 'myrealm',
    clientId: 'myapp'
});

try {
    const authenticated = await keycloak.init();
    console.log(`User is ${authenticated ? 'authenticated' : 'not authenticated'}`);
} catch (error) {
    console.error('Failed to initialize adapter:', error);
}

To authenticate, you call the login function. Two options exist to make the adapter automatically authenticate. You can pass login-required or check-sso to the init() function.

  • login-required authenticates the client if the user is logged in to Red Hat build of Keycloak or displays the login page if the user is not logged in.
  • check-sso only authenticates the client if the user is already logged in. If the user is not logged in, the browser is redirected back to the application and remains unauthenticated.

You can configure a silent check-sso option. With this feature enabled, your browser will not perform a full redirect to the Red Hat build of Keycloak server and back to your application, but this action will be performed in a hidden iframe. Therefore, your application resources are only loaded and parsed once by the browser, namely when the application is initialized and not again after the redirect back from Red Hat build of Keycloak to your application. This approach is particularly useful in case of SPAs (Single Page Applications).

To enable the silent check-sso, you provide a silentCheckSsoRedirectUri attribute in the init method. Make sure this URI is a valid endpoint in the application; it must be configured as a valid redirect for the client in the Red Hat build of Keycloak Admin Console:

keycloak.init({
    onLoad: 'check-sso',
    silentCheckSsoRedirectUri: `${location.origin}/silent-check-sso.html`
});

The page at the silent check-sso redirect uri is loaded in the iframe after successfully checking your authentication state and retrieving the tokens from the Red Hat build of Keycloak server. It has no other task than sending the received tokens to the main application and should only look like this:

<!doctype html>
<html>
<body>
    <script>
        parent.postMessage(location.href, location.origin);
    </script>
</body>
</html>

Remember that this page must be served by your application at the specified location in silentCheckSsoRedirectUri and is not part of the adapter.

Warning

Silent check-sso functionality is limited in some modern browsers. Please see the Modern Browsers with Tracking Protection Section.

To enable login-required set onLoad to login-required and pass to the init method:

keycloak.init({
    onLoad: 'login-required'
});

After the user is authenticated the application can make requests to RESTful services secured by Red Hat build of Keycloak by including the bearer token in the Authorization header. For example:

async function fetchUsers() {
    const response = await fetch('/api/users', {
        headers: {
            accept: 'application/json',
            authorization: `Bearer ${keycloak.token}`
        }
    });

    return response.json();
}

One thing to keep in mind is that the access token by default has a short life expiration so you may need to refresh the access token prior to sending the request. You refresh this token by calling the updateToken() method. This method returns a Promise, which makes it easy to invoke the service only if the token was successfully refreshed and displays an error to the user if it was not refreshed. For example:

try {
    await keycloak.updateToken(30);
} catch (error) {
    console.error('Failed to refresh token:', error);
}

const users = await fetchUsers();
Note

Both access and refresh token are stored in memory and are not persisted in any kind of storage. Therefore, these tokens should never be persisted to prevent hijacking attacks.

2.4.4. Session Status iframe

By default, the adapter creates a hidden iframe that is used to detect if a Single-Sign Out has occurred. This iframe does not require any network traffic. Instead the status is retrieved by looking at a special status cookie. This feature can be disabled by setting checkLoginIframe: false in the options passed to the init() method.

You should not rely on looking at this cookie directly. Its format can change and it’s also associated with the URL of the Red Hat build of Keycloak server, not your application.

Warning

Session Status iframe functionality is limited in some modern browsers. Please see Modern Browsers with Tracking Protection Section.

2.4.5. Implicit and hybrid flow

By default, the adapter uses the Authorization Code flow.

With this flow, the Red Hat build of Keycloak server returns an authorization code, not an authentication token, to the application. The JavaScript adapter exchanges the code for an access token and a refresh token after the browser is redirected back to the application.

Red Hat build of Keycloak also supports the Implicit flow where an access token is sent immediately after successful authentication with Red Hat build of Keycloak. This flow may have better performance than the standard flow because no additional request exists to exchange the code for tokens, but it has implications when the access token expires.

However, sending the access token in the URL fragment can be a security vulnerability. For example the token could be leaked through web server logs and or browser history.

To enable implicit flow, you enable the Implicit Flow Enabled flag for the client in the Red Hat build of Keycloak Admin Console. You also pass the parameter flow with the value implicit to init method:

keycloak.init({
    flow: 'implicit'
})

Note that only an access token is provided and no refresh token exists. This situation means that once the access token has expired, the application has to redirect to Red Hat build of Keycloak again to obtain a new access token.

Red Hat build of Keycloak also supports the Hybrid flow.

This flow requires the client to have both the Standard Flow and Implicit Flow enabled in the Admin Console. The Red Hat build of Keycloak server then sends both the code and tokens to your application. The access token can be used immediately while the code can be exchanged for access and refresh tokens. Similar to the implicit flow, the hybrid flow is good for performance because the access token is available immediately. But, the token is still sent in the URL, and the security vulnerability mentioned earlier may still apply.

One advantage in the Hybrid flow is that the refresh token is made available to the application.

For the Hybrid flow, you need to pass the parameter flow with value hybrid to the init method:

keycloak.init({
    flow: 'hybrid'
});

2.4.6. Hybrid Apps with Cordova

Red Hat build of Keycloak supports hybrid mobile apps developed with Apache Cordova. The adapter has two modes for this: cordova and cordova-native:

The default is cordova, which the adapter automatically selects if no adapter type has been explicitly configured and window.cordova is present. When logging in, it opens an InApp Browser that lets the user interact with Red Hat build of Keycloak and afterwards returns to the app by redirecting to http://localhost. Because of this behavior, you whitelist this URL as a valid redirect-uri in the client configuration section of the Admin Console.

While this mode is easy to set up, it also has some disadvantages:

  • The InApp-Browser is a browser embedded in the app and is not the phone’s default browser. Therefore it will have different settings and stored credentials will not be available.
  • The InApp-Browser might also be slower, especially when rendering more complex themes.
  • There are security concerns to consider, before using this mode, such as that it is possible for the app to gain access to the credentials of the user, as it has full control of the browser rendering the login page, so do not allow its use in apps you do not trust.

The alternative mode is`cordova-native`, which takes a different approach. It opens the login page using the system’s browser. After the user has authenticated, the browser redirects back into the application using a special URL. From there, the Red Hat build of Keycloak adapter can finish the login by reading the code or token from the URL.

You can activate the native mode by passing the adapter type cordova-native to the init() method:

keycloak.init({
    adapter: 'cordova-native'
});

This adapter requires two additional plugins:

The technical details for linking to an app differ on each platform and special setup is needed. Please refer to the Android and iOS sections of the deeplinks plugin documentation for further instructions.

Different kinds of links exist for opening apps: * custom schemes, such as myapp://login or android-app://com.example.myapp/https/example.com/login * Universal Links (iOS)) / Deep Links (Android). While the former are easier to set up and tend to work more reliably, the latter offer extra security because they are unique and only the owner of a domain can register them. Custom-URLs are deprecated on iOS. For best reliability, we recommend that you use universal links combined with a fallback site that uses a custom-url link.

Furthermore, we recommend the following steps to improve compatibility with the adapter:

  • Universal Links on iOS seem to work more reliably with response-mode set to query
  • To prevent Android from opening a new instance of your app on redirect add the following snippet to config.xml:
<preference name="AndroidLaunchMode" value="singleTask" />

2.4.7. Custom Adapters

In some situations, you may need to run the adapter in environments that are not supported by default, such as Capacitor. To use the JavasScript client in these environments, you can pass a custom adapter. For example, a third-party library could provide such an adapter to make it possible to reliably run the adapter:

import Keycloak from 'keycloak-js';
import KeycloakCapacitorAdapter from 'keycloak-capacitor-adapter';

const keycloak = new Keycloak();

keycloak.init({
    adapter: KeycloakCapacitorAdapter,
});

This specific package does not exist, but it gives a pretty good example of how such an adapter could be passed into the client.

It’s also possible to make your own adapter, to do so you will have to implement the methods described in the KeycloakAdapter interface. For example the following TypeScript code ensures that all the methods are properly implemented:

import Keycloak, { KeycloakAdapter } from 'keycloak-js';

// Implement the 'KeycloakAdapter' interface so that all required methods are guaranteed to be present.
const MyCustomAdapter: KeycloakAdapter = {
    login(options) {
        // Write your own implementation here.
    }

    // The other methods go here...
};

const keycloak = new Keycloak();

keycloak.init({
    adapter: MyCustomAdapter,
});

Naturally you can also do this without TypeScript by omitting the type information, but ensuring implementing the interface properly will then be left entirely up to you.

2.4.8. Modern Browsers with Tracking Protection

In the latest versions of some browsers, various cookies policies are applied to prevent tracking of the users by third parties, such as SameSite in Chrome or completely blocked third-party cookies. Those policies are likely to become more restrictive and adopted by other browsers over time. Eventually cookies in third-party contexts may become completely unsupported and blocked by the browsers. As a result, the affected adapter features might ultimately be deprecated.

The adapter relies on third-party cookies for Session Status iframe, silent check-sso and partially also for regular (non-silent) check-sso. Those features have limited functionality or are completely disabled based on how restrictive the browser is regarding cookies. The adapter tries to detect this setting and reacts accordingly.

2.4.8.1. Browsers with "SameSite=Lax by Default" Policy

All features are supported if SSL / TLS connection is configured on the Red Hat build of Keycloak side as well as on the application side. For example, Chrome is affected starting with version 84.

2.4.8.2. Browsers with Blocked Third-Party Cookies

Session Status iframe is not supported and is automatically disabled if such browser behavior is detected by the adapter. This means the adapter cannot use a session cookie for Single Sign-Out detection and must rely purely on tokens. As a result, when a user logs out in another window, the application using the adapter will not be logged out until the application tries to refresh the Access Token. Therefore, consider setting the Access Token Lifespan to a relatively short time, so that the logout is detected as soon as possible. For more details, see Session and Token Timeouts.

Silent check-sso is not supported and falls back to regular (non-silent) check-sso by default. This behavior can be changed by setting silentCheckSsoFallback: false in the options passed to the init method. In this case, check-sso will be completely disabled if restrictive browser behavior is detected.

Regular check-sso is affected as well. Since Session Status iframe is unsupported, an additional redirect to Red Hat build of Keycloak has to be made when the adapter is initialized to check the user’s login status. This check is different from the standard behavior when the iframe is used to tell whether the user is logged in, and the redirect is performed only when the user is logged out.

An affected browser is for example Safari starting with version 13.1.

2.4.9. API Reference

2.4.9.1. Constructor

new Keycloak();
new Keycloak('http://localhost/keycloak.json');
new Keycloak({ url: 'http://localhost', realm: 'myrealm', clientId: 'myApp' });

2.4.9.2. Properties

authenticated
Is true if the user is authenticated, false otherwise.
token
The base64 encoded token that can be sent in the Authorization header in requests to services.
tokenParsed
The parsed token as a JavaScript object.
subject
The user id.
idToken
The base64 encoded ID token.
idTokenParsed
The parsed id token as a JavaScript object.
realmAccess
The realm roles associated with the token.
resourceAccess
The resource roles associated with the token.
refreshToken
The base64 encoded refresh token that can be used to retrieve a new token.
refreshTokenParsed
The parsed refresh token as a JavaScript object.
timeSkew
The estimated time difference between the browser time and the Red Hat build of Keycloak server in seconds. This value is just an estimation, but is accurate enough when determining if a token is expired or not.
responseMode
Response mode passed in init (default value is fragment).
flow
Flow passed in init.
adapter

Allows you to override the way that redirects and other browser-related functions will be handled by the library. Available options:

  • "default" - the library uses the browser api for redirects (this is the default)
  • "cordova" - the library will try to use the InAppBrowser cordova plugin to load keycloak login/registration pages (this is used automatically when the library is working in a cordova ecosystem)
  • "cordova-native" - the library tries to open the login and registration page using the phone’s system browser using the BrowserTabs cordova plugin. This requires extra setup for redirecting back to the app (see Section 2.4.6, “Hybrid Apps with Cordova”).
  • "custom" - allows you to implement a custom adapter (only for advanced use cases)
responseType
Response type sent to Red Hat build of Keycloak with login requests. This is determined based on the flow value used during initialization, but can be overridden by setting this value.

2.4.9.3. Methods

init(options)

Called to initialize the adapter.

Options is an Object, where:

  • useNonce - Adds a cryptographic nonce to verify that the authentication response matches the request (default is true).
  • onLoad - Specifies an action to do on load. Supported values are login-required or check-sso.
  • silentCheckSsoRedirectUri - Set the redirect uri for silent authentication check if onLoad is set to 'check-sso'.
  • silentCheckSsoFallback - Enables fall back to regular check-sso when silent check-sso is not supported by the browser (default is true).
  • token - Set an initial value for the token.
  • refreshToken - Set an initial value for the refresh token.
  • idToken - Set an initial value for the id token (only together with token or refreshToken).
  • scope - Set the default scope parameter to the Red Hat build of Keycloak login endpoint. Use a space-delimited list of scopes. Those typically reference Client scopes defined on a particular client. Note that the scope openid will always be added to the list of scopes by the adapter. For example, if you enter the scope options address phone, then the request to Red Hat build of Keycloak will contain the scope parameter scope=openid address phone. Note that the default scope specified here is overwritten if the login() options specify scope explicitly.
  • timeSkew - Set an initial value for skew between local time and Red Hat build of Keycloak server in seconds (only together with token or refreshToken).
  • checkLoginIframe - Set to enable/disable monitoring login state (default is true).
  • checkLoginIframeInterval - Set the interval to check login state (default is 5 seconds).
  • responseMode - Set the OpenID Connect response mode send to Red Hat build of Keycloak server at login request. Valid values are query or fragment. Default value is fragment, which means that after successful authentication will Red Hat build of Keycloak redirect to JavaScript application with OpenID Connect parameters added in URL fragment. This is generally safer and recommended over query.
  • flow - Set the OpenID Connect flow. Valid values are standard, implicit or hybrid.
  • enableLogging - Enables logging messages from Keycloak to the console (default is false).
  • pkceMethod - The method for Proof Key Code Exchange (PKCE) to use. Configuring this value enables the PKCE mechanism. Available options:

    • "S256" - The SHA256 based PKCE method (default)
    • false - PKCE is disabled.
  • acrValues - Generates the acr_values parameter which refers to authentication context class reference and allows clients to declare the required assurance level requirements, e.g. authentication mechanisms. See Section 4. acr_values request values and level of assurance in OpenID Connect MODRNA Authentication Profile 1.0.
  • messageReceiveTimeout - Set a timeout in milliseconds for waiting for message responses from the Keycloak server. This is used, for example, when waiting for a message during 3rd party cookies check. The default value is 10000.
  • locale - When onLoad is 'login-required', sets the 'ui_locales' query param in compliance with section 3.1.2.1 of the OIDC 1.0 specification.

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 Red Hat build of Keycloak server side. For example, enforce displaying the login screen in case of value login.
  • 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 - Override the scope configured in init with a different value for this specific login.
  • idpHint - Used to tell Red Hat build of 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 inside claims parameter to the Red Hat build of 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.
  • acrValues - Generates the acr_values parameter which refers to authentication context class reference and allows clients to declare the required assurance level requirements, e.g. authentication mechanisms. See Section 4. acr_values request values and level of assurance in OpenID Connect MODRNA Authentication Profile 1.0.
  • action - If the value is register, the user is redirected to the registration page. See Registration requested by client section for more details. If the value is UPDATE_PASSWORD or another supported required action, the user will be redirected to the reset password page or the other required action page. However, if the user is not authenticated, the user will be sent to the login page and redirected after authentication. See Application Initiated Action section for more details.
  • 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 and location 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 log out 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 Console.

createAccountUrl(options)

Returns the URL to the Account 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:

try {
    const profile = await keycloak.loadUserProfile();
    console.log('Retrieved user profile:', profile);
} catch (error) {
    console.error('Failed to load user profile:', error);
}

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 -1 is passed as the minValidity, the token will be forcibly 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:

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);
}

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.

2.4.9.4. Callback Events

The adapter supports setting callback listeners for certain events. Keep in mind that these have to be set before the call to the init() method.

For example:

keycloak.onAuthSuccess = () => console.log('Authenticated!');

The available events are:

  • onReady(authenticated) - Called when the adapter is initialized.
  • onAuthSuccess - Called when a user is successfully authenticated.
  • onAuthError - Called if there was an error during authentication.
  • onAuthRefreshSuccess - Called when the token is refreshed.
  • onAuthRefreshError - Called if there was an error while trying to refresh the token.
  • onAuthLogout - Called if the user is logged out (will only be called if the session status iframe is enabled, or in Cordova mode).
  • onTokenExpired - Called when the access token is expired. If a refresh token is available the token can be refreshed with updateToken, or in cases where it is not (that is, with implicit flow) you can redirect to the login screen to obtain a new access token.

2.5. Red Hat build of Keycloak Node.js adapter

Red Hat build of Keycloak provides a Node.js adapter built on top of Connect to protect server-side JavaScript apps - the goal was to be flexible enough to integrate with frameworks like Express.js.

To use the Node.js adapter, first you must create a client for your application in the Red Hat build of Keycloak Admin Console. The adapter supports public, confidential, and bearer-only access type. Which one to choose depends on the use-case scenario.

Once the client is created, click Action at the top right and choose Download adapter config. For Format, choose *Keycloak OIDC JSON and click Download. The downloaded keycloak.json file is at the root folder of your project.

2.5.1. Installation

Assuming you have already installed Node.js, create a folder for your application:

mkdir myapp && cd myapp

Use npm init command to create a package.json for your application. Now add the Red Hat build of Keycloak connect adapter in the dependencies list:

    "dependencies": {
        "keycloak-connect": "file:keycloak-connect-24.0.0+redhat-00001.tgz"
    }

2.5.2. Usage

Instantiate a Keycloak class
The Keycloak class provides a central point for configuration and integration with your application. The simplest creation involves no arguments.

In the root directory of your project create a file called server.js and add the following code:

    const session = require('express-session');
    const Keycloak = require('keycloak-connect');

    const memoryStore = new session.MemoryStore();
    const keycloak = new Keycloak({ store: memoryStore });

Install the express-session dependency:

    npm install express-session

To start the server.js script, add the following command in the 'scripts' section of the package.json:

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "node server.js"
    },

Now we have the ability to run our server with following command:

    npm run start

By default, this will locate a file named keycloak.json alongside the main executable of your application, in our case on the root folder, to initialize Red Hat build of Keycloak specific settings such as public key, realm name, various URLs.

In that case a Red Hat build of Keycloak deployment is necessary to access Red Hat build of Keycloak admin console.

Please visit links on how to deploy a Red Hat build of Keycloak admin console with Podman or Docker

Now we are ready to obtain the keycloak.json file by visiting the Red Hat build of Keycloak Admin Console clients (left sidebar) choose your client Installation Format Option Keycloak OIDC JSON Download

Paste the downloaded file on the root folder of our project.

Instantiation with this method results in all the reasonable defaults being used. As alternative, it’s also possible to provide a configuration object, rather than the keycloak.json file:

    const kcConfig = {
        clientId: 'myclient',
        bearerOnly: true,
        serverUrl: 'http://localhost:8080',
        realm: 'myrealm',
        realmPublicKey: 'MIIBIjANB...'
    };

    const keycloak = new Keycloak({ store: memoryStore }, kcConfig);

Applications can also redirect users to their preferred identity provider by using:

    const keycloak = new Keycloak({ store: memoryStore, idpHint: myIdP }, kcConfig);
Configuring a web session store
If you want to use web sessions to manage server-side state for authentication, you need to initialize the Keycloak(…​) with at least a store parameter, passing in the actual session store that express-session is using.
    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 });
Passing a custom scope value
By default, the scope value openid is passed as a query parameter to Red Hat build of Keycloak’s login URL, but you can add an additional custom value:
    const keycloak = new Keycloak({ scope: 'offline_access' });

2.5.3. Installing middleware

Once instantiated, install the middleware into your connect-capable app:

In order to do so, first we have to install Express:

    npm install express

then require Express in our project as outlined below:

    const express = require('express');
    const app = express();

and configure Keycloak middleware in Express, by adding at the code below:

    app.use( keycloak.middleware() );

Last but not least, let’s set up our server to listen for HTTP requests on port 3000 by adding the following code to main.js:

    app.listen(3000, function () {
        console.log('App listening on port 3000');
    });

2.5.4. Configuration for proxies

If the application is running behind a proxy that terminates an SSL connection Express must be configured per the express behind proxies guide. Using an incorrect proxy configuration can result in invalid redirect URIs being generated.

Example configuration:

    const app = express();

    app.set( 'trust proxy', true );

    app.use( keycloak.middleware() );

2.5.5. Protecting resources

Simple authentication
To enforce that a user must be authenticated before accessing a resource, simply use a no-argument version of keycloak.protect():
    app.get( '/complain', keycloak.protect(), complaintHandler );
Role-based authorization
To secure a resource with an application role for the current app:
    app.get( '/special', keycloak.protect('special'), specialHandler );

To secure a resource with an application role for a different app:

    app.get( '/extra-special', keycloak.protect('other-app:special'), extraSpecialHandler );

To secure a resource with a realm role:

    app.get( '/admin', keycloak.protect( 'realm:admin' ), adminHandler );
Resource-Based Authorization
Resource-Based Authorization allows you to protect resources, and their specific methods/actions,** based on a set of policies defined in Keycloak, thus externalizing authorization from your application. This is achieved by exposing a keycloak.enforcer method which you can use to protect resources.*
    app.get('/apis/me', keycloak.enforcer('user:profile'), userProfileHandler);

The keycloak-enforcer method operates in two modes, depending on the value of the response_mode configuration option.

    app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'token'}), userProfileHandler);

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. If the server did not respond with a token with the expected permissions, the request is denied. When using this mode, you should be able to obtain the token from the request as follows:

    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
    });

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:

    app.get('/apis/me', keycloak.enforcer('user:profile', {response_mode: 'permissions'}), function (req, res) {
        const permissions = req.permissions;

        // show user profile
    });

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.

If your application is acting as both a public client(frontend) and resource server(backend), you can use the following configuration to reference a different client in Keycloak with the policies that you want to enforce:

      keycloak.enforcer('user:profile', {resource_server_id: 'my-apiserver'})

It is recommended to use distinct clients in Keycloak to represent your frontend and backend.

If the application you are protecting is enabled with Keycloak authorization services and you have defined client credentials in keycloak.json, you can push additional claims to the server and make them available to your policies in order to make decisions. For that, you can define a claims configuration option which expects a function that returns a JSON with the claims you want to push:

      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

For more details about how to configure Keycloak to protected your application resources, please take a look at the Authorization Services Guide.

Advanced authorization
To secure resources based on parts of the URL itself, assuming a role exists for each section:
    function protectBySection(token, request) {
      return token.hasRole( request.params.section );
    }

    app.get( '/:section/:page', keycloak.protect( protectBySection ), sectionHandler );

Advanced Login Configuration:

By default, all unauthorized requests will be redirected to the Red Hat build of Keycloak login page unless your client is bearer-only. However, a confidential or public client may host both browsable and API endpoints. To prevent redirects on unauthenticated API requests and instead return an HTTP 401, you can override the redirectToLogin function.

For example, this override checks if the URL contains /api/ and disables login redirects:

    Keycloak.prototype.redirectToLogin = function(req) {
    const apiReqMatcher = /\/api\//i;
    return !apiReqMatcher.test(req.originalUrl || req.url);
    };

2.5.6. Additional URLs

Explicit user-triggered logout
By default, the middleware catches calls to /logout to send the user through a Red Hat build of Keycloak-centric logout workflow. This can be changed by specifying a logout configuration parameter to the middleware() call:
    app.use( keycloak.middleware( { logout: '/logoff' } ));

When the user-triggered logout is invoked a query parameter redirect_url can be passed:

https://example.com/logoff?redirect_url=https%3A%2F%2Fexample.com%3A3000%2Flogged%2Fout

This parameter is then used as the redirect url of the OIDC logout endpoint and the user will be redirected to https://example.com/logged/out.

Red Hat build of Keycloak Admin Callbacks
Also, the middleware supports callbacks from the Red Hat build of Keycloak console to log out a single session or all sessions. By default, these type of admin callbacks occur relative to the root URL of / but can be changed by providing an admin parameter to the middleware() call:
    app.use( keycloak.middleware( { admin: '/callbacks' } );

2.5.7. Complete example

A complete example using the Node.js adapter usage can be found in Keycloak quickstarts for Node.js

2.6. Financial-grade API (FAPI) Support

Red Hat build of Keycloak makes it easier for administrators to make sure that their clients are compliant with these specifications:

This compliance means that the Red Hat build of Keycloak server will verify the requirements for the authorization server, which are mentioned in the specifications. Red Hat build of Keycloak adapters do not have any specific support for the FAPI, hence the required validations on the client (application) side may need to be still done manually or through some other third-party solutions.

2.6.1. FAPI client profiles

To make sure that your clients are FAPI compliant, you can configure Client Policies in your realm as described in the Server Administration Guide and link them to the global client profiles for FAPI support, which are automatically available in each realm. You can use either fapi-1-baseline or fapi-1-advanced profile based on which FAPI profile you need your clients to conform with. You can use also profiles fapi-2-security-profile or fapi-2-message-signing for the compliance with FAPI 2 Draft specifications.

In case you want to use Pushed Authorization Request (PAR), it is recommended that your client use both the fapi-1-baseline profile and fapi-1-advanced for PAR requests. Specifically, the fapi-1-baseline profile contains pkce-enforcer executor, which makes sure that client use PKCE with secured S256 algorithm. This is not required for FAPI Advanced clients unless they use PAR requests.

In case you want to use CIBA in a FAPI compliant way, make sure that your clients use both fapi-1-advanced and fapi-ciba client profiles. There is a need to use the fapi-1-advanced profile, or other client profile containing the requested executors, as the fapi-ciba profile contains just CIBA-specific executors. When enforcing the requirements of the FAPI CIBA specification, there is a need for more requirements, such as enforcement of confidential clients or certificate-bound access tokens.

2.6.2. Open Finance Brasil Financial-grade API Security Profile

Red Hat build of Keycloak is compliant with the Open Finance Brasil Financial-grade API Security Profile 1.0 Implementers Draft 3. This one is stricter in some requirements than the FAPI 1 Advanced specification and hence it may be needed to configure Client Policies in the more strict way to enforce some of the requirements. Especially:

  • If your client does not use PAR, make sure that it uses encrypted OIDC request objects. This can be achieved by using a client profile with the secure-request-object executor configured with Encryption Required enabled.
  • Make sure that for JWS, the client uses the PS256 algorithm. For JWE, the client should use the RSA-OAEP with A256GCM. This may need to be set in all the Client Settings where these algorithms are applicable.

2.6.3. Australia Consumer Data Right (CDR) Security Profile

Red Hat build of Keycloak is compliant with the Australia Consumer Data Right Security Profile.

If you want to apply the Australia CDR security profile, you need to use fapi-1-advanced profile because the Australia CDR security profile is based on FAPI 1.0 Advanced security profile. If your client also applies PAR, make sure that client applies RFC 7637 Proof Key for Code Exchange (PKCE) because the Australia CDR security profile requires that you apply PKCE when applying PAR. This can be achieved by using a client profile with the pkce-enforcer executor.

2.6.4. TLS considerations

As confidential information is being exchanged, all interactions shall be encrypted with TLS (HTTPS). Moreover, there are some requirements in the FAPI specification for the cipher suites and TLS protocol versions used. To match these requirements, you can consider configure allowed ciphers. This configuration can be done by setting the https-protocols and https-cipher-suites options. Red Hat build of Keycloak uses TLSv1.3 by default and hence it is possibly not needed to change the default settings. However it may be needed to adjust ciphers if you need to fall back to lower TLS version for some reason. For more details, see Configuring TLS chapter.

2.7. OAuth 2.1 Support

Red Hat build of Keycloak makes it easier for administrators to make sure that their clients are compliant with these specifications:

This compliance means that the Red Hat build of Keycloak server will verify the requirements for the authorization server, which are mentioned in the specifications. Red Hat build of Keycloak adapters do not have any specific support for the OAuth 2.1, hence the required validations on the client (application) side may need to be still done manually or through some other third-party solutions.

2.7.1. OAuth 2.1 client profiles

To make sure that your clients are OAuth 2.1 compliant, you can configure Client Policies in your realm as described in the Server Administration Guide and link them to the global client profiles for OAuth 2.1 support, which are automatically available in each realm. You can use either oauth-2-1-for-confidential-client profile for confidential clients or oauth-2-1-for-public-client profile for public clients.

Note

OAuth 2.1 specification is still a draft and it may change in the future. Hence the Red Hat build of Keycloak built-in OAuth 2.1 client profiles can change as well.

Note

When using OAuth 2.1 profile for public clients, it is recommended to use DPoP preview feature as described in the Server Administration Guide because DPoP binds an access token and a refresh token together with the public part of a client’s key pair. This binding prevents an attacker from using stolen tokens.

2.8. Recommendations

This section describes some recommendations when securing your applications with Red Hat build of Keycloak.

2.8.1. Validating access tokens

If you need to manually validate access tokens issued by Red Hat build of Keycloak, you can invoke the Introspection Endpoint. The downside to this approach is that you have to make a network invocation to the Red Hat build of Keycloak server. This can be slow and possibly overload the server if you have too many validation requests going on at the same time. Red Hat build of Keycloak issued access tokens are JSON Web Tokens (JWT) digitally signed and encoded using JSON Web Signature (JWS). Because they are encoded in this way, you can locally validate access tokens using the public key of the issuing realm. You can either hard code the realm’s public key in your validation code, or lookup and cache the public key using the certificate endpoint with the Key ID (KID) embedded within the JWS. Depending on what language you code in, many third party libraries exist and they can help you with JWS validation.

2.8.2. Redirect URIs

When using the redirect based flows, be sure to use valid redirect uris for your clients. The redirect uris should be as specific as possible. This especially applies to client-side (public clients) applications. Failing to do so could result in:

  • Open redirects - this can allow attackers to create spoof links that looks like they are coming from your domain
  • Unauthorized entry - when users are already authenticated with Red Hat build of Keycloak, an attacker can use a public client where redirect uris have not be configured correctly to gain access by redirecting the user without the users knowledge

In production for web applications always use https for all redirect URIs. Do not allow redirects to http.

A few special redirect URIs also exist:

http://127.0.0.1
This redirect URI is useful for native applications and allows the native application to create a web server on a random port that can be used to obtain the authorization code. This redirect uri allows any port. Note that per OAuth 2.0 for Native Apps, the use of localhost is not recommended and the IP literal 127.0.0.1 should be used instead.
urn:ietf:wg:oauth:2.0:oob
If you cannot start a web server in the client (or a browser is not available), you can use the special urn:ietf:wg:oauth:2.0:oob redirect uri. When this redirect uri is used, Red Hat build of Keycloak displays a page with the code in the title and in a box on the page. The application can either detect that the browser title has changed, or the user can copy and paste the code manually to the application. With this redirect uri, a user can use a different device to obtain a code to paste back to the application.
Red Hat logoGithubRedditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

© 2024 Red Hat, Inc.