Skip to main content
Version: v0.65.1

Authentication methods in Keycloak

This example showcases three different ways of getting a refresh token from Keycloak, using a public client, confidential client and service account, and then authenticating in MLOps and Driverless AI.

Before you begin
  • Install DriverlessAI.
  • Install keycloak.
    pip install keycloak
  • Install keycloak-client.
    pip install keycloak-client
  • Enable the service account in Keycloak.
    • In Keycloak, each OpenID Connect client has a built-in service account. You can use this service account to obtain an access token. For more information about how to enable service accounts in Keycloak, see Using a service account in Keycloak.
      Note

      However, from Keycloak version 12.0.0, the OAuth2 client credentials grant endpoint does not issue refresh tokens by default. Therefore, for the Keycloak version 12.0.0 or later versions, you can enable a refresh token for the particular client. To do this, go to Client Details in the Keycloak admin console. Then navigate to the OpenID Connect Compatibility Modes section and turn on Use Refresh Tokens For Client Credentials Grant. For more information, see Client credentials grant without refresh token by default.

  • You will need the values for the following constants in order to successfully carry out the task. Contact your administrator to obtain deployment specific values.
ConstantDescription
KEYCLOAK_SERVER_URLDefines Keycloak's server URL.
KEYCLOAK_REALM_NAMEDefines Keycloak realm set during MLOps installation.
KEYCLOAK_TOKEN_ENDPOINT_URLDefines Keycloak's token endpoint URL. Usually: https://mlops.keycloak.domain/auth/realms/[fill-in-realm-name]/protocol/openid-connect/token
MLOPS_API_URLDefines the URL for the MLOps gateway component. Usually: https://api.mlops.my.domain
DRIVERLESS_API_URLDefines DriverlessAI API URL.
PUBLIC_CLIENT_IDDefines public client ID set in Keycloak.
USERNAMEDefines a username for user's direct access.
PASSWORDDefines a password for user's direct access.
ConstantDescription
KEYCLOAK_SERVER_URLDefines Keycloak's server URL.
KEYCLOAK_REALM_NAMEDefines Keycloak realm set during MLOps installation.
KEYCLOAK_TOKEN_ENDPOINT_URLDefines Keycloak's token endpoint URL. Usually: https://mlops.keycloak.domain/auth/realms/[fill-in-realm-name]/protocol/openid-connect/token
MLOPS_API_URLDefines the URL for the MLOps gateway component. Usually: https://api.mlops.my.domain
DRIVERLESS_API_URLDefines DriverlessAI API URL.
CONFIDENTIAL_CLIENT_IDDefines confidential client ID set in Keycloak.
CONFIDENTIAL_CLIENT_SECRETDefines confidential client secret set in Keycloak.
USERNAMEDefines a username for user's direct access.
PASSWORDDefines a password for user's direct access.
ConstantDescription
KEYCLOAK_SERVER_URLDefines Keycloak's server URL.
KEYCLOAK_REALM_NAMEDefines Keycloak realm set during MLOps installation.
KEYCLOAK_TOKEN_ENDPOINT_URLDefines Keycloak's token endpoint URL. Usually: https://mlops.keycloak.domain/auth/realms/[fill-in-realm-name]/protocol/openid-connect/token
MLOPS_API_URLDefines the URL for the MLOps gateway component. Usually: https://api.mlops.my.domain
DRIVERLESS_API_URLDefines DriverlessAI API URL.
SERVICE_ACCOUNT_IDDefines service account ID set in Keycloak.
SERVICE_ACCOUNT_SECRETDefines service account secret set in Keycloak.
USERNAMEDefines a username for user's direct access.
PASSWORDDefines a password for user's direct access.

