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.
- 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.
- 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.
- 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.
Constant | Description |
---|---|
KEYCLOAK_SERVER_URL | Defines Keycloak's server URL. |
KEYCLOAK_REALM_NAME | Defines Keycloak realm set during MLOps installation. |
KEYCLOAK_TOKEN_ENDPOINT_URL | Defines Keycloak's token endpoint URL. Usually: https://mlops.keycloak.domain/auth/realms/[fill-in-realm-name]/protocol/openid-connect/token |
MLOPS_API_URL | Defines the URL for the MLOps gateway component. Usually: https://api.mlops.my.domain |
DRIVERLESS_API_URL | Defines DriverlessAI API URL. |
PUBLIC_CLIENT_ID | Defines public client ID set in Keycloak. |
USERNAME | Defines a username for user's direct access. |
PASSWORD | Defines a password for user's direct access. |
Constant | Description |
---|---|
KEYCLOAK_SERVER_URL | Defines Keycloak's server URL. |
KEYCLOAK_REALM_NAME | Defines Keycloak realm set during MLOps installation. |
KEYCLOAK_TOKEN_ENDPOINT_URL | Defines Keycloak's token endpoint URL. Usually: https://mlops.keycloak.domain/auth/realms/[fill-in-realm-name]/protocol/openid-connect/token |
MLOPS_API_URL | Defines the URL for the MLOps gateway component. Usually: https://api.mlops.my.domain |
DRIVERLESS_API_URL | Defines DriverlessAI API URL. |
CONFIDENTIAL_CLIENT_ID | Defines confidential client ID set in Keycloak. |
CONFIDENTIAL_CLIENT_SECRET | Defines confidential client secret set in Keycloak. |
USERNAME | Defines a username for user's direct access. |
PASSWORD | Defines a password for user's direct access. |
Constant | Description |
---|---|
KEYCLOAK_SERVER_URL | Defines Keycloak's server URL. |
KEYCLOAK_REALM_NAME | Defines Keycloak realm set during MLOps installation. |
KEYCLOAK_TOKEN_ENDPOINT_URL | Defines Keycloak's token endpoint URL. Usually: https://mlops.keycloak.domain/auth/realms/[fill-in-realm-name]/protocol/openid-connect/token |
MLOPS_API_URL | Defines the URL for the MLOps gateway component. Usually: https://api.mlops.my.domain |
DRIVERLESS_API_URL | Defines DriverlessAI API URL. |
SERVICE_ACCOUNT_ID | Defines service account ID set in Keycloak. |
SERVICE_ACCOUNT_SECRET | Defines service account secret set in Keycloak. |
USERNAME | Defines a username for user's direct access. |
PASSWORD | Defines 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.
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>"Run the
KeycloakAuthentications.py
file.python3 KeycloakAuthentications.py
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.
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.
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.
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(">>>")
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(">>>")
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.
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,
)
- Submit and view feedback for this page
- Send feedback about H2O MLOps to cloud-feedback@h2o.ai