Skip to content
  • There are no suggestions because the search field is empty.

Securing CLI and CI/CD flows with ZITADEL

A practical guide to non-interactive authentication for CLI and CI/CD pipelines using ZITADEL—covering device authorization, service users, and token security.

CLI Tools (Human Users) – Device Authorization Flow

For command-line tools used by real people, the OAuth 2.0 Device Authorization Grant (device code flow) is the industry-standard solution. This flow allows a CLI to obtain an access token by having the user complete a one-time authentication in a browser, without the CLI itself needing to launch a web UI. ZITADEL fully supports the device authorization flow as per RFC 8628, making it the recommended method for CLI logins.

  • How Device Flow Works: When the user runs the CLI (e.g. myservice-cli login), the tool requests a device code from ZITADEL. The CLI then prints a URL and a one-time code for the user to enter in their web browser. The user visits ZITADEL’s verification page, enters the code, and logs in (including any MFA). Upon user approval, ZITADEL issues an access token (and refresh token if requested) to the CLI via a polling call. This token can then authenticate the CLI’s API calls as the user.

  • Industry Adoption: This device code technique is widely used by major CLI tools. For example, the official GitHub CLI (gh) uses a device code flow for gh auth login, and AWS’s CLI falls back to device flow when it can’t open a browser. It’s considered a secure, user-friendly way to log into CLI applications in headless or terminal-only environments.

  • Why Device Flow is Recommended: It avoids storing user passwords or long-lived personal tokens on the machine. Each login grants a short-lived access token scoped to what the CLI needs. The token can be automatically refreshed if the CLI requests the offline_access scope for a refresh token, eliminating frequent re-authentication while still expiring credentials regularly. This balances security with usability, and it leverages ZITADEL’s standard OAuth flows (no special PAT needed for human users).

Note: ZITADEL does not support personal access tokens for human users (i.e. you cannot manually create a long-lived API key for a normal user account). Therefore, interactive OAuth flows like Device Authorization are the intended solution for human CLI usage. If persistent credentials are needed for a CLI, one approach is to use the device flow once and store the obtained refresh token securely on the user’s machine (for example, in an OS keychain or encrypted config file). Many CLI tools do this so that subsequent runs can silently refresh tokens without another browser login, while still allowing the user or admin to revoke access by invalidating that refresh token.

CI/CD Pipelines (Service Accounts) – Non-Interactive Authentication

For automated systems such as CI/CD workflows, there is no interactive user to complete a browser login. The industry best practice is to use a service account or machine identity with its own credentials, and never embed personal credentials or passwords in the pipeline. ZITADEL provides a concept of service users (machine users) for exactly this purpose. The service account can be granted only the minimal permissions needed (principle of least privilege)—for example, just enough role to run the required API calls—so that a compromise of its token is limited in scope.

Zitadel supports several authentication methods for service users, each with pros and cons:

  1. Client Credentials (OAuth2 Client ID & Secret): You can register a service user for the CI pipeline and treat it as a confidential client. The CI job stores a client ID and client secret (issued by ZITADEL for that service user) and requests an access token via the OAuth2 Client Credentials flow at runtime. This yields a short-lived access token on demand. Security: The client secret is essentially a password, so it must be stored securely (e.g. in your CI system’s secret store or vault, not in Git or plaintext config). The benefit is that access tokens are short-lived and automatically scoped, reducing impact if leaked. Please note that static secrets carry some risk (if a secret leaks, an attacker can impersonate the service until it’s rotated), so treat the secret with high care and rotate periodically.

  2. Private Key JWT (Signed JWT Assertion): This is ZITADEL’s recommended method for machine-to-machine auth, providing enhanced security. You generate a key pair for the service account and upload the public key to ZITADEL. The CI pipeline then creates a JWT signed with the private key for each run, which ZITADEL accepts in exchange for an access token. The token obtained is again short-lived. Security: The private signing key must be kept safe (store it as a secret in your CI platform). The advantage is that no secret is transmitted directly; only the signed assertion is sent, and it can have a very short expiration. This method mitigates some risks of long-lived secrets or keys being exposed in transit. ZITADEL favors this approach for its balance of security and performance.

  3. Personal Access Token (PAT) for Service Account: ZITADEL allows generating a PAT for service users only (not regular users). This is essentially a ready-to-use bearer token that you create once in the ZITADEL console (or via API) and then configure in your CI/CD as a secret. The PAT can be set to never expire or to expire at a certain date, and you can revoke it by deleting it in ZITADEL. This approach is the closest to a traditional “API key” usage – your CI jobs would just send this token in the Authorization header for API calls. Security: While convenient (no complex exchange needed at runtime), a PAT is a long-lived bearer token. If it leaks, an attacker could access whatever that service account is allowed to do until the token is explicitly revoked or expires. There is no fine-grained scope on the PAT itself beyond the service account’s roles. Therefore, use PATs only for low-risk or internal scenarios, or when other methods are infeasible, and limit the permissions on that service user aggressively. Always store the PAT securely (e.g. in an environment variable injected by your CI runner, not in the code repository) and consider setting an expiration on it so it isn’t valid indefinitely.

Storing Secrets Securely: In all cases above, some secret material (a client secret, private key, or PAT) must reside in your CI/CD system to authenticate non-interactively. The industry best practice is to keep such secrets out of source code and config files, and instead use your CI/CD platform’s secret management features. For example, in GitHub Actions or GitLab CI, store the token/secret in the pipeline’s secure secret store and inject it as an environment variable at build/runtime. This way it’s not visible in version control or logs. You should treat service user credentials (whether a key or token) like any sensitive credential – secure storage and rotation are critical.