The following steps demonstrate how you can use the MLOps Python client to get a refresh token from Keycloak in different ways.

  1. Download the KeycloakAuthentications.py file.

  2. Change the values of the following constants in your KeycloakAuthentications.py file as given in the preceding data table.

    KeycloakAuthentications.py
    ### Constants
    KEYCLOAK_SERVER_URL = "<your-keycloak-server-url>"
    KEYCLOAK_REALM_NAME = "<your-keycloak-realm-name>"
    KEYCLOAK_TOKEN_ENDPOINT_URL = "https://mlops.keycloak.domain/auth/realms/[fill-in-realm-name]/protocol/openid-connect/token"
    MLOPS_API_URL = "https://api.mlops.my.domain"
    DRIVERLESS_API_URL = "<your-dai-url>"
    PUBLIC_CLIENT_ID = "<your-public-client-id>"
    USERNAME = "<username>"
    PASSWORD = "<password>"
    KeycloakAuthentications.py
    ### Constants
    KEYCLOAK_SERVER_URL = "<your-keycloak-server-url>"
    KEYCLOAK_REALM_NAME = "<your-keycloak-realm-name>"
    KEYCLOAK_TOKEN_ENDPOINT_URL = "https://mlops.keycloak.domain/auth/realms/[fill-in-realm-name]/protocol/openid-connect/token"
    MLOPS_API_URL = "https://api.mlops.my.domain"
    DRIVERLESS_API_URL = "<your-dai-url>"
    CONFIDENTIAL_CLIENT_ID = "<your-confidential-client-id>"
    CONFIDENTIAL_CLIENT_SECRET = "<your-confidential-client-secret>"
    USERNAME = "<username>"
    PASSWORD = "<password>"
    KeycloakAuthentications.py
    ### Constants
    KEYCLOAK_SERVER_URL = "<your-keycloak-server-url>"
    KEYCLOAK_REALM_NAME = "<your-keycloak-realm-name>"
    KEYCLOAK_TOKEN_ENDPOINT_URL = "https://mlops.keycloak.domain/auth/realms/[fill-in-realm-name]/protocol/openid-connect/token"
    MLOPS_API_URL = "https://api.mlops.my.domain"
    DRIVERLESS_API_URL = "<your-dai-url"
    SERVICE_ACCOUNT_ID = "<your-service-account-id>"
    SERVICE_ACCOUNT_SECRET = "<your-service-account-secret>"
    USERNAME = "<username>"
    PASSWORD = "<password>"
  3. Run the KeycloakAuthentications.py file.

    python3 KeycloakAuthentications.py
  4. The script sets up authentication for the public client, confidential client, or service account. It then sends WhoAmI requests to MLOps which displays information about the current user and then to Driverless AI, which returns the Driverless AI client version and the projects created in Driverless AI. The following is a sample result.

    PUBLIC CLIENT <<<
    {'created_time': datetime.datetime(2022, 3, 8, 8, 22, 33, 197931, tzinfo=tzutc()),
    'id': 'f5b127c0-8cd4-476d-97e1-594b438eab7a',
    'primary_email': '',
    'username': 'h2omlops-tester'}
    {'version': '1.10.3.1', 'projects': ['H2O3UploadAndDeployExample', 'StandardFlow']}
    >>>
    CONFIDENTIAL CLIENT <<<
    {'created_time': datetime.datetime(2022, 3, 8, 8, 22, 33, 197931, tzinfo=tzutc()),
    'id': 'f5b127c0-8cd4-476d-97e1-594b438eab7a',
    'primary_email': '',
    'username': 'h2omlops-tester'}
    {'version': '1.10.3.1', 'projects': ['H2O3UploadAndDeployExample', 'StandardFlow']}
    >>>
    SERVICE ACCOUNT <<<
    {'created_time': datetime.datetime(2022, 9, 16, 11, 24, 45, 209975, tzinfo=tzutc()),
    'id': '1af1212d-30c8-4327-939f-4e09c97610d9',
    'primary_email': '',
    'username': 'service-account-mlops_python'}
    {'version': '1.10.3.1', 'projects': ['H2O3UploadAndDeployExample', 'StandardFlow']}
    >>>

Example walkthrough

This section provides a walkthrough of the KeycloakAuthentications.py file.

Functions to execute WhoAmI requests

The following function sends a WhoAmI request to MLOps, and returns the information about the user.

KeycloakAuthentications.py
def perform_mlops_whoami(
api_url: str, token_provider: mlops.TokenProvider
) -> mlops.StorageUser:
mlops_client = mlops.Client(
gateway_url=api_url,
token_provider=token_provider,
)

r: mlops.StorageWhoAmIResponse = mlops_client.storage.user.who_am_i(body={})
return r.user

This function sends a WhoAmI request to DriverlessAI, and returns the DriverlessAI client version and lists the projects created in DriverlessAI.

KeycloakAuthentications.py
def perform_dai_whoami(
api_url: str, token_provide: driverlessai.token_providers.OAuth2TokenProvider
) -> Dict[str, str]:
dai_client = driverlessai.Client(
address=api_url, token_provider=token_provide.ensure_fresh_token
)

return {
"version": dai_client.server.version,
"projects": [p.name for p in dai_client.projects.list()],
}

Authentication

This example demonstrates three different ways of establishing authentication using Keycloak: using a public client, confidential client, and service account. You can choose among the following functions to suit your requirement, depending on the type of authentication you want.

KeycloakAuthentications.py
    def public_client_auth(
public_client_id: str,
direct_access_grant_user: str,
direct_access_grant_password: str,
):
keycloak_openid = keycloak.KeycloakOpenID(
server_url=urljoin(KEYCLOAK_SERVER_URL, "/auth/"),
client_id=public_client_id,
realm_name=KEYCLOAK_REALM_NAME,
)

