From 21cfe28f8bd59fde8c34ad90eff8a1f28b226d55 Mon Sep 17 00:00:00 2001 From: Brandon Corbett Date: Wed, 21 Oct 2026 16:06:37 -0400 Subject: [PATCH] docs: document the 0.19.4 security changes Organization member adds need admin:write, provider secret names end in _CLIENT_SECRET, OAUTH_PROVIDER_PRIVATE_HOSTS, and starters refuse the example secrets outside development. --- src/content/docs/build/oauth-login.mdx | 6 +++++- src/content/docs/build/organizations-memberships.mdx | 12 +++++++++--- src/content/docs/build/react-sdk.mdx | 2 +- src/content/docs/operate/production-checklist.mdx | 4 +++- src/content/docs/reference/environment-variables.mdx | 3 ++- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/content/docs/build/oauth-login.mdx b/src/content/docs/build/oauth-login.mdx index 105fc34..87aadc0 100644 --- a/src/content/docs/build/oauth-login.mdx +++ b/src/content/docs/build/oauth-login.mdx @@ -67,7 +67,7 @@ Provider config fields: | `name` | Display name returned to the UI | | `enabled` | Whether the provider is visible and usable | | `clientId` | OAuth client ID | -| `clientSecretEnv` | Env var name that contains the client secret | +| `clientSecretEnv` | Env var name that contains the client secret. Must end in `_CLIENT_SECRET` (API v0.19.4) | | `authorizationUrl` | Provider authorization endpoint | | `tokenUrl` | Provider token endpoint | | `userInfoUrl` | Provider userinfo endpoint | @@ -88,6 +88,10 @@ Provider config fields: | `externalIdJsonPath` | ID token claim holding the imported user's external id, such as `oid` | | `promptPasskeyEnrollment` | Default `false`. When `true`, a user with no passkey who signs in through it gets `nextStep: 'enroll_passkey'` | +In production, provider URLs must use `https` and connect only to public addresses; the server does +not follow redirects from them. For an identity provider on your own network, list its host in +`OAUTH_PROVIDER_PRIVATE_HOSTS`. Link-local addresses are refused even when listed. + In production, set `OAUTH_STATE_SECRET`. Development can fall back, but production should not rely on derived local state secrets. diff --git a/src/content/docs/build/organizations-memberships.mdx b/src/content/docs/build/organizations-memberships.mdx index 887d0dd..230987a 100644 --- a/src/content/docs/build/organizations-memberships.mdx +++ b/src/content/docs/build/organizations-memberships.mdx @@ -68,7 +68,7 @@ Through the Express adapter, browser apps call these routes at `/auth/organizati | `PATCH` | `/auth/organizations/:organizationId` | Update an organization | | `POST` | `/auth/organizations/:organizationId/switch` | Set active organization for the current session | | `GET` | `/auth/organizations/:organizationId/members` | List members | -| `POST` | `/auth/organizations/:organizationId/members` | Add a member | +| `POST` | `/auth/organizations/:organizationId/members` | Add a member (`admin:write` only) | | `PATCH` | `/auth/organizations/:organizationId/members/:userId` | Update member roles and scopes | | `DELETE` | `/auth/organizations/:organizationId/members/:userId` | Remove a member | @@ -220,13 +220,19 @@ export function MemberList() { ``` Members can be listed by users with `members:read`, organization managers, or global admin read -access. +access. A member's phone number and global roles are shown only to callers with `admin:read`; +everyone else sees `phone: null` and `roles: []`, with the organization roles and scopes intact. + +Adding a member needs `admin:write` from v0.19.4. Adding someone by email without their consent let +any account learn which addresses have accounts and attach strangers to an organization, so +self-service adds return as invitations the member accepts in v0.20. --- ## Step 5: Add Or Update Members -Add a member by `userId` or `email`. +An administrator (`admin:write`) can add a member by `userId` or `email`. For anyone else the call +answers `403`. ```ts await authClient.addOrganizationMember(activeOrganization.id, { diff --git a/src/content/docs/build/react-sdk.mdx b/src/content/docs/build/react-sdk.mdx index 1849a2f..021585b 100644 --- a/src/content/docs/build/react-sdk.mdx +++ b/src/content/docs/build/react-sdk.mdx @@ -443,7 +443,7 @@ For full custom organization management, use the headless client methods: - `updateOrganization(organizationId, input)` - `switchOrganization(organizationId)` - `listOrganizationMembers(organizationId)` -- `addOrganizationMember(organizationId, input)` +- `addOrganizationMember(organizationId, input)` (needs `admin:write` from API v0.19.4) - `updateOrganizationMember(organizationId, userId, input)` - `removeOrganizationMember(organizationId, userId)` diff --git a/src/content/docs/operate/production-checklist.mdx b/src/content/docs/operate/production-checklist.mdx index 794d6dc..0907817 100644 --- a/src/content/docs/operate/production-checklist.mdx +++ b/src/content/docs/operate/production-checklist.mdx @@ -114,7 +114,9 @@ kept out of version control. On your backend, give the server adapter the same `SEAMLESS_SERVICE_TOKEN` as its `serviceSecret`, and set its own production options (a long random `cookieSecret`, secure cookies and an explicit -`jwksKid`). See [Required options](/build/server-sdks#required-options). Run the backend with +`jwksKid`). If the backend started from a starter's `.env.example`, replace its example +`COOKIE_SIGNING_KEY` and `SEAMLESS_SERVICE_TOKEN`: those values are public, and starters refuse them +outside development. See [Required options](/build/server-sdks#required-options). Run the backend with `NODE_ENV=production` as well: the starter backends log sign-in codes to the console in development. --- diff --git a/src/content/docs/reference/environment-variables.mdx b/src/content/docs/reference/environment-variables.mdx index 174f51e..f282e24 100644 --- a/src/content/docs/reference/environment-variables.mdx +++ b/src/content/docs/reference/environment-variables.mdx @@ -124,7 +124,8 @@ passkey flows even when the rest of the stack looks healthy. | `OAUTH_PROVIDERS` | JSON provider config array | | `OAUTH_STATE_SECRET` | OAuth state signing secret | | `OAUTH_ALLOW_UNBOUND_STATE` | Deprecated, removed in v0.20. `true` accepts an OAuth callback without the `binding` from `/start`, for clients older than `@seamless-auth/client` 0.7.1. It reopens login CSRF while set and warns at boot | -| Provider secret vars | Values referenced by provider `clientSecretEnv` | +| `OAUTH_PROVIDER_PRIVATE_HOSTS` | Comma-separated OAuth provider hosts allowed to resolve to private or loopback addresses in production, for an on-premises IdP. Link-local is always refused | +| Provider secret vars | Values referenced by provider `clientSecretEnv`; names must end in `_CLIENT_SECRET` | Example provider configs reference secrets by name: