Authentication flow
datumctl utilizes OAuth 2.0 Authorization Code Flow with PKCE (Proof Key for Code Exchange) for user authentication. This flow is orchestrated by the datumctl auth login command and leverages OpenID Connect (OIDC) for discovering provider endpoints and verifying user identity.
Login process (datumctl auth login)
Section titled “Login process (datumctl auth login)”The following diagram illustrates the sequence of events during login:
sequenceDiagram
participant User
participant Datumctl as datumctl CLI
participant Browser
participant OSKeyring as OS Keyring
participant DatumAuth as Datum Cloud Auth Server
User->>+Datumctl: datumctl auth login [--hostname AUTH_HOST]
Datumctl->>DatumAuth: OIDC Discovery (using AUTH_HOST)
DatumAuth-->>Datumctl: OIDC Endpoints (Authorization, Token)
Datumctl->>Datumctl: Generate PKCE Code Verifier & Challenge
Datumctl->>Datumctl: Generate State parameter
Datumctl->>Browser: Open Authorization URL (incl. client_id, challenge, state, scopes)
Note over Browser,DatumAuth: User authenticates with Datum Cloud (e.g., username/password, SSO)
DatumAuth->>Browser: Redirect to datumctl callback URL (incl. code, state)
Browser->>Datumctl: GET /datumctl/auth/callback?code=AUTH_CODE&state=STATE
Datumctl->>Datumctl: Verify State parameter
Datumctl->>DatumAuth: Token Request (incl. AUTH_CODE, client_id, code_verifier)
DatumAuth-->>Datumctl: Token Response (Access Token, Refresh Token, ID Token)
Datumctl->>DatumAuth: Verify ID Token signature & claims
Datumctl->>Datumctl: Extract user info (email, name) from ID Token
Datumctl->>OSKeyring: Store Credentials (Hostname, Tokens, User Info) keyed by user email
Datumctl->>OSKeyring: Update Active User pointer
Datumctl->>OSKeyring: Update Known Users list
Datumctl-->>-User: Login SuccessfulKey steps:
- Discovery:
datumctluses the provided--hostname(or default) to perform OIDC discovery and find the necessary authorization and token endpoints. - PKCE Generation: A cryptographically random
code_verifieris generated. A SHA256 hash of this verifier creates thecode_challenge, which is sent in the authorization request. - Authorization Request: The user’s browser is directed to the Datum Cloud authorization endpoint with parameters including the
client_id,code_challenge,code_challenge_method=S256,state(for CSRF protection), and requestedscopes(openid, profile, email, offline_access). - User Authentication: The user authenticates directly with the Datum Cloud Auth Server via the browser.
- Callback: The Auth Server redirects the browser back to a local HTTP server temporarily run by
datumctl(http://localhost:8085/datumctl/auth/callback). The redirect includes theauthorization_codeand the originalstate. - State Verification:
datumctlverifies that the receivedstatematches the one it generated. - Token Exchange:
datumctlmakes a POST request to the token endpoint, exchanging theauthorization_codefor tokens. Crucially, it includes the originalcode_verifier. The Auth Server hashes this verifier and compares it to thecode_challengesent in step 3 to ensure the request comes from the same client. - Token Storage: Upon successful exchange,
datumctlreceives an access token, a refresh token (due to theoffline_accessscope), and an ID token. - ID Token Verification: The ID token (a JWT) is verified:
- Signature is checked against the provider’s public keys (obtained during discovery).
- Claims like
iss(issuer),aud(audience/client_id),exp(expiry),iat(issued at) are validated. - User information (email, name) is extracted from the claims.
- Keyring Storage: The complete set of credentials (
authutil.StoredCredentials), including the tokens, hostname, client ID, endpoints, scopes, and user info, is marshalled to JSON and stored securely in the OS keyring. The key for this entry is the user’s email address. - Active User: A pointer (
active_user) is also stored in the keyring, indicating which user’s credentials should be used by default. - Known Users: The user’s key (email) is added to a list (
known_users) in the keyring to facilitate listing and multi-user management.
Token refresh & usage
Section titled “Token refresh & usage”When commands like datumctl organizations list or datumctl auth get-token need to authenticate API calls:
- They call
authutil.GetTokenSource(). GetTokenSourceretrieves the active user’sStoredCredentialsfrom the keyring.- It configures an
oauth2.Configusing the stored Client ID and Token Endpoint. - It creates an
oauth2.TokenSourceinitialized with the stored token (access + refresh). - The standard Go
oauth2library uses thisTokenSource:- If the access token is valid, it’s used directly.
- If the access token is expired, the library automatically uses the refresh token and the
oauth2.Configto request a new access token from the token endpoint (Refresh Token Grant). - If the refresh is successful, the new token is used, and
datumctlattempts to update the stored token in the keyring (internal/cmd/auth/get_token.go).
Keyring interaction
Section titled “Keyring interaction”- The
internal/keyringpackage provides a wrapper aroundgithub.com/zalando/go-keyring. - The
internal/authutilpackage defines constants for the service name (datumctl-auth) and keys (active_user,known_users,<user-email>) used within the keyring. authutil.StoredCredentialsis the structure marshalled into JSON for storage.
Hostname derivation
Section titled “Hostname derivation”For commands that interact with the Datum Cloud API (not the auth server), the API hostname is needed (e.g., api.datum.net).
- The
logincommand stores the authentication hostname (auth.datum.net) in the keyring. - Commands needing the API hostname call
authutil.GetActiveCredentialsto retrieve the stored auth hostname. - They then call
authutil.DeriveAPIHostname, which currently implements a simple logic: if the auth hostname starts withauth., it replaces it withapi.. - If derivation fails, an error is returned, preventing the use of potentially incorrect default hostnames.