diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 7ede31efb625a..389a969427323 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -715,6 +715,7 @@ export const auth = { { name: 'Rate Limits', url: '/guides/auth/rate-limits' }, { name: 'Bot Detection (CAPTCHA)', url: '/guides/auth/auth-captcha' }, { name: 'JWTs', url: '/guides/auth/jwts' }, + { name: 'JWT Fields Reference', url: '/guides/auth/jwt-fields' }, { name: 'Row Level Security', url: '/guides/database/postgres/row-level-security' }, { name: 'Column Level Security', diff --git a/apps/docs/content/guides/auth.mdx b/apps/docs/content/guides/auth.mdx index 8fefd30e7a3c9..213d1b91a1ce2 100644 --- a/apps/docs/content/guides/auth.mdx +++ b/apps/docs/content/guides/auth.mdx @@ -17,7 +17,7 @@ Authentication and authorization are the core responsibilities of any Auth syste - **Authentication** means checking that a user is who they say they are. - **Authorization** means checking what resources a user is allowed to access. -Supabase Auth uses [JSON Web Tokens (JWTs)](/docs/guides/auth/jwts) for authentication. Auth integrates with Supabase's database features, making it easy to use [Row Level Security (RLS)](/docs/guides/database/postgres/row-level-security) for authorization. +Supabase Auth uses [JSON Web Tokens (JWTs)](/docs/guides/auth/jwts) for authentication. For a complete reference of all JWT fields, see the [JWT Fields Reference](/docs/guides/auth/jwt-fields). Auth integrates with Supabase's database features, making it easy to use [Row Level Security (RLS)](/docs/guides/database/postgres/row-level-security) for authorization. ## The Supabase ecosystem diff --git a/apps/docs/content/guides/auth/jwt-fields.mdx b/apps/docs/content/guides/auth/jwt-fields.mdx new file mode 100644 index 0000000000000..2a1542864563c --- /dev/null +++ b/apps/docs/content/guides/auth/jwt-fields.mdx @@ -0,0 +1,288 @@ +--- +id: 'jwt-fields' +title: 'JWT Fields Reference' +subtitle: 'Complete reference for JWT fields in Supabase' +--- + +This page provides a comprehensive reference for all JWT fields used in Supabase authentication tokens. This information is essential for server-side JWT validation and serialization, especially when implementing authentication in languages like Rust where field names like `ref` are reserved keywords. + +## JWT structure overview + +Supabase JWTs follow the standard JWT structure with three parts: + +- **Header**: Contains algorithm and key information +- **Payload**: Contains the claims (user data and metadata) +- **Signature**: Cryptographic signature for verification + +The payload contains various claims that provide user identity, authentication level, and authorization information. + +## Required claims + +These claims are always present in Supabase JWTs and cannot be removed: + +| Field | Type | Description | Example | +| -------------- | -------------------- | ----------------------------------------------------------- | --------------------------------------------- | +| `iss` | `string` | **Issuer** - The entity that issued the JWT | `"https://project-ref.supabase.co/auth/v1"` | +| `aud` | `string \| string[]` | **Audience** - The intended recipient of the JWT | `"authenticated"` or `"anon"` | +| `exp` | `number` | **Expiration Time** - Unix timestamp when the token expires | `1640995200` | +| `iat` | `number` | **Issued At** - Unix timestamp when the token was issued | `1640991600` | +| `sub` | `string` | **Subject** - The user ID (UUID) | `"123e4567-e89b-12d3-a456-426614174000"` | +| `role` | `string` | **Role** - User's role in the system | `"authenticated"`, `"anon"`, `"service_role"` | +| `aal` | `string` | **Authenticator Assurance Level** - Authentication strength | `"aal1"`, `"aal2"` | +| `session_id` | `string` | **Session ID** - Unique session identifier | `"session-uuid"` | +| `email` | `string` | **Email** - User's email address | `"user@example.com"` | +| `phone` | `string` | **Phone** - User's phone number | `"+1234567890"` | +| `is_anonymous` | `boolean` | **Anonymous Flag** - Whether the user is anonymous | `false` | + +## Optional claims + +These claims may be present depending on the authentication context: + +| Field | Type | Description | Example | +| --------------- | -------- | -------------------------------------------------------------------------- | --------------------------------------------------- | +| `jti` | `string` | **JWT ID** - Unique identifier for the JWT | `"jwt-uuid"` | +| `nbf` | `number` | **Not Before** - Unix timestamp before which the token is invalid | `1640991600` | +| `app_metadata` | `object` | **App Metadata** - Application-specific user data | `{"provider": "email"}` | +| `user_metadata` | `object` | **User Metadata** - User-specific data | `{"name": "John Doe"}` | +| `amr` | `array` | **Authentication Methods Reference** - List of authentication methods used | `[{"method": "password", "timestamp": 1640991600}]` | + +## Special fields + +| Field | Type | Description | Example | Context | +| ----- | -------- | --------------------------------------------------- | ------------------------ | ----------------------------- | +| `ref` | `string` | **Project Reference** - Supabase project identifier | `"abcdefghijklmnopqrst"` | Anon/Service role tokens only | + +## Field value constraints + +### Authenticator assurance level (`aal`) + +| Value | Description | +| -------- | ---------------------------------------------------- | +| `"aal1"` | Single-factor authentication (password, OAuth, etc.) | +| `"aal2"` | Multi-factor authentication (password + TOTP, etc.) | + +### Role values (`role`) + +| Value | Description | Use Case | +| ----------------- | ------------------ | ----------------------------------- | +| `"anon"` | Anonymous user | Public access with RLS policies | +| `"authenticated"` | Authenticated user | Standard user access | +| `"service_role"` | Service role | Admin privileges (server-side only) | + +### Audience values (`aud`) + +| Value | Description | +| ----------------- | ----------------------------- | +| `"authenticated"` | For authenticated user tokens | +| `"anon"` | For anonymous user tokens | + +### Authentication methods (`amr.method`) + +| Value | Description | +| ----------------- | ----------------------------- | +| `"oauth"` | OAuth provider authentication | +| `"password"` | Email/password authentication | +| `"otp"` | One-time password | +| `"totp"` | Time-based one-time password | +| `"recovery"` | Account recovery | +| `"invite"` | Invitation-based signup | +| `"sso/saml"` | SAML single sign-on | +| `"magiclink"` | Magic link authentication | +| `"email/signup"` | Email signup | +| `"email_change"` | Email change | +| `"token_refresh"` | Token refresh | +| `"anonymous"` | Anonymous authentication | + +## JWT examples + +### Authenticated user token + +```json +{ + "aal": "aal1", + "amr": [ + { + "method": "password", + "timestamp": 1640991600 + } + ], + "app_metadata": { + "provider": "email", + "providers": ["email"] + }, + "aud": "authenticated", + "email": "user@example.com", + "exp": 1640995200, + "iat": 1640991600, + "iss": "https://abcdefghijklmnopqrst.supabase.co/auth/v1", + "phone": "", + "role": "authenticated", + "session_id": "123e4567-e89b-12d3-a456-426614174000", + "sub": "123e4567-e89b-12d3-a456-426614174000", + "user_metadata": { + "name": "John Doe" + }, + "is_anonymous": false +} +``` + +### Anonymous user token + +```json +{ + "iss": "supabase", + "ref": "abcdefghijklmnopqrst", + "role": "anon", + "iat": 1640991600, + "exp": 1640995200 +} +``` + +### Service role token + +```json +{ + "iss": "supabase", + "ref": "abcdefghijklmnopqrst", + "role": "service_role", + "iat": 1640991600, + "exp": 1640995200 +} +``` + +## Language-Specific considerations + +### Rust + +In Rust, the `ref` field is a reserved keyword. When deserializing JWTs, you'll need to handle this: + +```rust +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize)] +struct JwtClaims { + iss: String, + #[serde(rename = "ref")] // Handle reserved keyword + project_ref: Option, + role: String, + iat: i64, + exp: i64, + // ... other fields +} +``` + +### TypeScript/JavaScript + +```typescript +interface JwtClaims { + iss: string + aud: string | string[] + exp: number + iat: number + sub: string + role: string + aal: 'aal1' | 'aal2' + session_id: string + email: string + phone: string + is_anonymous: boolean + jti?: string + nbf?: number + app_metadata?: Record + user_metadata?: Record + amr?: Array<{ + method: string + timestamp: number + }> + ref?: string // Only in anon/service role tokens +} +``` + +### Python + +```python +from typing import Optional, Union, List, Dict, Any +from dataclasses import dataclass + +@dataclass +class AmrEntry: + method: str + timestamp: int + +@dataclass +class JwtClaims: + iss: str + aud: Union[str, List[str]] + exp: int + iat: int + sub: str + role: str + aal: str + session_id: str + email: str + phone: str + is_anonymous: bool + jti: Optional[str] = None + nbf: Optional[int] = None + app_metadata: Optional[Dict[str, Any]] = None + user_metadata: Optional[Dict[str, Any]] = None + amr: Optional[List[AmrEntry]] = None + ref: Optional[str] = None # Only in anon/service role tokens +``` + +### Go + +```go +type AmrEntry struct { + Method string `json:"method"` + Timestamp int64 `json:"timestamp"` +} + +type JwtClaims struct { + Iss string `json:"iss"` + Aud interface{} `json:"aud"` // string or []string + Exp int64 `json:"exp"` + Iat int64 `json:"iat"` + Sub string `json:"sub"` + Role string `json:"role"` + Aal string `json:"aal"` + SessionID string `json:"session_id"` + Email string `json:"email"` + Phone string `json:"phone"` + IsAnonymous bool `json:"is_anonymous"` + Jti *string `json:"jti,omitempty"` + Nbf *int64 `json:"nbf,omitempty"` + AppMetadata map[string]interface{} `json:"app_metadata,omitempty"` + UserMetadata map[string]interface{} `json:"user_metadata,omitempty"` + Amr []AmrEntry `json:"amr,omitempty"` + Ref *string `json:"ref,omitempty"` // Only in anon/service role tokens +} +``` + +## Validation guidelines + +When implementing JWT validation on your server: + +1. **Check Required Fields**: Ensure all required claims are present +2. **Validate Types**: Verify field types match expected types +3. **Check Expiration**: Validate `exp` timestamp is in the future +4. **Verify Issuer**: Ensure `iss` matches your Supabase project +5. **Check Audience**: Validate `aud` matches expected audience +6. **Handle Reserved Keywords**: Use field renaming for languages like Rust + +## Security considerations + +- **Always validate the JWT signature** before trusting any claims +- **Never expose service role tokens** to client-side code +- **Validate all claims** before trusting the JWT +- **Check token expiration** on every request +- **Use HTTPS** for all JWT transmission +- **Rotate JWT secrets** regularly +- **Implement proper error handling** for invalid tokens + +## Related documentation + +- [JWT Overview](/docs/guides/auth/jwts) +- [Custom Access Token Hooks](/docs/guides/auth/auth-hooks/custom-access-token-hook) +- [Row Level Security](/docs/guides/database/postgres/row-level-security) +- [Server-Side Auth](/docs/guides/auth/server-side) diff --git a/apps/docs/content/guides/auth/jwts.mdx b/apps/docs/content/guides/auth/jwts.mdx index b568d6c7b662c..0e68d9952f434 100644 --- a/apps/docs/content/guides/auth/jwts.mdx +++ b/apps/docs/content/guides/auth/jwts.mdx @@ -177,3 +177,4 @@ Now that you understand what JWTs are and where they're used in Supabase, you ca ## Resources - JWT debugger: https://jwt.io/ +- [JWT Fields Reference](/docs/guides/auth/jwt-fields) - Complete reference for all JWT fields in Supabase diff --git a/apps/docs/content/guides/getting-started/mcp.mdx b/apps/docs/content/guides/getting-started/mcp.mdx index 22f72bb04f729..44d85683efb0b 100644 --- a/apps/docs/content/guides/getting-started/mcp.mdx +++ b/apps/docs/content/guides/getting-started/mcp.mdx @@ -14,7 +14,7 @@ The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) - [Cline](#cline) (VS Code extension) - [Claude desktop](#claude-desktop) - [Claude code](#claude-code) -- [Qodo Gen](#qodo-gen) +- [Amp](#amp) Once connected, your AI assistants can interact with and query your Supabase projects on your behalf. @@ -123,6 +123,68 @@ You can also add the Supabase MCP server as a locally-scoped server, which will Locally-scoped servers take precedence over project-scoped servers with the same name and are stored in your project-specific user settings. +### Amp + +You can add the Supabase MCP server to Amp in two ways: + +#### Option 1: VSCode settings.json + +1. Open "Preferences: Open User Settings (JSON)" +2. Add the following configuration: + + ```json + { + "amp.mcpServers": { + "supabase": { + "command": "npx", + "args": [ + "-y", + "@supabase/mcp-server-supabase@latest", + "--read-only", + "--project-ref=" + ], + "env": { + "SUPABASE_ACCESS_TOKEN": "" + } + } + } + } + ``` + + Replace `project-ref` and `personal-access-token` with your project ref and personal access token. + +3. Save the configuration file. +4. Restart VS Code to apply the new configuration. + +#### Option 2: Amp CLI + +1. Edit `~/.config/amp/settings.json` +1. Add the following configuration: + + ```json + { + "amp.mcpServers": { + "supabase": { + "command": "npx", + "args": [ + "-y", + "@supabase/mcp-server-supabase@latest", + "--read-only", + "--project-ref=" + ], + "env": { + "SUPABASE_ACCESS_TOKEN": "" + } + } + } + } + ``` + + Replace `project-ref` and `personal-access-token` with your project ref and personal access token. + +1. Save the configuration file. +1. Restart Amp to apply the new configuration. + ### Qodo Gen 1. Open [Qodo Gen](https://docs.qodo.ai/qodo-documentation/qodo-gen) chat panel in VSCode or IntelliJ.