Authenticate in OpenStack API as a federated OIDC user

This section offers an example workflow of federated user authentication in OpenStack using an external identity provider and OpenID Connect (OIDC) protocol. The example illustrates a typical HTTP-based interchange of authentication data happening underneath between the client software, identity provider, and MOSK Identity service (OpenStack Keystone) when a cloud user logs in to a cloud using their corporate or social ID, depending on cloud configuration.

The instructions below can be handy for cloud operators who want to delve into how federated authentication operates in OpenStack and troubleshoot any related issues, as well as advanced cloud users keen on crafting their own basic automation tools for cloud interactions.

The instructions are provided for educational purposes. Mirantis encourages the majority of cloud users to rely on existing mature tools and libraries, such as openstacksdk, keystoneauth, python-openstackclient, or gophercloud to communicate with OpenStack APIs in a programmable manner.

Warning

Mirantis advises cloud users not to rely on federated authentication when managing their cloud resources using command line and cloud automation tool. Instead, consider using OpenStack built-in application credentials mechanism to ensure secure and reliable access to OpenStack APIs of your MOSK cloud. See Manage application credentials for details.

Verify cloud configuration with the cloud administrator

For cloud users to be able to log in to an OpenStack cloud using their federated identity, the cloud administrator should configure the cloud to integrate with an external OIDC-compatible identity provider, such as Mirantis Container Cloud IAM (Keycloak) with all the necessary resources pre-created in the OpenStack Keystone API.

To authenticate in OpenStack using your federated identity, an OIDC-compatible identity provider protocol, and the v3oidcpassword authentication method, get yourself acquainted with the authentication parameters listed below. You can extract the required information from the OpenStack RC file that is available for download from OpenStack Dashboard (Horizon) once you log in to it with your federated identity, or the clouds.yaml file.

OpenStack RC file - authetication parameters

Parameter

Description

OS_AUTH_URL

The URL pointing to the OpenStack Keystone API.

OS_DISCOVERY_ENDPOINT

The URL pointing to the OIDC discovery document served by the identity provider, usually ends with .well-known/openid-configuration.

OS_CLIENT_ID

The identifier of the OIDC client to use.

OS_CLIENT_SECRET

The secret for the OIDC client. Many OIDC providers require this parameter. Some providers, including Mirantis Container Cloud IAM (Keycloak), allow so-called public clients, where secrets are not used and can be any string.

OS_OPENID_SCOPE

The scope requested when using OIDC authentication. This is at least openid, but your identity provider may allow to return additional scopes.

OS_IDENTITY_PROVIDER

The name of the corresponding identity provider object as created in the Keystone API.

OS_PROTOCOL

The name of the protocol object as created in the Keystone API.

OS_USERNAME

Your user name.

OS_PASSWORD

Your password in the identity provider, such as Mirantis Container Cloud IAM (Keycloak).

Note

Additionally, to obtain a scoped token, you need information about the target scope, such as, OS_PROJECT_DOMAIN_NAME and OS_PROJECT_NAME.

Below is an example of RC file to set environment variables used in the further code examples for the project scope authentication:

export OS_AUTH_URL=https://keystone.it.just.works
export OS_DISCOVERY_ENDPOINT=https://keycloak.it.just.works/auth/realms/iam/.well-known/openid-configuration
export OS_CLIENT_ID=os
export OS_CLIENT_SECRET=someRandomClientSecretMightBeNull
export OS_USERNAME=writer
export OS_PASSWORD=password
export OS_IDENTITY_PROVIDER=keycloak
export OS_PROTOCOL=mapped
export OS_OPENID_SCOPE=openid
export OS_PROJECT_DOMAIN_NAME=Default
export OS_PROJECT_NAME=admin

Obtain the OIDC access token

  1. Obtain the token endpoint of the identity provider by extracting it from the OIDC discovery document:

    token_endpoint=$(curl -sk -X GET $OS_DISCOVERY_ENDPOINT | jq -r .token_endpoint)
    
  2. Obtain the access token from the identity provider by sending the POST request to the token endpoint that will return the access token in exchange to the login credentials:

    access_token=$(curl -sk \
     -X POST $token_endpoint \
     -u $OS_CLIENT_ID:$OS_CLIENT_SECRET \
     -d "username=${OS_USERNAME}&password=${OS_PASSWORD}&scope=${OS_OPENID_SCOPE}&grant_type=password" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     | jq -r .access_token)
    

    Note

    As per OIDC RFC, the request to the endpoint must be of the URL-encoded content type.

Now, you can exchange the OIDC access token for an unscoped token from OpenStack Keystone.

Obtain the unscoped token from OpenStack Keystone

The Keystone token is included in the response header. However, the response body in the JSON format often contains additional data that may prove useful for certain applications. The following example excludes the body by using the -I flag:

unscoped_token=$(curl -sik \
 -I \
 -X POST $OS_AUTH_URL/OS-FEDERATION/identity_providers/${OS_IDENTITY_PROVIDER}/protocols/${OS_PROTOCOL}/auth \
 -H "Authorization: Bearer $access_token" \
 | grep x-subject-token \
 | awk '{print $2}' \
 | tr -d '\r')

Note

The tr -d '\r' line trims the carriage return characters from grep and awk outputs so that the extracted data can be properly inserted into the JSON authentication request later.

Now, you can generate a scoped token from OpenStack Keystone using the unscoped token and specifying the project scope.

Optional. Discover available scopes

In case you do not know beforehand the OpenStack authorization scope you want to log in to, use the unscoped token to get a list of the available scopes:

curl -sk $OS_AUTH_URL/auth/projects \
 -H "X-Auth-Token: $unscoped_token" | jq .projects

Obtain the scoped token from OpenStack Keystone

  1. Create a JSON-formatted authentication request using the following script. For example, for the project scope:

    token_request=$(mktemp)
    cat > $token_request << EOJSON
    {
      "auth": {
        "identity": {
          "methods": [
            "token"
          ],
          "token": {
            "id": "$unscoped_token"
          }
        },
        "scope": {
          "project": {
            "domain": {
              "name": "$OS_PROJECT_DOMAIN_NAME"
            },
            "name": "$OS_PROJECT_NAME"
          }
        }
      }
    }
    EOJSON
    
  2. Send the authentication request to OpenStack Keystone to obtain a scoped token:

    scoped_token=$(curl -sik \
     -X POST $OS_AUTH_URL/auth/tokens \
     -d "@$token_request" -H "Content-Type: application/json" \
     | grep x-subject-token \
     | awk '{print $2}' \
     | tr -d '\r')
    
  3. Remove the temporary file used to store the authentication request:

    rm $token_request
    

Use the scoped token to access OpenStack services APIs

To verify that you can access the OpenStack services APIs, for example, obtain the list of available images:

curl -sk -H "X-Auth-Token: $scoped_token" \
 -X GET https://glance.it.just.works/v2/images

Replace https://glance.it.just.works with the endpoint of the MOSK Image service (OpenStack Glance) that you can obtain from the Access & Security dashboard of OpenStack Horizon.