Skip to content

Commit 0bdc120

Browse files
committed
fix(auth): complete OAuth integration and lifecycle guarantees
1 parent 8441f5e commit 0bdc120

117 files changed

Lines changed: 3728 additions & 1013 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test-build.yml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,80 @@ permissions:
88
contents: read
99

1010
jobs:
11+
oauth-postgres:
12+
name: OAuth PostgreSQL (${{ matrix.provision }})
13+
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-latest' }}
14+
timeout-minutes: 15
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
provision: [push, migrate]
19+
services:
20+
postgres:
21+
image: pgvector/pgvector:pg17
22+
env:
23+
POSTGRES_USER: postgres
24+
POSTGRES_PASSWORD: postgres
25+
POSTGRES_DB: sim_oauth
26+
ports:
27+
- 5432:5432
28+
options: >-
29+
--health-cmd "pg_isready -U postgres -d sim_oauth"
30+
--health-interval 5s
31+
--health-timeout 5s
32+
--health-retries 10
33+
env:
34+
DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/sim_oauth
35+
OAUTH_TOKEN_FAMILY_TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/sim_oauth
36+
BETTER_AUTH_SECRET: oauth-postgres-ci-secret-at-least-32-characters
37+
OAUTH_PROVIDER_ENABLED: 'true'
38+
NEXT_PUBLIC_APP_URL: https://test.sim.ai
39+
ENCRYPTION_KEY: '0000000000000000000000000000000000000000000000000000000000000000'
40+
41+
steps:
42+
- name: Checkout code
43+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
44+
45+
- name: Setup Bun
46+
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
47+
with:
48+
bun-version: 1.4.1
49+
50+
- name: Setup Node
51+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
52+
with:
53+
node-version: 24
54+
55+
- name: Mount Bun cache
56+
uses: ./.github/actions/cache-mount
57+
with:
58+
provider: ${{ vars.CI_PROVIDER }}
59+
key: ${{ github.repository }}-bun-cache-${{ github.event_name }}${{ github.event.pull_request.head.repo.fork && '-fork' || '' }}
60+
path: ~/.bun/install/cache
61+
62+
- name: Install dependencies
63+
run: bun install --frozen-lockfile --ignore-scripts
64+
65+
- name: Provision a fresh database through the supported command
66+
working-directory: packages/db
67+
run: |
68+
bun -e 'import postgres from "postgres"; const sql = postgres(process.env.DATABASE_URL); for (const extension of ["vector", "btree_gin", "pg_trgm"]) await sql`CREATE EXTENSION IF NOT EXISTS ${sql(extension)}`; await sql.end()'
69+
bun run db:${{ matrix.provision }}
70+
71+
- name: Verify migration replay is a no-op
72+
if: matrix.provision == 'migrate'
73+
working-directory: packages/db
74+
run: bun run db:migrate
75+
76+
- name: Verify provider issuance and token lifecycle in PostgreSQL
77+
working-directory: apps/sim
78+
run: >-
79+
bunx vitest run
80+
lib/auth/oauth-token-family.postgres.test.ts
81+
lib/auth/oauth-provider-lifecycle.postgres.test.ts
82+
app/api/auth/oauth2/token/route.postgres.test.ts
83+
lib/auth/sim-auth-adapter.test.ts
84+
1185
test-build:
1286
name: Lint and Test
1387
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-8vcpu-ubuntu-2404' || 'ubuntu-latest' }}

apps/docs/content/docs/api-reference/authentication.mdx

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
---
22
title: Authentication
3-
description: API key types, generation, and how to authenticate requests
3+
description: Authenticate with API keys or delegated OAuth access tokens
44
---
55

66
import { Callout } from 'fumadocs-ui/components/callout'
77
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
88

9-
To access the Sim API, you need an API key. Sim supports two types of API keys — **personal keys** and **workspace keys** — each with different billing and access behaviors.
9+
The Sim API accepts API keys and, when enabled by your deployment, OAuth access tokens. API keys support automation and the SDKs. OAuth lets the CLI and registered applications act on your behalf with permissions you approve.
10+
11+
Sim supports two types of API keys — **personal keys** and **workspace keys** — each with different billing and access behaviors.
1012

1113
## Key Types
1214

@@ -87,6 +89,27 @@ API keys authenticate access to:
8789
- **MCP servers** — authenticate connections to deployed MCP servers
8890
- **SDKs** — the [Python](/api-reference/python) and [TypeScript](/api-reference/typescript) SDKs use API keys for all operations
8991

