API Authentication

All Teabar API requests require authentication via OIDC access tokens from Keycloak. This guide covers the available authentication methods and best practices.

Authentication Methods

MethodUse CaseToken Lifetime
Interactive LoginCLI, local developmentConfigured in Keycloak (default 15min, auto-refresh)
Service AccountsCI/CD, automation, scriptsConfigured per client in Keycloak

Using Access Tokens

Include the Keycloak access token in the Authorization header:

curl -H "Authorization: Bearer <keycloak-access-token>" 
  https://api.teabar.dev/teabar.v1.EnvironmentService/ListEnvironments

Interactive Authentication

For CLI and interactive use, teactl handles authentication via the OIDC device flow:

teactl auth login

The CLI automatically:

  • Stores tokens securely in system keychain
  • Refreshes tokens before expiry
  • Includes tokens in API requests

Service Account Authentication

For CI/CD and automation, use Keycloak service accounts with the client credentials flow.

Getting a Token

# Request token from Keycloak
TOKEN=$(curl -s -X POST 
  "https://auth.bcp.technology/realms/teabar/protocol/openid-connect/token" 
  -d "client_id=my-service-account" 
  -d "client_secret=$CLIENT_SECRET" 
  -d "grant_type=client_credentials" | jq -r '.access_token')

# Use with teactl
export TEABAR_TOKEN="$TOKEN"
teactl env list

# Or use directly with API
curl -H "Authorization: Bearer $TOKEN" 
  https://api.teabar.dev/teabar.v1.EnvironmentService/ListEnvironments

Device Authorization Flow

For CLI and devices without browsers (used by teactl auth login):

# Step 1: Request device code
curl -X POST "https://auth.bcp.technology/realms/teabar/protocol/openid-connect/auth/device" 
  -d "client_id=teabar-cli"

# Response
{
  "device_code": "ABC123...",
  "user_code": "ABCD-1234",
  "verification_uri": "https://auth.bcp.technology/realms/teabar/device",
  "verification_uri_complete": "https://auth.bcp.technology/realms/teabar/device?user_code=ABCD-1234",
  "expires_in": 600,
  "interval": 5
}

User visits the URL and enters the code, then poll for the token:

# Poll for token
curl -X POST "https://auth.bcp.technology/realms/teabar/protocol/openid-connect/token" 
  -d "client_id=teabar-cli" 
  -d "grant_type=urn:ietf:params:oauth:grant-type:device_code" 
  -d "device_code=ABC123..."

Refreshing Tokens

Access tokens are short-lived. Use refresh tokens to obtain new access tokens:

curl -X POST "https://auth.bcp.technology/realms/teabar/protocol/openid-connect/token" 
  -d "grant_type=refresh_token" 
  -d "refresh_token=$REFRESH_TOKEN" 
  -d "client_id=teabar-cli"

Role-Based Access

Permissions are controlled via Keycloak roles mapped to Teabar permissions:

Keycloak RoleTeabar Permission
teabar-adminFull access
teabar-env-managerCreate, manage, delete environments
teabar-blueprint-managerManage blueprints
teabar-viewerRead-only access

Roles are assigned to users and service accounts in Keycloak.

Token Validation

Teabar validates tokens against Keycloak’s JWKS endpoint. Validate a token programmatically:

curl -H "Authorization: Bearer $TOKEN" 
  https://api.teabar.dev/teabar.v1.AuthService/GetCurrentUser

Response:

{
  "user": {
    "id": "user_abc123",
    "email": "[email protected]",
    "name": "John Doe"
  },
  "organization": {
    "id": "org_xyz789",
    "slug": "my-company"
  }
}

Best Practices

Token Security

  1. Never expose tokens in code - Use environment variables
  2. Use secrets management - HashiCorp Vault, AWS Secrets Manager
  3. Limit service account roles - Request minimum required permissions
  4. Enable MFA in Keycloak - For interactive users

Token Storage

EnvironmentRecommended Storage
CI/CDPipeline secrets/variables
ServerSecrets manager or env vars
DesktopOS keychain (teactl default)
Web ApphttpOnly cookies (server-side)

Service Account Best Practices

  1. Create dedicated service accounts per use case
  2. Rotate client secrets periodically
  3. Assign minimal roles needed
  4. Audit usage via Keycloak logs

Error Responses

Invalid Token

{
  "code": "unauthenticated",
  "message": "Invalid or expired token"
}

HTTP Status: 401 Unauthorized

Insufficient Permissions

{
  "code": "permission_denied",
  "message": "User does not have required role"
}

HTTP Status: 403 Forbidden

Token Expired

{
  "code": "unauthenticated",
  "message": "Token has expired"
}

Solution: Refresh the token or re-authenticate.

Code Examples

TypeScript

import { createClient } from "@connectrpc/connect";
import { createConnectTransport } from "@connectrpc/connect-web";
import { EnvironmentService } from "./gen/teabar/v1/environment_connect";

const transport = createConnectTransport({
  baseUrl: "https://api.teabar.dev",
  interceptors: [
    (next) => async (req) => {
      req.header.set("Authorization", `Bearer ${process.env.TEABAR_TOKEN}`);
      return next(req);
    },
  ],
});

const client = createClient(EnvironmentService, transport);

Go

import (
    "context"
    "google.golang.org/grpc"
    "google.golang.org/grpc/metadata"
)

// Add token to context
ctx := metadata.AppendToOutgoingContext(
    context.Background(),
    "authorization", "Bearer "+token,
)

// Make request with authenticated context
resp, err := client.ListEnvironments(ctx, &ListEnvironmentsRequest{})

Python

import grpc

# Create authenticated channel
credentials = grpc.access_token_call_credentials(token)
channel = grpc.secure_channel(
    'api.teabar.dev:443',
    grpc.composite_channel_credentials(
        grpc.ssl_channel_credentials(),
        credentials
    )
)

Self-Hosted Configuration

For self-hosted deployments with your own OIDC provider:

# Helm values.yaml
auth:
  oidc:
    issuerURL: "https://your-idp.example.com/realms/teabar"
    webClientID: "teabar-web"
    webClientSecret: "${OIDC_CLIENT_SECRET}"
    cliClientID: "teabar-cli"
    audience: "teabar-web"

See Also

ende