Authentication¶
Using external token from identity provider¶
OpenStack trusts Keycloak as its identity provider, so the OpenStack API only accepts tokens issued by Keycloak. If a workload or a user already has a token from another OpenID Connect (OIDC) identity provider, Keycloak can accept that token and issue its own token in return, which then authenticates with the OpenStack API.
Keycloak supports two ways to do this, depending on who the token represents:
Federated client authentication is for workloads, such as services, CI pipelines or Kubernetes service accounts, that get tokens from their own identity provider with the client credentials grant. Keycloak authenticates the workload as a Keycloak client without a client secret, and issues a token for that client’s service account.
JWT authorization grant is for users who already have an account in the Keycloak realm that is linked to the external identity provider, for example because they signed in through it before.
---
config:
theme: dark
---
sequenceDiagram
participant Client
participant OIDC Provider
participant Keycloak
participant Keystone
participant OpenStack
Client->>OIDC Provider: Request token
OIDC Provider-->>Client: Returns OIDC token
Client->>Keycloak: Send OIDC token as an assertion
Keycloak-->>Client: Returns Keycloak token
Client->>Keystone: Authenticate with Keycloak token
Keystone-->>Client: Returns Keystone token
Client->>OpenStack: Use Keystone token
OpenStack-->>Client: OpenStack API access granted
The examples below use the following placeholders:
<KEYCLOAK_URL>: The URL of your Keycloak instance.<KEYCLOAK_REALM>: The Keycloak realm for your Keystone domain, which isatmosphereby default.<OIDC_TOKEN>: The token issued by your identity provider.
Configure the identity provider¶
Both methods need the external identity provider registered in the Keycloak realm, so that Keycloak can validate its tokens.
In the Keycloak admin console, open the realm and go to Identity providers, then add an OpenID Connect v1.0 provider.
Set Issuer to the
issclaim of the tokens issued by the provider, enable Use JWKS URL and set JWKS URL to the provider’s signing keys endpoint.Make sure the provider issues tokens with a single
audclaim set tohttps://<KEYCLOAK_URL>/realms/<KEYCLOAK_REALM>. If the provider can’t do that, enable Allows Client ID as audience for assertions in the advanced settings and set Client ID to the audience that the provider issues.Enable the option for the method that you use:
For federated client authentication, enable Supports client assertions. Keycloak accepts each token only once by default. If the workload can’t get a new token for every request, also enable Allows client assertions to be re-used.
For the JWT authorization grant, enable JWT Authorization Grant in the Authorization Grant Settings section. Keycloak rejects tokens that are valid for longer than Max allowed assertion expiration, which is 5 minutes by default.
Federated client authentication¶
Create a client for the workload
In Clients, create an OpenID Connect client with Client authentication and Service account roles enabled. In the Credentials tab, set Client Authenticator to Signed JWT - Federated, set Identity provider to the alias of the provider that you configured, and set Federated subject to the
subclaim of the workload’s tokens.Realms created before Keycloak 26.6 don’t include this authenticator in the client authentication flow. In Authentication, duplicate the clients flow, add the Signed JWT - Federated step as Alternative, and bind the copy as the Client authentication flow.
Get a token from Keycloak
Send the token from your identity provider as the client assertion. Keycloak finds the client from the token, so no client ID or secret is needed.
curl -X POST "https://<KEYCLOAK_URL>/realms/<KEYCLOAK_REALM>/protocol/openid-connect/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \ -d "client_assertion=<OIDC_TOKEN>"
The response contains a token for the client’s service account, which Keycloak names
service-account-<CLIENT_ID>. Assign OpenStack roles to that user before you use the token with Keystone.{ "access_token" : ".....", "expires_in" : "...." }
Use the Keycloak token with OpenStack¶
Authenticate with Keystone Using the Keycloak Token
With the token issued by Keycloak, you can now authenticate with Keystone in order to obtain a fernet token which can be used to talk to all of the OpenStack APIs.
You will need to replace the following placeholders in the example code:
<OPENSTACK_AUTH_URL>: The URL of your Keystone authentication endpoint.<KEYCLOAK_OIDC_TOKEN>: The token issued by Keycloak.
curl "<OPENSTACK_AUTH_URL>/v3/OS-FEDERATION/identity_providers/atmosphere/protocols/openid/auth" \ -H "Authorization: Bearer <KEYCLOAK_OIDC_TOKEN>"
This response will return an unscoped Keystone token (not tied to any project) which will be in the
X-Subject-Tokenheader.HTTP/1.1 201 Created X-Subject-Token: <UNSCOPED_KEYSTONE_TOKEN>
List projects using the Keystone Token (optional, if you already know the project ID)
At this point, you have an unscoped token issued by Keystone which is not bound to any project. You will need to exchange that token for a project-scoped token in order to be able to interact with the OpenStack APIs.
You can choose to list what projects you have access to using the Keystone token that you have obtained.
You will need to replace the following placeholders in the example code:
<OPENSTACK_AUTH_URL>: The URL of your Keystone authentication endpoint.<UNSCOPED_KEYSTONE_TOKEN>: The token issued by Keystone.
curl "<OPENSTACK_AUTH_URL>/v3/projects" \ -H "X-Auth-Token: <UNSCOPED_KEYSTONE_TOKEN>"
This response will return a list of projects that you have access to.
{ "projects": [ { "id": "....", "name": "....", "description": "...." } ] }
Exchange the unscoped token for a project-scoped token
Once you have identified the project that you want to interact with, you can exchange the unscoped token for a project-scoped token.
You will need to replace the following placeholders in the example code:
<OPENSTACK_AUTH_URL>: The URL of your Keystone authentication endpoint.<UNSCOPED_KEYSTONE_TOKEN>: The token issued by Keystone.<PROJECT_ID>: The ID of the project that you want to interact with.
curl "<OPENSTACK_AUTH_URL>/v3/auth/projects" \ -H "Content-Type: application/json" \ -H "X-Auth-Token: <UNSCOPED_KEYSTONE_TOKEN>" \ -d '{ "auth": { "identity": { "methods": ["token"], "token": { "id": "<UNSCOPED_KEYSTONE_TOKEN>" } }, "scope": { "project": { "id": "<PROJECT_ID>" } } } }'
This response will return a project-scoped token which you can use to interact with the OpenStack APIs which will be in the
X-Subject-Tokenheader.HTTP/1.1 201 Created X-Subject-Token: <SCOPED_KEYSTONE_TOKEN>
OpenStack Keystone will provide the token details in the response body, including the full catalog of services that you have access to.
{ "token": { "methods": [ "token" ], "expires_at": "....", "issued_at": "....", "user": { "domain": { "id": "....", "name": "...." }, "id": "....", "name": "...." }, "audit_ids": [ "...." ], "catalog": [ { "endpoints": [ { "id": "....", "interface": "....", "region": "....", "url": "...." } ], "id": "....", "name": "....", "type": "...." } ], "project": { "domain": { "id": "....", "name": "...." }, "id": "....", "name": "...." } } }
You can then use the project-scoped token to interact with the OpenStack APIs, such as creating a server, listing servers, etc.