token = keycloak_openid.token(
direct_access_grant_user, direct_access_grant_password
)
refresh_token = keycloak_openid.refresh_token(
token["refresh_token"],
)

mlops_tp = mlops.TokenProvider(
refresh_token=refresh_token["refresh_token"],
client_id=public_client_id,
token_endpoint_url=KEYCLOAK_TOKEN_ENDPOINT_URL,
)

dai_tp = driverlessai.token_providers.OAuth2TokenProvider(
refresh_token=refresh_token["refresh_token"],
client_id=public_client_id,
token_endpoint_url=KEYCLOAK_TOKEN_ENDPOINT_URL,
token_introspection_url=urljoin(KEYCLOAK_TOKEN_ENDPOINT_URL, "/introspect"),
)

print("PUBLIC CLIENT", "<<<")
print(perform_mlops_whoami(MLOPS_API_URL, mlops_tp))
print(perform_dai_whoami(DRIVERLESS_API_URL, dai_tp))
print(">>>")
KeycloakAuthentications.py
    def confidential_client_auth(
confidential_client_id: str,
confidential_client_secret: str,
direct_access_grant_user: str,
direct_access_grant_password: str,
):
keycloak_openid = keycloak.KeycloakOpenID(
server_url=urljoin(KEYCLOAK_SERVER_URL, "/auth/"),
client_id=confidential_client_id,
client_secret_key=confidential_client_secret,
realm_name=KEYCLOAK_REALM_NAME,
)

token = keycloak_openid.token(
direct_access_grant_user, direct_access_grant_password
)
refresh_token = keycloak_openid.refresh_token(
token["refresh_token"],
)

mlops_tp = mlops.TokenProvider(
refresh_token=refresh_token["refresh_token"],
client_id=confidential_client_id,
client_secret=confidential_client_secret,
token_endpoint_url=KEYCLOAK_TOKEN_ENDPOINT_URL,
)

dai_tp = driverlessai.token_providers.OAuth2TokenProvider(
refresh_token=refresh_token["refresh_token"],
client_id=confidential_client_id,
client_secret=confidential_client_secret,
token_endpoint_url=KEYCLOAK_TOKEN_ENDPOINT_URL,
token_introspection_url=urljoin(KEYCLOAK_TOKEN_ENDPOINT_URL, "/introspect"),
)

print("CONFIDENTIAL CLIENT", "<<<")
print(perform_mlops_whoami(MLOPS_API_URL, mlops_tp))
print(perform_dai_whoami(DRIVERLESS_API_URL, dai_tp))
print(">>>")
KeycloakAuthentications.py
    def service_account_auth(
service_account_client_id: str, service_account_client_secret: str
):
keycloak_openid = keycloak.KeycloakOpenID(
server_url=urljoin(KEYCLOAK_SERVER_URL, "/auth/"),
client_id=service_account_client_id,
client_secret_key=service_account_client_secret,
realm_name=KEYCLOAK_REALM_NAME,
)

token = keycloak_openid.token(grant_type=["client_credentials"])
refresh_token = keycloak_openid.refresh_token(
token["refresh_token"],
)

mlops_tp = mlops.TokenProvider(
refresh_token=refresh_token["refresh_token"],
client_id=service_account_client_id,
client_secret=service_account_client_secret,
token_endpoint_url=KEYCLOAK_TOKEN_ENDPOINT_URL,
)

dai_tp = driverlessai.token_providers.OAuth2TokenProvider(
refresh_token=refresh_token["refresh_token"],
client_id=service_account_client_id,
client_secret=service_account_client_secret,
token_endpoint_url=KEYCLOAK_TOKEN_ENDPOINT_URL,
token_introspection_url=urljoin(KEYCLOAK_TOKEN_ENDPOINT_URL, "/introspect"),
)

print("SERVICE ACCOUNT", "<<<")
print(perform_mlops_whoami(MLOPS_API_URL, mlops_tp))
print(perform_dai_whoami(DRIVERLESS_API_URL, dai_tp))
print(">>>")

Use the following code to run the preceding functions.

KeycloakAuthentications.py
if __name__ == "__main__":
public_client_auth(
public_client_id=PUBLIC_CLIENT_ID,
direct_access_grant_user=USERNAME,
direct_access_grant_password=PASSWORD,
)
confidential_client_auth(
confidential_client_id=CONFIDENTIAL_CLIENT_ID,
confidential_client_secret=CONFIDENTIAL_CLIENT_SECRET,
direct_access_grant_user=USERNAME,
direct_access_grant_password=PASSWORD,
)
service_account_auth(
service_account_client_id=SERVICE_ACCOUNT_ID,
service_account_client_secret=SERVICE_ACCOUNT_SECRET,
)

Feedback