Skip to main content

Authentication & Identity Flows

The authentication architecture in this SDK is designed to handle diverse identity flows required by modern cloud-native environments, ranging from interactive user logins to automated service-to-service communication. At its core, the system abstracts the complexities of OAuth2 and OIDC behind a unified Authenticator interface, ensuring that the rest of the SDK can interact with Flyte Admin without concern for the underlying credential acquisition mechanism.

The Authenticator Abstraction

The Authenticator base class (found in flyte.remote._client.auth._authenticators.base) serves as the foundation for all identity flows. It manages three critical aspects of the authentication lifecycle:

  1. Configuration Resolution: Through _resolve_config(), the authenticator merges local client settings with remote configuration fetched from the Flyte backend. This allows the Flyte platform to dynamically dictate authentication endpoints, scopes, and audiences, reducing the configuration burden on the end-user.
  2. Thread and Coroutine Safety: Authentication often happens concurrently across multiple threads or async tasks. The refresh_credentials() method implements a "double-check" locking pattern using an asyncio.Lock. By tracking a _creds_id, the authenticator ensures that if multiple concurrent requests trigger a 401 error, only one task performs the actual network refresh while others wait and then reuse the newly acquired credentials.
  3. Persistence: To avoid redundant logins, the SDK integrates with the system keyring via KeyringStore. Successfully acquired credentials are automatically stored and retrieved based on the endpoint URL, unless explicitly disabled via the disable_keyring flag.

Interactive Identity: PKCE Flow

For interactive users, the SDK implements the Proof Key for Code Exchange (PKCE) flow via the PKCEAuthenticator. This is the default flow for most CLI-based interactions.

The design choice to use PKCE over standard Authorization Code flow is driven by security; PKCE eliminates the need for a client secret in public clients (like a local Python environment). The PKCEAuthenticator leverages an AuthorizationClient to:

  • Generate a cryptographically secure code_verifier and code_challenge.
  • Open a local browser window for the user to authenticate with the Identity Provider (IdP).
  • Handle the callback via a temporary local HTTP server to capture the authorization code.
# From flyte.remote._client.auth._authenticators.pkce
async def _do_refresh_credentials(self) -> Credentials:
await self._initialize_auth_client()
if self._creds:
try:
# Attempt to use the refresh token if available
return await self._auth_client.refresh_access_token(self._creds)
except AccessTokenNotFoundError:
logger.warning("Logging in...")

# Fall back to full browser-based flow
return await self._auth_client.get_creds_from_remote()

Service-to-Service: Client Credentials

In automated environments like CI/CD pipelines or scheduled tasks, interactive browser logins are impossible. The ClientCredentialsAuthenticator (in flyte.remote._client.auth._authenticators.client_credentials) addresses this by implementing the standard OAuth2 Client Credentials grant.

This flow requires a client_id and a client_credentials_secret. Unlike the PKCE flow, these credentials are typically provided directly via environment variables or configuration files. The implementation uses basic authentication to exchange these secrets for a bearer token:

# From flyte.remote._client.auth._authenticators.client_credentials
async def _do_refresh_credentials(self) -> Credentials:
cfg = await self._resolve_config()
authorization_header = token_client.get_basic_authorization_header(
self._client_id, self._client_credentials_secret
)

token, refresh_token, expires_in = await token_client.get_token(
token_endpoint=cfg.token_endpoint,
authorization_header=authorization_header,
# ... other params
)
return Credentials(...)

Headless Environments: Device Code Flow

The DeviceCodeAuthenticator provides a middle ground for "headless" environments—such as a remote SSH session—where a browser cannot be opened locally.

This flow follows the RFC 8628 standard. The SDK requests a device code from the Flyte backend and instructs the user to navigate to a URL on a different device (like a smartphone or laptop) to approve the request. The SDK then polls the token endpoint until the user completes the approval.

# From flyte.remote._client.auth._authenticators.device_code
"""Fall back to device flow"""
resp = await token_client.get_device_code(...)

full_uri = f"{resp.verification_uri}?user_code={resp.user_code}"
text = f"To Authenticate, navigate in a browser to the following URL: {full_uri}"
rich_print(text)

token, refresh_token, expires_in = await token_client.poll_token_endpoint(...)

Integration and Interceptors

The SDK does not require manual token management during API calls. Instead, the get_async_authenticator factory (in flyte.remote._client.auth._authenticators.factory.py) instantiates the correct authenticator based on the auth_type configuration.

These authenticators are then wrapped in ConnectRPC interceptors (e.g., AuthUnaryInterceptor). These interceptors automatically:

  1. Call get_auth_headers() before every request to inject the Authorization header.
  2. Detect 401 Unauthorized responses.
  3. Trigger refresh_credentials() and retry the request once, providing a seamless experience for the developer even as tokens expire.

This layered approach—separating the flow logic (Authenticators) from the transport logic (Interceptors)—allows the SDK to support complex identity requirements while maintaining a simple, high-level API for interacting with Flyte.