Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
55942d8
feat(auth): widen header contract for non-Bearer providers (phase 2 p…
May 23, 2026
9a1ebae
feat(auth/aws_sigv4): add provider verifying via STS reflection (phas…
May 23, 2026
5b71071
feat(auth/gcp_iap): add Identity-Aware Proxy provider (phase 2 pr 3)
May 23, 2026
1e23140
feat(auth/azure_ad): add Entra ID provider composing OIDC (phase 2 pr 4)
May 23, 2026
47c4748
feat(cli/ui): expose Phase 2 providers via flags + Web UI + validate …
May 23, 2026
98578f9
docs(auth): operator docs for Phase 2 providers + CHANGELOG (phase 2 …
May 23, 2026
639bfa9
fix(cli/tui): run Auth step before Egress; auto-add auth hosts to all…
May 24, 2026
b5f303b
fix(auth): audit-pass refinements — iap_jwt token_kind, parse caching…
May 24, 2026
aaf8375
chore(docs): gitignore docs/auth and drop in-repo copies
May 24, 2026
382294e
docs(auth/aws_sigv4): ship reference client + document sign-for-STS c…
May 24, 2026
b3444c2
refactor(auth/aws_sigv4): switch to pre-signed URL pattern (aws-iam-a…
May 24, 2026
8568535
fix(auth/aws_sigv4): preserve raw presigned URL; use SigV4QueryAuth i…
May 24, 2026
24be30a
feat(auth): TUI sub-flows + allowed_accounts shortcut for aws_sigv4
May 24, 2026
3364ca8
fix(cli/init): quote all-digit YAML scalars to preserve string type
May 24, 2026
774268d
fix(auth): pin CheckRedirect on Phase 2 outbound clients (B1+B2+B3)
May 24, 2026
674bf2f
fix(auth/aws_sigv4): reject token URLs with userinfo (M1)
May 24, 2026
c6e0aff
fix(auth/aws_sigv4): parser-side token freshness (M2)
May 24, 2026
57d12a5
fix(auth): refine token_kind post-verify when gcp_iap matches (M4)
May 24, 2026
419f20f
feat(auth/azure_ad): allowed_tenants gate + fix misleading multi-tena…
May 24, 2026
ca92921
fix(auth): closed-key whitelist for provider settings (M5)
May 24, 2026
745c024
chore(auth): clear seven Phase 2 review NITs
May 24, 2026
4dc8ce0
docs: sync Phase 2 auth provider work across affected pages
May 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 24 additions & 19 deletions .claude/commands/sync-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,33 @@ After feature work, update the affected documentation to reflect code changes.

1. **Identify changed files** — Run `git diff main --name-only` to find modified Go files.

2. **Map files to docs** — Use this mapping to determine which docs need updates:
2. **Map files to docs** — Use this mapping to determine which docs need updates.
Paths match the actual `docs/` tree (nested under `core-concepts/`, `reference/`,
`security/`, `deployment/`, `skills/`).

| Changed path pattern | Affected docs |
|---------------------|---------------|
| `forge-core/runtime/` | `docs/runtime.md`, `docs/hooks.md` |
| `forge-core/security/` | `docs/security/overview.md`, `docs/security/egress.md` |
| `forge-core/tools/` | `docs/tools.md` |
| `forge-core/llm/` | `docs/runtime.md` |
| `forge-core/memory/` | `docs/memory.md` |
| `forge-core/scheduler/` | `docs/scheduling.md` |
| `forge-core/secrets/` | `docs/security/secrets.md` |
| `forge-core/skills/` | `docs/skills.md` |
| `forge-core/channels/` | `docs/channels.md` |
| `forge-cli/cmd/` | `docs/commands.md` |
| `forge-cli/runtime/` | `docs/runtime.md` |
| `forge-cli/server/` | `docs/architecture.md` |
| `forge-cli/channels/` | `docs/channels.md` |
| `forge-cli/tools/` | `docs/tools.md` |
| `forge-plugins/` | `docs/channels.md`, `docs/plugins.md` |
| `forge-ui/` | `docs/dashboard.md` |
| `forge-skills/` | `docs/skills.md` |
| `forge.yaml` / `types/` | `docs/configuration.md` |
| `forge-core/auth/` | `docs/security/authentication.md`, `docs/security/audit-logging.md` |
| `forge-core/runtime/` | `docs/core-concepts/runtime-engine.md`, `docs/core-concepts/hooks.md` |
| `forge-core/security/` | `docs/security/overview.md`, `docs/security/egress-control.md` |
| `forge-core/tools/` | `docs/core-concepts/tools-and-builtins.md` |
| `forge-core/llm/` | `docs/core-concepts/runtime-engine.md` |
| `forge-core/memory/` | `docs/core-concepts/memory-system.md` |
| `forge-core/scheduler/` | `docs/core-concepts/scheduling.md` |
| `forge-core/secrets/` | `docs/security/secret-management.md` |
| `forge-core/channels/` | `docs/core-concepts/channels.md` |
| `forge-core/validate/` | `docs/reference/forge-yaml-schema.md` |
| `forge-cli/cmd/` | `docs/reference/cli-reference.md` |
| `forge-cli/runtime/` | `docs/core-concepts/runtime-engine.md` |
| `forge-cli/server/` | `docs/core-concepts/how-forge-works.md` |
| `forge-cli/channels/` | `docs/core-concepts/channels.md` |
| `forge-cli/tools/` | `docs/core-concepts/tools-and-builtins.md` |
| `forge-cli/internal/tui/` | `docs/reference/cli-reference.md` (wizard flow) |
| `forge-plugins/` | `docs/core-concepts/channels.md`, `docs/reference/framework-plugins.md` |
| `forge-ui/` | `docs/reference/web-dashboard.md` |
| `forge-skills/` | `docs/skills/writing-custom-skills.md`, `docs/skills/contributing-a-skill.md` |
| `forge-core/types/` / `forge.yaml` | `docs/reference/forge-yaml-schema.md` |
| `CHANGELOG.md` | (rendered into release notes; no per-doc sync needed) |

3. **Read the diff** — For each mapped doc, read the relevant `git diff main` output to understand what changed.

Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,8 @@ profile.cov

# OS files
.DS_Store

# Auth provider docs — kept on disk locally but not version-controlled.
# Source-of-truth docs live in the design folder; in-repo copies are
# scratch space until we decide on the doc-site delivery story.
docs/auth/
125 changes: 125 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Changelog

## v0.11.0 — Phase 2: cloud-native auth providers (in progress)

### Added

- **`aws_sigv4` auth provider.** Authenticate AWS-IAM callers by reflecting
their Sigv4 signature to AWS STS `GetCallerIdentity`. No `aws-sdk-go-v2`
dependency.
- **`gcp_iap` auth provider.** Verify the JWT IAP forwards as
`X-Goog-Iap-Jwt-Assertion` when Forge sits behind a GCP HTTPS Load
Balancer with IAP enabled.
- **`azure_ad` auth provider.** Verify Microsoft Entra ID Bearer tokens
with tenant lock-in and optional Microsoft Graph group enrichment.
- Non-interactive `forge init` flags for the three new providers:
`--auth-aws-region`, `--auth-aws-allowed-principal` (repeatable),
`--auth-gcp-iap-audience`, `--auth-azure-tenant`,
`--auth-azure-multi-tenant`, `--auth-azure-groups-mode`.
- Web UI exposes the three new types via the `/api/wizard-meta` endpoint;
server-side validation rejects malformed payloads before scaffold.
- `egress_hosts` automatically extended for each new provider
(`sts.<region>.amazonaws.com`, `www.gstatic.com`,
`login.microsoftonline.com`, `graph.microsoft.com` when applicable).

### Changed

- Middleware now consults the auth chain **even when no Bearer token is
extracted**, so non-Bearer formats (Sigv4 `Authorization`, IAP
`X-Goog-Iap-Jwt-Assertion`) can be recognized. Existing Bearer + JWT
flows are unchanged.
- `auth.HeadersFromRequest` widened with `X-Goog-Iap-Jwt-Assertion`
for `gcp_iap`. Providers that don't consume this header are unaffected.
- `auth.TokenKind` recognizes the `forge-aws-v1.` Bearer prefix and
returns `"sigv4"`. The audit `token_kind` field now has five possible
values: `empty`, `opaque`, `jwt`, `sigv4`, `iap_jwt`.
- `validate.ValidateAuthConfig` admits the three new provider types and
enforces their per-type required keys (`aws_sigv4.region`,
`gcp_iap.audience`, `azure_ad.audience`, `azure_ad.tenant_id`-unless-
multi-tenant, `azure_ad.groups_mode` whitelist).

### Notes for upgraders

- **No forge.yaml changes are required** for callers continuing to use
Phase 1 providers (`static_token`, `oidc`, `http_verifier`). Phase 1
test suite passes without modification.
- If you wrote a custom provider that inspects headers, the `Headers`
map now contains additional keys. Existing keys are unchanged.
- The `oidc` package gained an internal `SkipIssuerCheck` field carrying
`yaml:"-"` — it cannot be set via `forge.yaml` and is reachable only
from Go callers (currently only `azure_ad` multi-tenant). Operators see
no change.

### `allowed_accounts` shortcut for whole-account trust

For "any IAM principal in these AWS accounts" without writing
glob patterns:

```yaml
auth:
providers:
- type: aws_sigv4
settings:
region: us-east-1
allowed_accounts: ["412664885516", "109887654321"]
```

Internally expands to the canonical glob set covering all identity
shapes (IAM users, IAM roles, STS assumed-roles, federated users)
for each account. Composes with `allowed_principals` — you can list
specific roles AND whole accounts in the same provider entry.

For AWS-Org-wide trust without enumerating accounts, use AWS IAM
Identity Center (SSO) — SSO permission sets gate Org membership at
sign-in, and you can match Identity Center-assumed roles with the
existing `allowed_principals` globs.

### `azure_ad.allowed_tenants` — explicit allowlist for multi-tenant mode

```yaml
auth:
providers:
- type: azure_ad
settings:
audience: api://forge
allow_multi_tenant: true
allowed_tenants:
- "00000000-1111-2222-3333-444444444444" # partner A
- "55555555-6666-7777-8888-999999999999" # partner B
```

When `allow_multi_tenant: true`, the `tid` claim must be in
`allowed_tenants` (case-insensitive GUID match). Empty list +
multi-tenant remains the documented "any tenant globally" mode for
back-compat, but `forge validate` now emits a warning when the list
is empty to make the trade-off explicit. Non-interactive flag:
`--auth-azure-allowed-tenant` (repeatable).

### TUI wizard supports Phase 2 providers

`forge init`'s TUI picker now includes `AWS Sigv4 (IAM)`,
`GCP Identity-Aware Proxy`, and `Azure AD / Entra ID` entries with
step-by-step input flows. AAD is single-tenant in the TUI;
multi-tenant remains a deliberate YAML edit (security default).

### Client experience for `aws_sigv4`

The client side is a Bearer token with a 3-line mint:

```python
import boto3, base64
url = boto3.client('sts', region_name='us-east-1').generate_presigned_url(
'get_caller_identity', ExpiresIn=900)
token = 'forge-aws-v1.' + base64.urlsafe_b64encode(url.encode()).rstrip(b'=').decode()

requests.post(forge_url, headers={'Authorization': f'Bearer {token}'}, data=msg)
```

Pattern is identical to `aws-iam-authenticator` for EKS. Reference client
in `scripts/forge-aws-sign.py` — use it directly or as a template for
Go / Java / Node clients. Wire format is documented in the package
docstring of `forge-core/auth/providers/aws_sigv4/provider.go`.

### Known deferred work

- (none for Phase 2)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ You write a `SKILL.md`. Forge compiles it into a secure, runnable agent with egr
| Document | Description |
|----------|-------------|
| [Security Overview](docs/security/overview.md) | Complete security architecture |
| [Authentication](docs/security/authentication.md) | Pluggable auth providers — OIDC, AWS Sigv4, GCP IAP, Azure AD |
| [Egress Security](docs/security/egress-control.md) | Egress enforcement deep dive |
| [Secrets](docs/security/secret-management.md) | Encrypted secret management |
| [Build Signing](docs/security/build-signing.md) | Ed25519 signing and verification |
Expand Down
37 changes: 37 additions & 0 deletions docs/reference/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ forge init [name] [flags]
| `--org-id` | | | OpenAI Organization ID (enterprise) |
| `--from-skills` | | | Path to a SKILL.md file for auto-configuration |
| `--non-interactive` | | `false` | Skip interactive prompts |
| `--auth` | | | Auth mode: `none`, `oidc`, `http_verifier`, `aws_sigv4`, `gcp_iap`, `azure_ad`, `custom` |
| `--auth-issuer` | | | OIDC issuer URL (required with `--auth=oidc`) |
| `--auth-audience` | | | OIDC audience (required with `--auth=oidc`) |
| `--auth-url` | | | Verifier URL (required with `--auth=http_verifier`) |
| `--auth-default-org` | | | Default `org_id` for http_verifier |
| `--auth-groups-claim` | | | Custom JWT claim name for groups (oidc, default `groups`) |
| `--auth-aws-region` | | | AWS region for `aws_sigv4` (e.g. `us-east-1`) |
| `--auth-aws-audience` | | | Informational audience for `aws_sigv4` |
| `--auth-aws-allowed-principal` | | | Allowed principal glob for `aws_sigv4` (repeatable) |
| `--auth-aws-allowed-account` | | | Allowed AWS account ID for `aws_sigv4` (repeatable, 12-digit) |
| `--auth-aws-cache-ttl` | | `60s` | `aws_sigv4` identity cache TTL |
| `--auth-gcp-iap-audience` | | | Backend service ID for `gcp_iap` |
| `--auth-azure-tenant` | | | Entra tenant GUID for `azure_ad` |
| `--auth-azure-audience` | | | Audience (Application ID URI) for `azure_ad` |
| `--auth-azure-multi-tenant` | | `false` | Accept tokens from any Entra tenant |
| `--auth-azure-allowed-tenant` | | | Allowed Entra tenant GUID for multi-tenant `azure_ad` (repeatable) |
| `--auth-azure-groups-mode` | | `claim` | `azure_ad` groups mode: `claim` or `graph` |

### Generated Files

Expand Down Expand Up @@ -84,8 +101,28 @@ forge init my-agent \
--api-key sk-... \
--org-id org-xxxxxxxxxxxxxxxxxxxxxxxx \
--non-interactive

# AWS IAM auth (any caller in account 412664885516)
forge init my-agent \
--model-provider ollama \
--auth=aws_sigv4 \
--auth-aws-region=us-east-1 \
--auth-aws-allowed-account=412664885516 \
--non-interactive

# Azure AD multi-tenant with explicit partner allowlist
forge init my-agent \
--model-provider ollama \
--auth=azure_ad \
--auth-azure-audience=api://forge \
--auth-azure-multi-tenant \
--auth-azure-allowed-tenant=00000000-1111-... \
--auth-azure-allowed-tenant=55555555-6666-... \
--non-interactive
```

See [Authentication](../security/authentication.md) for the full auth provider reference.

---

## `forge build`
Expand Down
45 changes: 45 additions & 0 deletions docs/reference/forge-yaml-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,51 @@ package:
dest: "/usr/local/bin/custom-tool" # Install destination
chmod: "0755" # File permissions

auth: # a2a HTTP-server auth chain (optional)
required: true # 401 every unauthenticated request
providers: # ordered; first match wins (fail-closed on rejection)
- type: "static_token" # local dev / shared-secret
settings:
token_env: "FORGE_AUTH_TOKEN" # env var name (preferred over literal `token:`)
- type: "oidc" # any IdP with OIDC discovery
settings:
issuer: "https://login.example.com/auth/realms/forge"
audience: "api://forge"
client_id: "" # optional azp fallback
jwks_url: "" # overrides discovery
jwks_cache_ttl: "1h"
clock_skew: "30s"
claim_map: {groups: "roles"}
- type: "http_verifier" # legacy external /verify endpoint
settings:
url: "https://auth.example.com/verify"
default_org: "acme"
timeout: "10s"
- type: "aws_sigv4" # Phase 2: AWS IAM via pre-signed STS URL
settings:
region: "us-east-1" # required
audience: "api://forge" # informational, emitted in audit Claims
allowed_accounts: # ergonomic: "anyone in these AWS accounts"
- "412664885516"
allowed_principals: # explicit globs (path.Match)
- "arn:aws:sts::412664885516:assumed-role/ci-deploy/*"
identity_cache_ttl: "60s"
max_token_expires: "15m" # caps caller's X-Amz-Expires claim
clock_skew: "5m"
- type: "gcp_iap" # Phase 2: GCP IAP-fronted Forge
settings:
audience: "/projects/PNUM/global/backendServices/BACKEND_ID"
jwks_refresh_ttl: "1h"
- type: "azure_ad" # Phase 2: Microsoft Entra ID
settings:
tenant_id: "00000000-1111-..." # required unless allow_multi_tenant
audience: "api://forge"
allow_multi_tenant: false
allowed_tenants: # required when multi-tenant + want allowlist
- "55555555-6666-..."
groups_mode: "claim" # "claim" | "graph"
graph_timeout: "5s"

secrets:
providers: # Secret providers (order matters)
- "encrypted-file" # AES-256-GCM encrypted file
Expand Down
31 changes: 31 additions & 0 deletions docs/reference/web-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,42 @@ A multi-step wizard (web equivalent of `forge init`) that walks through the full
| Tools | Select builtin tools; web_search shows Tavily vs Perplexity provider choice with API key input |
| Skills | Browse registry skills by category with inline required/optional env var collection |
| Fallback | Select backup LLM providers with API keys for automatic failover |
| Auth | Select a2a auth provider (see below) |
| Egress | Review the outbound allowlist (including auth-provider-derived hosts) |
| Env & Security | Add extra env vars; set passphrase for AES-256-GCM secret encryption |
| Review | Summary of all selections before creation |

The wizard collects credentials inline at each step (matching the CLI TUI behavior) and supports all the same options: model selection, OAuth, web search providers, fallback chains, and encrypted secret storage.

### Auth step

The Auth step exposes the same provider chain as `forge.yaml`'s
`auth.providers[]` block. Picker options:

| Option | Effect |
|---|---|
| **None** | Anonymous access — no `auth:` block written |
| **OIDC (JWT)** | Generic OIDC IdP (Keycloak, Auth0, Okta, Google) — collects issuer + audience |
| **HTTP Verifier** | Legacy — POST tokens to your own `/verify` endpoint |
| **AWS Sigv4 (IAM)** | AWS-IAM-based callers — collects region + optional audience + comma-separated allowed accounts |
| **GCP Identity-Aware Proxy** | Forge behind GCP LB+IAP — collects backend service ID |
| **Azure AD / Entra ID** | Microsoft Entra ID (single-tenant in the wizard; multi-tenant requires editing `forge.yaml` as a deliberate security trade-off) |
| **Custom** | Comment stub — edit `forge.yaml` yourself |

The Auth step runs **before** Egress, so the Egress step's review screen
displays auth-provider-derived hosts (e.g. `sts.us-east-1.amazonaws.com`
when AWS Sigv4 is selected) alongside the model / channel / tool / skill
hosts. The operator sees the full outbound surface for approval in one
place.

The Web UI's `POST /api/create` endpoint additionally filters incoming
auth `settings` through a closed-key whitelist (defined in
`forge-core/validate/auth.go`) before scaffolding — unknown keys are
silently dropped rather than written to disk.

See [Authentication](../security/authentication.md) for the full
provider reference.

## Config Editor

Edit `forge.yaml` for any agent with a Monaco-based YAML editor:
Expand Down
Loading
Loading