92+
## OAuth access tokens
93+
94+
Use `sim login` to authorize the CLI in your browser, or `sim login --read-only` to request read access. The CLI stores the login locally and refreshes access tokens automatically. See [CLI authentication](/cli/authentication) for profiles, sign-in, and sign-out.
95+
96+
Registered OAuth applications send access tokens in the `Authorization` header:
97+
98+
```bash
99+
curl https://www.sim.ai/api/v2/workspaces \
100+
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
101+
```
102+
103+
| Scope | Access |
104+
| --- | --- |
105+
| `api:read` | Read operations, including searches sent as POST requests |
106+
| `api:write` | Mutations and execution, including operations that can start external work |
107+
| `offline_access` | Refresh tokens for continued access after the access token expires |
108+
109+
Scopes limit what an application may do; your current workspace membership and role still apply. Each endpoint documents its required scope. Some GET endpoints that perform external discovery require `api:write`, so HTTP method alone does not determine the permission.
110+
111+
Manage grants in **Settings****Authorized apps**. Revoking an application signs out all of its logins. `sim logout` revokes the current CLI login and removes it from your machine. The Python and TypeScript SDKs currently use API keys; they do not manage OAuth sign-in or refresh tokens.
112+
90113
## Security
91114

92115
- Keys use the `sk-sim-` prefix and are encrypted at rest

apps/docs/content/docs/api-reference/getting-started.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Download the [complete OpenAPI 3.1 specification](/openapi.json) as JSON for cli
2626
<Step>
2727
### Get your API key
2828

29-
Go to the Sim platform and navigate to **Settings**, then go to **Sim Keys** and click **Create**. See [Authentication](/api-reference/authentication) for details on key types.
29+
Open **Account settings****Sim API keys** to create a personal key, or **Workspace settings****Sim API keys** for a workspace key. These examples and the SDKs use API keys; the CLI also supports browser sign-in with `sim login`. See [Authentication](/api-reference/authentication) for key types and OAuth permissions.
3030
</Step>
3131

3232
<Step>

apps/docs/openapi-v2-billing.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
"get": {
4040
"operationId": "getBillingStatus",
4141
"summary": "Get Billing Status",
42-
"description": "Return the current plan, billing standing, credit allowance, and storage quota. `credits` and `storage` report the payer's pooled allowances and are null unless the caller can manage that payer's billing; they are always null for a workspace API key. Billing history lives at `GET /api/v2/billing/logs`.",
42+
"description": "Return the current plan, billing standing, credit allowance, and storage quota. `credits` and `storage` report the payer's pooled allowances and are null unless the caller can manage that payer's billing; they are always null for a workspace API key. Billing history lives at `GET /api/v2/billing/logs`.\n\nOAuth scope: `api:read`.",
43+
"x-sim-operation": "billing.status.read",
44+
"x-oauth-scope": "api:read",
4345
"tags": ["Billing"],
4446
"parameters": [
4547
{
@@ -105,7 +107,9 @@
105107
"get": {
106108
"operationId": "listBillingLogs",
107109
"summary": "List Billing Logs",
108-
"description": "List the credit-denominated billing ledger with source filtering and opaque cursor pagination. `period` defaults to `30d`, so an unqualified request covers only the last 30 days: paginating to `nextCursor: null` exhausts that window, not the whole ledger. An inverted custom window is a 400 rather than an empty page.",
110+
"description": "List the credit-denominated billing ledger with source filtering and opaque cursor pagination. `period` defaults to `30d`, so an unqualified request covers only the last 30 days: paginating to `nextCursor: null` exhausts that window, not the whole ledger. An inverted custom window is a 400 rather than an empty page.\n\nOAuth scope: `api:read`.",
111+
"x-sim-operation": "billing.logs.list",
112+
"x-oauth-scope": "api:read",
109113
"tags": ["Billing"],
110114
"parameters": [
111115
{
@@ -262,7 +266,7 @@
262266
"type": "http",
263267
"scheme": "bearer",
264268
"bearerFormat": "OAuth 2.0 access token",
265-
"description": "A Sim OAuth access token obtained by a registered client through the authorization-code flow. The token must carry the scope required by the operation."
269+
"description": "A Sim OAuth access token obtained by a registered client through the authorization-code flow. Each operation declares its required scope: api:read permits reads and searches; api:write also permits changes and execution and implies api:read. Scope requirements follow the application operation, independent of HTTP method or workspace role."
266270
}
267271
},
268272
"headers": {

0 commit comments

Comments
 (0)