From 746e7a226d2ea938a349daeb73a64db1b46bfbd2 Mon Sep 17 00:00:00 2001 From: Roberto D'Auria Date: Mon, 2 Feb 2026 17:51:26 +0100 Subject: [PATCH 1/6] docs: update README with API endpoints, configuration, and architecture --- README.md | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/README.md b/README.md index 9cbe6f9..9d878b2 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,94 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/m-lab/token-exchange.svg)](https://pkg.go.dev/github.com/m-lab/token-exchange) A Cloud Run service that exchanges API keys for signed JWTs for M-Lab services. + +## API Endpoints + +| Method | Path | Description | +|--------|------|-------------| +| `POST` | `/v0/token/autojoin` | Exchange autojoin API key for JWT token | +| `POST` | `/v0/token/integration` | Exchange client integration API key for JWT token | +| `GET` | `/.well-known/jwks.json` | Serve JSON Web Key Set for token verification | +| `GET` | `/health` | Health check endpoint | + +## Development + +### Build and Test + +```bash +# Build all packages +go build -v ./... + +# Run all tests with race detection +go test -race -v ./... + +# Run tests with coverage +go test -coverprofile=coverage.out -coverpkg=./... ./... +``` + +### Docker + +```bash +# Build Docker image +docker build -t token-exchange . +``` + +### Deploy to Cloud Run + +```bash +gcloud builds submit --config cloudbuild.yaml +``` + +## Configuration + +Configuration is via command-line flags. All flags can also be set via environment variables (e.g., `-port` becomes `PORT`). + +| Flag | Default | Description | +|------|---------|-------------| +| `-port` | `8080` | Port to listen on | +| `-private-key-path` | `/secrets/jwk-priv.json` | Path to JWK private key file | +| `-platform-ns` | `platform-credentials` | Datastore namespace for autojoin credentials | +| `-client-integration-ns` | `client-integration` | Datastore namespace for client integration credentials | +| `-project-id` | `mlab-sandbox` | Google Cloud project ID | + +## CLI Tools + +### create-integration + +Creates a new client integration and API key in Datastore. + +```bash +go run ./cmd/create-integration -project=mlab-sandbox -integration-id=my-integration +``` + +Options: +- `-project` (required): Google Cloud project ID +- `-integration-id` (required): Integration ID +- `-namespace`: Datastore namespace (default: `client-integration`) +- `-key-id`: Key ID (auto-generated if not provided) +- `-integration-description`: Human-readable description for the integration +- `-key-description`: Human-readable description for the API key +- `-key-tier`: Service tier for the API key (default: 0) + +## Architecture + +The service validates API keys stored in Google Cloud Datastore and returns signed JWT tokens. Tokens are signed using RS256 with keys loaded from a JWK file. + +### Token Flow + +```mermaid +sequenceDiagram + participant Client + participant TokenExchange + participant Datastore + + Client->>TokenExchange: POST /v0/token/autojoin (API key) + TokenExchange->>Datastore: Validate API key + Datastore-->>TokenExchange: Organization/Integration info + TokenExchange->>TokenExchange: Generate JWT (1-hour expiry) + TokenExchange-->>Client: Signed JWT token + + Note over Client: Later, to verify tokens + Client->>TokenExchange: GET /.well-known/jwks.json + TokenExchange-->>Client: Public keys (JWKS) +``` From 2afc65b25ba06bc1ff9257887b1c5e3b32801c6f Mon Sep 17 00:00:00 2001 From: Roberto D'Auria Date: Mon, 2 Feb 2026 17:53:08 +0100 Subject: [PATCH 2/6] docs: update token flow diagram to show integration endpoint --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9d878b2..65a3d84 100644 --- a/README.md +++ b/README.md @@ -87,9 +87,9 @@ sequenceDiagram participant TokenExchange participant Datastore - Client->>TokenExchange: POST /v0/token/autojoin (API key) + Client->>TokenExchange: POST /v0/token/integration (API key) TokenExchange->>Datastore: Validate API key - Datastore-->>TokenExchange: Organization/Integration info + Datastore-->>TokenExchange: Integration info TokenExchange->>TokenExchange: Generate JWT (1-hour expiry) TokenExchange-->>Client: Signed JWT token From 3a2c6a069ee1e48303dc88bcc5f1771076690b9a Mon Sep 17 00:00:00 2001 From: Roberto D'Auria Date: Mon, 2 Feb 2026 17:54:41 +0100 Subject: [PATCH 3/6] docs: fix integration token expiry to 20 seconds --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 65a3d84..db38880 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ sequenceDiagram Client->>TokenExchange: POST /v0/token/integration (API key) TokenExchange->>Datastore: Validate API key Datastore-->>TokenExchange: Integration info - TokenExchange->>TokenExchange: Generate JWT (1-hour expiry) + TokenExchange->>TokenExchange: Generate JWT (20-second expiry) TokenExchange-->>Client: Signed JWT token Note over Client: Later, to verify tokens From 91c0e073bbb346a1f1a4d995e6ad6097166df1ae Mon Sep 17 00:00:00 2001 From: Roberto D'Auria Date: Mon, 2 Feb 2026 17:55:27 +0100 Subject: [PATCH 4/6] docs: simplify token flow diagram --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index db38880..1e981fd 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,4 @@ sequenceDiagram Datastore-->>TokenExchange: Integration info TokenExchange->>TokenExchange: Generate JWT (20-second expiry) TokenExchange-->>Client: Signed JWT token - - Note over Client: Later, to verify tokens - Client->>TokenExchange: GET /.well-known/jwks.json - TokenExchange-->>Client: Public keys (JWKS) ``` From d8b14ab613ab6440410882a2645fdd1faf4b4980 Mon Sep 17 00:00:00 2001 From: Roberto D'Auria Date: Mon, 2 Feb 2026 17:58:06 +0100 Subject: [PATCH 5/6] docs: add integration endpoint usage with request/response examples --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/README.md b/README.md index 1e981fd..40551b0 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,51 @@ Options: - `-key-description`: Human-readable description for the API key - `-key-tier`: Service tier for the API key (default: 0) +## Usage + +### Integration Token Exchange + +Exchange an API key for a short-lived JWT token. + +**Request:** + +```bash +curl -X POST https://your-service-url/v0/token/integration \ + -H "Content-Type: application/json" \ + -d '{"api_key": "your-api-key"}' +``` + +**Response:** + +```json +{ + "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ii..." +} +``` + +**JWT Claims:** + +| Claim | Description | +|-------|-------------| +| `int_id` | Integration ID | +| `key_id` | API Key ID | +| `tier` | Service tier | +| `aud` | Audience (`integration`) | +| `exp` | Expiration time (20 seconds from issue) | +| `iat` | Issued at time | +| `iss` | Issuer | +| `jti` | Unique token ID | +| `nbf` | Not valid before time | + +**Error Responses:** + +| Status | Description | +|--------|-------------| +| `400 Bad Request` | Invalid request body | +| `401 Unauthorized` | Invalid API key | +| `405 Method Not Allowed` | Request method is not POST | +| `500 Internal Server Error` | Failed to generate token | + ## Architecture The service validates API keys stored in Google Cloud Datastore and returns signed JWT tokens. Tokens are signed using RS256 with keys loaded from a JWK file. From e97b248de52ae513550c6631985bab9771b21aba Mon Sep 17 00:00:00 2001 From: Roberto D'Auria Date: Mon, 2 Feb 2026 21:38:12 +0100 Subject: [PATCH 6/6] docs: address review feedback --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 40551b0..19a0f19 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ gcloud builds submit --config cloudbuild.yaml ## Configuration -Configuration is via command-line flags. All flags can also be set via environment variables (e.g., `-port` becomes `PORT`). +Configuration is via command-line flags. All flags can also be set via environment variables (e.g., `-port` becomes `PORT`, `-platform-ns` becomes `PLATFORM_NS`). | Flag | Default | Description | |------|---------|-------------| @@ -66,14 +66,15 @@ Creates a new client integration and API key in Datastore. go run ./cmd/create-integration -project=mlab-sandbox -integration-id=my-integration ``` -Options: -- `-project` (required): Google Cloud project ID -- `-integration-id` (required): Integration ID -- `-namespace`: Datastore namespace (default: `client-integration`) -- `-key-id`: Key ID (auto-generated if not provided) -- `-integration-description`: Human-readable description for the integration -- `-key-description`: Human-readable description for the API key -- `-key-tier`: Service tier for the API key (default: 0) +| Flag | Default | Description | +|------|---------|-------------| +| `-project` | (required) | Google Cloud project ID | +| `-integration-id` | (required) | Integration ID | +| `-namespace` | `client-integration` | Datastore namespace | +| `-key-id` | (auto-generated) | Key ID | +| `-integration-description` | | Human-readable description for the integration | +| `-key-description` | | Human-readable description for the API key | +| `-key-tier` | `0` | Service tier for the API key | ## Usage