OIDC Issuer — CipherVault como IdP
A partir da v4.6, o CipherVault não é apenas consumer de OIDC (GitHub Actions, GitLab, etc.) — também atua como OIDC Identity Provider, expondo discovery e JWKS pra que clouds (AWS, GCP, Azure) federe workloads contra ele.
Paridade real com Pulumi ESC.
Discovery
curl https://cv.acme.com.br/.well-known/openid-configuration
{
"issuer": "https://cv.acme.com.br",
"jwks_uri": "https://cv.acme.com.br/.well-known/jwks.json",
"token_endpoint": "https://cv.acme.com.br/oidc/token",
"response_types_supported": ["id_token"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["ES256"]
}
JWKS
EC P-256 ES256 keys públicas pra verificação de assinatura:
curl https://cv.acme.com.br/.well-known/jwks.json
{
"keys": [
{ "kid": "cv-2026-05", "kty": "EC", "crv": "P-256", "x": "...", "y": "...", "use": "sig", "alg": "ES256" }
]
}
Token mint
App autenticado no CV troca seu bearer por JWT assinado:
curl -X POST https://cv.acme.com.br/oidc/token \
-H "Authorization: Bearer $CV_TOKEN" \
-d '{
"audience": "sts.amazonaws.com",
"subject": "app1:production"
}'
# → { "token": "eyJ...", "expires_at": "...", "kid": "cv-2026-05" }
Claims emitidos:
| Claim | Conteúdo |
|---|---|
iss | URL do CV (https://cv.acme.com.br) |
aud | Audience requisitado (sts.amazonaws.com, etc.) |
sub | Subject identifier (tenant:app, configurável) |
iat, exp, nbf | Standard |
cv_tenant_id, cv_app_id | Custom claims para policy matching |
Rotação de chaves
POST /oidc/rotate gera novo keypair, mantém o anterior como inactive
até TTL de tokens emitidos expirar (default 24h):
curl -X POST https://cv.acme.com.br/oidc/rotate \
-H "Authorization: Bearer $CV_ADMIN_TOKEN"
# → { "new_kid": "cv-2026-06", "previous_kid": "cv-2026-05", "rotated_at": "..." }
JWKS pública continua expondo ambas até cleanup automático.
Caso de uso: AWS AssumeRoleWithWebIdentity
App no Kubernetes (ou EC2, ou Lambda) federa AWS sem static keys:
1. AWS-side — cria OIDC provider:
aws iam create-open-id-connect-provider \
--url https://cv.acme.com.br \
--client-id-list sts.amazonaws.com \
--thumbprint-list <(curl -s https://cv.acme.com.br/.well-known/openid-configuration | jq -r .jwks_uri | xargs -I {} curl -s {} | jq -r ...)
2. AWS-side — cria role com trust policy:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::123456789:oidc-provider/cv.acme.com.br" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cv.acme.com.br:aud": "sts.amazonaws.com",
"cv.acme.com.br:sub": "tenant42:checkout-svc"
}
}
}]
}
3. App-side — troca por AWS creds:
JWT=$(curl -s -X POST https://cv.acme.com.br/oidc/token \
-H "Authorization: Bearer $CV_TOKEN" \
-d '{"audience":"sts.amazonaws.com","subject":"tenant42:checkout-svc"}' | jq -r .token)
aws sts assume-role-with-web-identity \
--role-arn arn:aws:iam::123:role/checkout-svc-fed \
--role-session-name checkout1 \
--web-identity-token "$JWT"
# → { Credentials: { AccessKeyId, SecretAccessKey, SessionToken, Expiration } }
Zero static keys em qualquer ponto da chain.
UI
Settings → OIDC tab (v4.7) mostra:
- Discovery preview (mock fetch + render)
- JWKS keys table com
kid,created_at,status,expires_at - Rotate button com confirm dialog
- AWS IAM setup walkthrough copiável (gera CLI commands com tenant_id pré-preenchido)
Comparação vs static AWS keys
| Static keys (legacy) | OIDC federation | |
|---|---|---|
| Setup | IAM user + access key | OIDC provider + role |
| Storage | CV armazena key | CV nada (mint on-demand) |
| Rotation | Manual | Automática (JWT TTL 1h) |
| Audit | App→AWS opaco | OIDC token em audit CV |
| Compromise blast | Permanente até rotate | Expira em 1h |
Use sempre OIDC federation quando possível.
Limitações atuais
- Apenas EC P-256 ES256 — RSA não suportado (overhead JWKS desnecessário)
- Sem PKCE ainda (auth code flow não implementado — token endpoint usa CV bearer)
- Sem Dynamic Client Registration — relying parties (AWS/GCP/Azure) configurados estaticamente
Referências
backend/src/routes/oidc.jsno repo do produto- Pulumi ESC OIDC docs — comparação
- Blog post v4.6