OIDC concepts
This page explains how workload identity federation works in Thalassa Cloud — the concepts, vocabulary, and access control model. Read this before configuring identity providers or federated identities.
The problem OIDC solves
CI/CD pipelines and other automated systems need to call the Thalassa Cloud API. The traditional approach is to create service account access credentials (a client ID and secret) and store them in GitHub Secrets, GitLab CI variables, or a vault.
That works, but long-lived secrets in CI are risky: they can leak through logs, forked PRs, or misconfigured workflows, and they require manual rotation.
Workload identity federation removes the secret from CI. Instead, your pipeline receives a short-lived OIDC ID token from the CI platform (GitHub, GitLab, etc.). Thalassa Cloud validates that token and, if it matches your rules, returns a bearer access token that acts as a specific service account.
In one sentence: your CI job proves who it is with a JWT; Thalassa Cloud checks that proof and issues a token that impersonates a service account you configured.
How the flow works
sequenceDiagram
participant CI as CI platform
participant TC as Thalassa Cloud API
participant SA as Service account
CI->>CI: Job starts, issues OIDC ID token
CI->>TC: POST /oidc/token (ID token + org + SA ID)
TC->>TC: Verify signature (JWKS), issuer, audience, claims
TC->>CI: Bearer access token (short-lived)
CI->>TC: API call with bearer token
TC->>SA: Evaluate SA roles + token scopes
TC->>CI: Response
- CI platform issues an ID token — A signed JWT that describes the job (repository, branch, environment, etc.).
- Pipeline exchanges the token — Sends the ID token to
https://api.thalassa.cloud/oidc/token. - Thalassa Cloud validates and matches — Checks the signature, issuer, audience, and claim rules on the federated identity.
- Bearer token is returned — A short-lived Thalassa Cloud access token scoped to the service account.
- API calls use the bearer token — Same as any other Thalassa Cloud API authentication.
The cast of characters
| Component | What it is | Where you configure it |
|---|---|---|
| CI platform | GitHub Actions, GitLab CI, or another OIDC issuer | Your pipeline YAML |
| Identity provider | Trust relationship with an external OIDC issuer (issuer URL + JWKS) | IAM → Identity Providers |
| Service account | Non-human identity with IAM roles | IAM → Service Accounts |
| Federated identity | Rules linking an identity provider to a service account | Service account → Federated Identities |
| OIDC ID token | Short-lived JWT from the CI platform | Issued automatically by CI |
| Bearer access token | Short-lived Thalassa Cloud token after exchange | Returned by /oidc/token |
Why two configuration steps?
Engineers often ask why there is both an identity provider and a federated identity. They serve different purposes:
| Step | Scope | Purpose |
|---|---|---|
| Identity provider | Organisation-wide, reusable | “We trust tokens signed by GitHub” — defines the issuer and how to verify signatures (JWKS) |
| Federated identity | Per service account | “Only repo:myorg/myapp:ref:refs/heads/main may impersonate SA ci-deploy” — defines who can exchange tokens and what scopes the result gets |
One identity provider (e.g. github-actions) can back many federated identities on different service accounts, each with different claim matching rules.
OIDC federation vs access credentials
| Access credentials | OIDC workload federation | |
|---|---|---|
| Secret in CI? | Yes — client ID and secret stored in secrets/variables | No — only organisation ID and service account ID (not secrets) |
| Token lifetime | Long-lived until revoked | Short-lived (default 1 hour, configurable) |
| Rotation | Manual — create new credentials, update CI, revoke old | Automatic — each job gets a fresh ID token |
| Blast radius if leaked | High — works from anywhere until revoked | Limited — only jobs matching your claim rules can exchange |
| Setup complexity | Low | Higher — identity provider + federated identity + pipeline config |
| Auditability | Harder to tie API calls to a specific repo/branch | Claim rules tie access to repository, branch, or environment |
| Recommended for | Local dev, quick tests, legacy integrations | Production CI/CD (GitHub Actions, GitLab CI, etc.) |
For production pipelines, use OIDC federation. Use access credentials when you need a simple static secret for a system that cannot obtain OIDC ID tokens (for example, a long-running service outside CI).
See Service Accounts for creating and managing access credentials.
Three layers of access control
After a successful token exchange, three separate mechanisms control what the pipeline can do. Confusing these is a common source of “token exchange worked but API call failed” issues.
| Layer | Configured on | Controls |
|---|---|---|
| 1. Token matching | Federated identity | Who is allowed to exchange a token (subject claim, additional claims, audience) |
| 2. API scopes | Federated identity (and optionally at exchange time) | What the bearer token itself is allowed to do (api:read, api:write, containerRegistry:pull, etc.) |
| 3. IAM roles | Service account | What the service account can do in Thalassa Cloud (RBAC roles, IAM policy bindings) |
All three must allow an action for it to succeed:
- The CI job’s ID token must match the federated identity rules.
- The returned bearer token must include the required API scope.
- The service account must have the IAM permission for the resource and action.
API scopes can narrow access below what the service account’s IAM roles allow, but they cannot grant permissions the service account does not have.
Glossary
| Term | Meaning |
|---|---|
| OIDC | OpenID Connect — a standard for identity tokens (JWTs) issued by an identity provider |
| JWT | JSON Web Token — a signed, base64-encoded token with three parts: header, payload, signature |
| ID token | The JWT your CI platform issues to the job. This is what you send to /oidc/token |
| Bearer token | The Thalassa Cloud access token returned after a successful exchange. Use this for API calls |
Issuer (iss) | The OIDC provider that signed the token (e.g. https://token.actions.githubusercontent.com). Must match the identity provider configuration exactly |
Subject (sub) | Primary claim identifying the caller. Format varies by provider — this is what federated identity matching uses |
Audience (aud) | Who the ID token is intended for. Must match Trusted Audiences on the federated identity and what you configure in your CI pipeline |
| JWKS | JSON Web Key Set — public keys used to verify the ID token signature. Fetched automatically from the issuer’s well-known configuration |
| Token exchange | The process of trading an external OIDC ID token for a Thalassa Cloud bearer token via POST /oidc/token |
| Impersonation | Acting as a service account using a bearer token obtained through federation |
Decode your ID token
When claim matching fails, decode the ID token to see what the CI platform actually issued:
# Print the JWT payload (middle segment) as JSON
echo "${OIDC_TOKEN}" | cut -d. -f2 | base64 -d 2>/dev/null | jqOn macOS, if base64 -d fails, use base64 -D instead.
Compare the output to your federated identity configuration:
| JWT field | Federated identity setting |
|---|---|
iss | Identity provider issuer URL |
aud | Trusted Audiences |
sub | Subject claim |
Other fields (repository, ref, environment, etc.) | Additional claims |
Example: GitHub Actions token
A token from a push to main on myorg/myrepo might contain:
{
"iss": "https://token.actions.githubusercontent.com",
"sub": "repo:myorg/myrepo:ref:refs/heads/main",
"aud": "https://api.thalassa.cloud",
"repository": "myorg/myrepo",
"repository_owner": "myorg",
"ref": "refs/heads/main"
}A matching federated identity would use:
- Trusted Audiences:
https://api.thalassa.cloud - Subject claim:
repo:myorg/myrepo:ref:refs/heads/main
And the GitHub workflow must request the token with the same audience (see GitHub Actions and GitLab CI guides).
Project context
Many Thalassa Cloud resources are scoped to a project. When calling project-scoped APIs, set the active project using one of:
X-Project-Identityheader- OIDC
projectclaim on the bearer token (if configured) ?project=query parameter
Without a project context, only organisation-scoped resources and organisation-level permissions apply. See IAM policy concepts for how project context affects permission checks.
Related documentation
- Create Identity Provider — Configure trust in an external OIDC issuer
- Create Federated Identity — Link a provider to a service account with claim rules
- Troubleshooting — Diagnose token exchange and permission failures
- Service Accounts — Create service accounts and access credentials
- RBAC roles — Assign permissions to service accounts