OIDC Authentication Examples#
This section demonstrates various approaches to OIDC (OpenID Connect) authentication using the Authorization Code + PKCE flow with different token storage strategies.
Basic OIDC Login#
Demonstrates how to perform a basic OIDC login. Tokens are kept in memory only and are not persisted.
The login process:
Opens your default browser to the OIDC provider’s login page
You authenticate with your credentials
Redirects back with an authorization code
Exchanges the code for tokens
Tokens are kept in memory only (not persisted)
Code#
# Copyright (C) 2022 - 2026 Synopsys, Inc. and ANSYS, Inc. All rights reserved.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Basic OIDC login example.
Demonstrates how to perform an OIDC login and use the access token.
Tokens are kept in memory only (not persisted).
If these tokens are passed to ``Client`` without ``token_storage``, refreshed
tokens also remain in memory only and are not persisted across runs.
"""
import argparse
import logging
from ansys.hps.client import Client
from ansys.hps.client.auth.api.oidc_login import browser_login
log = logging.getLogger(__name__)
def main(hps_url: str, verify_ssl: bool):
"""Perform OIDC login and log the access token."""
storage_mode = "memory"
# Perform login - opens browser for authentication
tokens = browser_login(hps_url=hps_url, verify_ssl=verify_ssl)
# Configure Client with explicit in-memory refresh persistence mode.
_ = Client(
url=hps_url,
access_token=tokens["access_token"],
refresh_token=tokens.get("refresh_token"),
token_storage=storage_mode,
verify=verify_ssl,
)
# Access token is now available
log.info("Access Token: %s...", tokens["access_token"][:50])
log.info("Token Expires In: %s seconds", tokens.get("expires_in"))
log.info("TLS certificate verification enabled: %s", verify_ssl)
log.info("Client token_storage is set to 'memory' (refresh updates are in-process only)")
# Use the access token in your API calls
# Example: requests.get(url, headers={"Authorization": f"Bearer {tokens['access_token']}"})
return tokens
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Perform basic OIDC login using browser flow.")
parser.add_argument("-U", "--hps-url", default="https://localhost:8443/hps")
parser.add_argument(
"--insecure",
action="store_true",
help="Disable TLS certificate verification for local/self-signed endpoints.",
)
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format="%(message)s")
main(hps_url=args.hps_url, verify_ssl=not args.insecure)
Usage#
Run the example:
cd examples/oidc
python basic_login.py
Output:
Access Token: eyJhbGciOiJSUzI1NiIsInR5cCI...
Token Expires In: 3600 seconds
Notes#
Tokens are kept in memory only and are not persisted to disk
The token is lost when the script exits
- If you pass these tokens to
ansys.hps.client.Clientwith default token_storage=\"memory\", refreshed tokens are also in-memory only and are not persisted across runs
- If you pass these tokens to
For persistent storage, see the examples below
OIDC Login with System Keyring Storage#
Demonstrates how to save OIDC tokens to the system credential manager for secure, persistent storage. This is the recommended storage method for security.
The system credential manager varies by platform:
Windows: Credential Manager
macOS: Keychain
Linux: Secret Service (via python-keyring)
Code#
# Copyright (C) 2022 - 2026 Synopsys, Inc. and ANSYS, Inc. All rights reserved.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""OIDC login with system keyring storage.
Demonstrates how to save refresh-token data to the system credential manager:
- Windows: Credential Manager
- macOS: Keychain
- Linux: Secret Service (via python-keyring)
Requires: pip install keyring
This example also demonstrates creating ``Client`` with
``token_storage=\"keyring\"`` so automatic refresh updates are persisted
to keyring across runs. Access tokens remain memory-only.
"""
import argparse
import logging
from ansys.hps.client import Client
from ansys.hps.client.auth.api.oidc_login import browser_login, save_tokens
log = logging.getLogger(__name__)
def main(hps_url: str, verify_ssl: bool):
"""Perform OIDC login and save tokens to system keyring."""
storage_mode = "keyring"
# Perform login
tokens = browser_login(hps_url=hps_url, verify_ssl=verify_ssl)
# Save tokens to system keyring (preferred storage method)
try:
result = save_tokens(tokens, hps_url=hps_url, storage=storage_mode)
except RuntimeError as ex:
log.error("%s", ex)
return None
# Configure Client to persist automatic token refresh updates to keyring.
_ = Client(
url=hps_url,
access_token=tokens["access_token"],
refresh_token=tokens.get("refresh_token"),
token_storage=storage_mode,
verify=verify_ssl,
)
if result is None:
log.info("Tokens saved to system keyring")
log.info("Client token_storage is set to 'keyring' for persistent refresh updates")
log.info("TLS certificate verification enabled: %s", verify_ssl)
log.info("Token Expires In: %s seconds", tokens.get("expires_in"))
return tokens
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Perform OIDC login and persist tokens using system keyring storage."
)
parser.add_argument("-U", "--hps-url", default="https://localhost:8443/hps")
parser.add_argument(
"--insecure",
action="store_true",
help="Disable TLS certificate verification for local/self-signed endpoints.",
)
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format="%(message)s")
main(hps_url=args.hps_url, verify_ssl=not args.insecure)
Prerequisites#
Install the keyring package:
pip install keyring
Usage#
Run the example:
cd examples/oidc
python login_with_keyring.py
Output:
Tokens saved to system keyring
Token Expires In: 3600 seconds
Security Benefits#
Tokens are encrypted at rest in the system credential manager
Credentials are managed by the operating system
No plain-text files on disk
Automatic cleanup when user logs out (on some systems)
Notes#
Requires the
keyringpackage- Tokens can be loaded from keyring with
ansys.hps.client.auth.api.oidc_login.load_tokens()usingstorage="keyring"
For automatic refresh persistence across runs in
ansys.hps.client.Client, initialize the client withtoken_storage=\"keyring\"
OIDC Login with Disk Storage#
Demonstrates how to save OIDC tokens to disk with platform-specific security.
Storage locations:
Windows:
%USERPROFILE%\.ansys\hps_tokens.json(encrypted with DPAPI)Unix/Linux:
~/.ansys/hps/hps_tokens.json(file permissions 0o600)
Code#
# Copyright (C) 2022 - 2026 Synopsys, Inc. and ANSYS, Inc. All rights reserved.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""OIDC login with disk storage.
Demonstrates how to save tokens to disk with platform-specific security:
- Windows: Encrypted with DPAPI (user-scoped) at %USERPROFILE%\\.ansys\\hps_tokens.json
- Unix/Linux: Plaintext with restrictive permissions (0o600) at ~/.ansys/hps/hps_tokens.json
This example also demonstrates creating ``Client`` with
``token_storage=\"disk\"`` so automatic refresh updates are persisted
to disk across runs. Access tokens remain memory-only.
"""
import argparse
import logging
from ansys.hps.client import Client
from ansys.hps.client.auth.api.oidc_login import browser_login, save_tokens
log = logging.getLogger(__name__)
def main(hps_url: str, verify_ssl: bool):
"""Perform OIDC login and save tokens to disk."""
storage_mode = "disk"
# Perform login
tokens = browser_login(hps_url=hps_url, verify_ssl=verify_ssl)
# Save tokens to disk
token_file = save_tokens(tokens, hps_url=hps_url, storage=storage_mode)
# Configure Client to persist automatic token refresh updates to disk.
_ = Client(
url=hps_url,
access_token=tokens["access_token"],
refresh_token=tokens.get("refresh_token"),
token_storage=storage_mode,
verify=verify_ssl,
)
log.info("Tokens saved to: %s", token_file)
log.info("TLS certificate verification enabled: %s", verify_ssl)
log.info("Client token_storage is set to 'disk' for persistent refresh updates")
log.info("Token Expires In: %s seconds", tokens.get("expires_in"))
return tokens
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Perform OIDC login and persist tokens using disk storage."
)
parser.add_argument("-U", "--hps-url", default="https://localhost:8443/hps")
parser.add_argument(
"--insecure",
action="store_true",
help="Disable TLS certificate verification for local/self-signed endpoints.",
)
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format="%(message)s")
main(hps_url=args.hps_url, verify_ssl=not args.insecure)
Usage#
Run the example:
cd examples/oidc
python login_with_disk_storage.py
Output:
Tokens saved to: C:\Users\username\.ansys\hps_tokens.json
Token Expires In: 3600 seconds
Security#
Windows#
On Windows, tokens are encrypted using DPAPI (Data Protection API), which provides user-scoped encryption. The encrypted file is only readable by the user who encrypted it on the same computer.
Unix/Linux#
On Unix/Linux systems, the token file is created with restrictive permissions (0o600). Only the owner can read and write it.
Notes#
Tokens persist across script invocations
- Tokens can be loaded from disk with
ansys.hps.client.auth.api.oidc_login.load_tokens()usingstorage="disk"
For automatic refresh persistence across runs in
ansys.hps.client.Client, initialize the client withtoken_storage=\"disk\"For higher security, use keyring storage instead
Load and Use Saved Tokens#
Demonstrates how to load previously saved tokens and use them in API calls.
Tokens are loaded from an explicitly selected backend (for example,
storage="keyring" or storage="disk").
Code#
# Copyright (C) 2022 - 2026 Synopsys, Inc. and ANSYS, Inc. All rights reserved.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Load and use previously saved tokens.
Demonstrates how to load tokens from an explicitly selected storage backend
and use them in API calls.
"""
import argparse
import logging
import time
from ansys.hps.client.auth.api.oidc_login import load_tokens
log = logging.getLogger(__name__)
def is_token_expired(tokens: dict, buffer_seconds: int = 60) -> bool:
"""Return True when token is expired or near expiry."""
expires_in = tokens.get("expires_in")
saved_at = tokens.get("saved_at")
if expires_in is None or saved_at is None:
return True
return (saved_at + expires_in - buffer_seconds) <= time.time()
def main(storage_mode: str, verify_ssl: bool):
"""Load saved tokens and use them."""
tokens = load_tokens(storage=storage_mode)
if not tokens:
log.info("No saved tokens found in %s storage. Please run login first.", storage_mode)
return
log.info("Loaded tokens for: %s", tokens.get("hps_url"))
if not tokens.get("access_token"):
log.info("No access token is persisted by design. Refresh to obtain a new access token.")
return
# Check if token is expired (with 60 second buffer)
if is_token_expired(tokens, buffer_seconds=60):
log.info("Token is expired or expiring soon. Please refresh.")
return
log.info("Token is valid")
log.info("Access Token: %s...", tokens["access_token"][:50])
log.info("TLS certificate verification enabled: %s", verify_ssl)
# Example: Use the token in API calls
# response = requests.get(
# "https://localhost:8443/hps/api/v1/projects",
# headers={"Authorization": f"Bearer {tokens['access_token']}"},
# verify=verify_ssl,
# )
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Load and inspect previously saved OIDC tokens from a selected backend."
)
parser.add_argument(
"-s",
"--storage",
default="keyring",
choices=["keyring", "disk"],
help="Token storage backend to load from (default: keyring).",
)
parser.add_argument(
"--insecure",
action="store_true",
help="Disable TLS certificate verification for local/self-signed endpoints.",
)
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format="%(message)s")
main(storage_mode=args.storage, verify_ssl=not args.insecure)
Usage#
First, save tokens using one of the preceding login examples. Then run this example:
cd examples/oidc
python load_saved_tokens.py
Output:
Loaded tokens for: https://localhost:8443/hps
Token is valid
Access Token: eyJhbGciOiJSUzI1NiIsInR5cCI...
Features#
Uses an explicit storage selection (keyring or disk)
Checks token expiration with configurable buffer (default 60 seconds)
Provides access token for use in API calls
Error Handling#
If no saved tokens are found, you’ll see:
No saved tokens found. Please run login first.
If tokens are expired or expiring soon:
Token is expired or expiring soon. Please refresh.
In this case, see the token refresh example below.
Notes#
Choose the backend explicitly via
load_tokens(storage=...)The buffer parameter (default 60 seconds) provides a safety margin before expiration
Use the
Authorizationheader with the access token in API requests
Refresh OIDC Tokens#
Demonstrates how to refresh tokens using the refresh_token grant. This allows you to obtain a new access token without requiring user re-authentication.
The refresh token flow is useful when the access token expires but the refresh token is still valid.
Code#
# Copyright (C) 2022 - 2026 Synopsys, Inc. and ANSYS, Inc. All rights reserved.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Refresh saved tokens.
Demonstrates how to refresh tokens using the refresh_token grant for an
explicitly selected storage backend.
"""
import argparse
import logging
from ansys.hps.client.auth.api.oidc_login import load_tokens, refresh_tokens, save_tokens
log = logging.getLogger(__name__)
def main(storage_mode: str, verify_ssl: bool):
"""Refresh saved tokens."""
# Load current tokens from selected storage
current_tokens = load_tokens(storage=storage_mode)
if not current_tokens:
log.info("No saved tokens found in %s storage. Please run login first.", storage_mode)
return
log.info("Refreshing tokens...")
# Refresh the tokens from the same selected backend
new_tokens = refresh_tokens(
hps_url=current_tokens.get("hps_url"),
storage=storage_mode,
verify_ssl=verify_ssl,
)
if not new_tokens:
log.info("Token refresh failed. You may need to login again.")
return
# Save refreshed tokens back to the same storage backend
result = save_tokens(new_tokens, new_tokens.get("hps_url"), storage=storage_mode)
if storage_mode == "keyring":
log.info("New tokens saved to system keyring")
else:
log.info("New tokens saved to disk at: %s", result)
log.info("New token expires in: %s seconds", new_tokens.get("expires_in"))
log.info("New refresh token expires in: %s seconds", new_tokens.get("refresh_expires_in"))
log.info("TLS certificate verification enabled: %s", verify_ssl)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Refresh saved OIDC tokens from a selected storage backend."
)
parser.add_argument(
"-s",
"--storage",
default="keyring",
choices=["keyring", "disk"],
help="Token storage backend to use (default: keyring).",
)
parser.add_argument(
"--insecure",
action="store_true",
help="Disable TLS certificate verification for local/self-signed endpoints.",
)
args = parser.parse_args()
logging.basicConfig(level=logging.INFO, format="%(message)s")
main(storage_mode=args.storage, verify_ssl=not args.insecure)
Prerequisites#
You must have previously saved tokens using one of the preceding login examples.
Usage#
Run the example:
cd examples/oidc
python refresh_tokens_example.py
Output:
Refreshing tokens...
New tokens saved to system keyring
New token expires in: 3600 seconds
New refresh token expires in: 86400 seconds
Workflow#
Loads current tokens from the selected storage backend
Uses the refresh_token to obtain new tokens without user interaction
Saves the new tokens back to the same selected backend
This allows your app to:
Automatically refresh tokens when they expire
Keep the refresh token synchronized across invocations
Maintain a valid access token for API calls
Error Handling#
If no saved tokens are found:
No saved tokens found. Please run login first.
If refresh fails (for example, refresh token expired):
Token refresh failed. You may need to login again.
Automation#
To automatically refresh tokens when needed, combine token expiration checking with refresh:
from ansys.hps.client.auth.api.oidc_login import (
load_tokens,
_is_token_expired,
refresh_tokens,
save_tokens
)
tokens = load_tokens(storage="keyring")
if tokens and _is_token_expired(tokens, buffer_seconds=300):
# Refresh if expiring in next 5 minutes
new_tokens = refresh_tokens(storage="keyring")
if new_tokens:
save_tokens(new_tokens, new_tokens.get("hps_url"), storage="keyring")
Notes#
Refresh tokens typically have a longer expiration time than access tokens
If both access and refresh tokens expire, you must login again
Always save refreshed tokens back to storage for consistency