# Vonvon: full public documentation corpus (en) - Concise index: https://vonvon.id/en/llms.txt - Documentation hub: https://vonvon.id/docs - Published pages: 44 ## Canonical aliases - `/docs/oidc` -> `/oidc-oauth` - `/docs/oauth` -> `/oidc-oauth` - `/docs/sso` -> `/enterprise-sso` ## Pages # Build identity at the edge, without giving up control > Vonvon brings Hosted Auth, OIDC, organizations, enterprise federation, directory sync, and SDKs into one MIT-licensed platform running on Cloudflare Workers. Locale: en Canonical: https://vonvon.id/ Markdown: https://vonvon.id/index.md Source: https://vonvon.id/index.mdx Vonvon brings Hosted Auth, OIDC, organizations, enterprise federation, directory sync, and SDKs into one MIT-licensed platform running on Cloudflare Workers. ## Three focused Workers. One identity platform. Public content and management stay binding-free. Identity state, protocols, and policy remain in Core. - **Site:** Product, documentation, search, and agent-readable content - **Console:** Organization and instance management UI - **Core:** Hosted Auth, protocols, APIs, data, and asynchronous work ## One control plane from sign-in to enterprise access Use the complete platform or adopt the protocol, UI, and SDK layers that fit your architecture. ### Authentication and account Hosted sign-in, passkeys, password, MFA, session management, consent, and account self-service share one tenant-aware Core. ### Organizations and access Model organizations, OrgUnits, projects, roles, grants, approval policies, and access requests without creating a separate admin tenant. ### Federation and provisioning Connect inbound SAML and OIDC, downstream SaaS SSO, SCIM, directory sync, and domain discovery behind explicit policy boundaries. ### Protocols and developer experience Ship OIDC and OAuth flows, Management APIs, webhooks, framework SDKs, localized docs, and networkless token verification from one repository. ## Production where proven, local where not First-party Hosted Auth, Console, and Management API paths have production evidence on vonvon.id. Enterprise IdP, downstream SaaS, social OAuth, and SMS or WhatsApp claims stay non-production-supported until a real L4 row exists for that path. ## Open source, inspectable, and self-hostable The MIT-licensed repository includes the complete feature set. Security posture and project governance remain visible through OpenSSF and the public source. # Vonvon Identity Platform > Vonvon brings Hosted Auth, passkeys, OIDC and OAuth, organizations, enterprise federation, SCIM, and SDKs into one identity platform built for Cloudflare. Locale: en Canonical: https://vonvon.id/docs Markdown: https://vonvon.id/docs/index.md Source: https://vonvon.id/docs/index.mdx Open source identity platform Vonvon brings Hosted Auth, passkeys, OIDC and OAuth, organizations, enterprise federation, SCIM, and SDKs into one identity platform built for Cloudflare. ## One platform for identity Vonvon handles sign-in, authorization, organization membership, enterprise connections, and user lifecycle from one identity core. Applications use standards-based endpoints while teams manage the instance and every organization from one Console. Run the same MIT-licensed codebase in single-tenant or multi-tenant mode on your Cloudflare account. Self-hosting includes every feature with no license check or separate edition. Vonvon is pre-1.0 and not production-ready. The documentation distinguishes local implementation evidence from real provider and SaaS verification. ## What Vonvon includes - Hosted Auth, passkeys, passwords, magic links, social sign-in, MFA, recovery, consent, and account self-service. - OIDC and OAuth 2.0, token issuance, discovery, JWKS, organizations, projects, applications, roles, and permissions. - Inbound enterprise SAML and OIDC, SCIM user and group provisioning, webhooks, audit records, and management APIs. - Browser, server, web framework, mobile, and desktop SDKs for integrating applications across the stack. ## Explore Vonvon ### Product and setup - [Getting started](/getting-started) Connect an application to Vonvon with OIDC discovery and Hosted Auth. - [Hosted Auth](/hosted-auth) Configure the unified sign-in and user creation flow. - [Organizations](/organizations) Model Organization, OrgUnit, project, and access approval boundaries. - [Branding](/branding) Manage the seven stored organization branding fields exposed by the Management API. Hosted Auth runtime application and custom CSS are not shipped. - [Self-hosting](/self-hosting) Deploy Vonvon as three isolated Workers on your Cloudflare account, with the complete data, queue, consistency, email, routing, and production-control inventory. ### Identity protocols - [OIDC and OAuth](/oidc-oauth) Discovery, authorization, token, logout, and OAuth extension endpoints. - [Enterprise SSO](/enterprise-sso) Configure upstream enterprise IdPs and track downstream SaaS SSO boundaries without overstating production support. - [Social login](/social-login) Configure social OAuth providers while keeping provider-ready separate from production support. - [SCIM API reference](/scim) SCIM 2.0 endpoint contract for provisioning users and groups into Vonvon. - [SAML SSO](/saml) Connect enterprise identity providers using SAML 2.0. ### APIs and operations - [Management API](/management-api) Use scoped API keys to manage organization resources from your backend. - [Webhooks](/webhooks) Subscribe to implemented user, organization, membership, invitation, and SAML certificate events through signed HTTP deliveries. ### SDK overview - [SDKs](/sdks) TypeScript packages and locally verified native SDKs for server, web, mobile, and desktop. ### Server SDKs - [@vonvon-kit/backend](/sdks/backend) Networkless JWT verification, request authentication, and webhook signature validation for edge and server runtimes. - [sdk/go](/sdks/go) Go server SDK for networkless JWT verification, request authentication, and webhook signature validation. - [sdk/rust](/sdks/rust) Async Rust server SDK for networkless JWT verification, request authentication, and webhook signature validation. - [sdk/python](/sdks/python) Async Python server SDK for networkless JWT verification, request authentication, and webhook signature validation. - [sdk/ruby](/sdks/ruby) Ruby server SDK for networkless JWT verification, Rack/Rails request authentication, and webhook signature validation. - [sdk/php](/sdks/php) PHP 8.1+ server SDK for networkless JWT verification, PSR-7 request authentication, and webhook signature validation. - [sdk/java](/sdks/java) Java 17+ server SDK for networkless JWT verification, HTTP request authentication, and webhook signature validation. - [sdk/dotnet](/sdks/dotnet) .NET 8 server SDK for networkless JWT verification, ASP.NET Core request authentication, and webhook signature validation. ### Web SDKs - [@vonvon-kit/core](/sdks/core) Browser client for session state, short-lived JWT access, and Management API helpers. - [@vonvon-kit/react](/sdks/react) React 19 provider, hooks, control components, and hosted UI building blocks for customer applications. - [@vonvon-kit/nextjs](/sdks/nextjs) Next.js middleware, App Router and Pages Router server helpers, plus React SDK re-exports. - [@vonvon-kit/vue](/sdks/vue) Vue 3 plugin, composables, and headless components on top of @vonvon-kit/core. - [@vonvon-kit/nuxt](/sdks/nuxt) Nuxt 3 module with H3/Nitro server middleware and auto-imported Vue composables for SSR and full-stack apps. - [@vonvon-kit/svelte](/sdks/svelte) Svelte 5 reactive stores and SvelteKit server hook for client and SSR authentication. - [@vonvon-kit/solid](/sdks/solid) SolidJS context provider, signal-based auth primitives, and headless components on top of @vonvon-kit/core. - [@vonvon-kit/angular](/sdks/angular) Angular 17+ standalone provider, RxJS service, functional route guards, and standalone components on top of @vonvon-kit/core. - [@vonvon-kit/astro](/sdks/astro) Astro integration with SSR middleware, server helpers, and island client singleton for static and server-rendered Astro sites. - [@vonvon-kit/remix](/sdks/remix) Remix loader and action server helpers, cookie session storage, and OAuth callback handler with React SDK re-exports. ### Mobile SDKs - [@vonvon-kit/react-native](/sdks/react-native) React Native provider and hooks for Hosted Auth redirect, PKCE S256, deep-link callback, and secure token storage adapters. - [@vonvon-kit/expo](/sdks/expo) Expo SecureStore and WebBrowser adapters for @vonvon-kit/react-native plus an Expo Router route guard hook. - [sdk/ios](/sdks/ios) Swift SDK for iOS and macOS using ASWebAuthenticationSession, PKCE S256 authorization code flow, and Keychain token storage. - [sdk/android](/sdks/android) Kotlin SDK for Android using Chrome Custom Tabs, PKCE S256 authorization code flow, and Keystore-backed EncryptedSharedPreferences token storage. - [sdk/flutter](/sdks/flutter) Dart / Flutter SDK for iOS, Android, and desktop using flutter\_web\_auth\_2, PKCE S256 authorization code flow, and flutter\_secure\_storage token persistence. ### Desktop SDKs - [@vonvon-kit/electron](/sdks/electron) Electron SDK with main process PKCE flow, contextBridge preload, OS keychain token storage, and loopback or custom-scheme callback strategies. - [@vonvon-kit/tauri](/sdks/tauri) Tauri v2 desktop SDK with PKCE S256 flow, deeplink callback handler, OS keychain adapter, and Rust plugin template. - [sdk/macos](/sdks/macos) Swift SDK for macOS using ASWebAuthenticationSession, PKCE S256 authorization code flow, and Keychain token storage. Shares the implementation pattern with sdk/ios. - [sdk/windows](/sdks/windows) C# / .NET SDK for WinUI 3 applications using WebView2 for authorization, PKCE S256, and DPAPI-protected IsolatedStorage for token persistence. - [sdk/linux](/sdks/linux) Rust SDK for Linux desktop using xdg-open for browser launch, loopback TCP for the authorization callback, PKCE S256, and freedesktop.org Secret Service token storage. # Getting started > Connect an application to Vonvon with OIDC discovery and Hosted Auth. Locale: en Canonical: https://vonvon.id/getting-started Markdown: https://vonvon.id/getting-started/index.md Source: https://vonvon.id/getting-started/index.mdx ## Base URLs Vonvon is an identity provider. The instance domain is the OIDC issuer, API base URL, Hosted Auth base URL, and console base URL. Organizations provide policy, branding, membership, and resource isolation. Start by reading `/.well-known/openid-configuration` from the instance domain. The discovery document gives your app the authorization, token, userinfo, logout, and JWKS endpoints for that issuer. | Surface | Path | Use | | --- | --- | --- | | OIDC discovery | `/.well-known/openid-configuration` | Read issuer metadata before configuring a relying party. | | Authorization | `/authorize` | Redirect users to the unified Hosted Auth flow. | | Token | `/token` | Exchange an authorization code or refresh token. | | Userinfo | `/userinfo` | Read claims for a valid access token. | | Management API | `/v1/*` | Manage organization resources from your backend. | ## Minimum integration - Create an OIDC application and register exact redirect URI values. - Use authorization code flow with PKCE S256 for browser and native clients. - Validate ID tokens and access tokens against the issuer and JWKS URI. - Store refresh tokens server side or in a secure native credential store. ## Authorization request ```mermaid sequenceDiagram participant Browser participant RP participant Vonvon RP->>Browser: /authorize + PKCE S256 Browser->>Vonvon: /authorize + PKCE S256 Vonvon->>Browser: /sign-in Browser->>Vonvon: WebAuthn / SSO Vonvon->>Browser: code Browser->>RP: code RP->>Vonvon: /token + PKCE S256 Vonvon->>RP: ID token + access token ``` ```js const authorizeUrl = new URL('https://vonvon.id/authorize') authorizeUrl.searchParams.set('client_id', clientId) authorizeUrl.searchParams.set('redirect_uri', redirectUri) authorizeUrl.searchParams.set('response_type', 'code') authorizeUrl.searchParams.set('scope', 'openid profile email') authorizeUrl.searchParams.set('code_challenge', codeChallenge) authorizeUrl.searchParams.set('code_challenge_method', 'S256') ``` # Hosted Auth > Configure the unified sign-in and user creation flow. Locale: en Canonical: https://vonvon.id/hosted-auth Markdown: https://vonvon.id/hosted-auth/index.md Source: https://vonvon.id/hosted-auth/index.mdx ## Unified flow Vonvon does not expose separate end-user registration and login products. Hosted Auth starts from the same identifier step and decides login or user creation from organization policy and account state. A guest or credential sign-up with intent=sign-up then creates a top-level Tenant with an Email, Organization name, and URL slug. A guest Email stays pending without reserving an account address; the new owner can read Console data, and verifies that Email before the first business change. The same Email in another Tenant remains a separate account. Bootstrap defaults enable email magic link and email OTP only. Password, WhatsApp OTP, SMS OTP, passkey, social OAuth, and enterprise SSO stay hidden until the organization policy and required credentials enable them. Passwordless email magic-link and OTP requests freeze the sign-up intent, server-owned continuation, application client, and invitation row ID when the challenge is created. Social OAuth invitation authorization likewise validates the raw capability before redirecting upstream, persists only the invitation row ID in its one-time state, and ignores caller-owned token routes on callback. Verification cannot rewrite those fields. Raw invitation capabilities are never stored in these flow contexts; when MFA interrupts acceptance, Vonvon uses a short-lived signed continuation bound to the Tenant, User, Session, and invitation. ```mermaid flowchart TD Browser --> authorize["/authorize"] authorize --> signIn["/sign-in"] signIn --> config["/auth/config"] config --> methods["WebAuthn / Password / OTP / SSO"] config --> signup["guest or intent=sign-up"] signup --> createOrg["/create-organization"] createOrg --> readOnly["Console reads"] readOnly --> verifyEmail["Verify Email"] verifyEmail --> mutations["Business mutations"] methods --> code["code"] methods --> mfa["/auth/mfa/passkey/*"] mfa --> code ``` ## Configuration endpoint `GET /auth/config` returns the public Hosted Auth configuration for the organization. Provider secrets and disabled providers are not returned to the browser. | Method | Displayed when | | --- | --- | | Magic link | Enabled and allowed for login or user creation. | | Email OTP | Enabled and allowed for login or user creation. | | Phone OTP | WhatsApp or SMS provider is configured, enabled, and allowed for login or user creation. | | Password | Password policy enables login or user creation. | | Social OAuth | Provider is enabled, credentials exist, and policy allows the action. | | Inbound enterprise SSO | Domain discovery matches a verified organization domain. | ## Identifier policy - Organizations can require email identifiers, username identifiers, or both. - Allowed and blocked email domain lists apply before user creation. - Force SSO hides local methods when an enterprise connection is required. ## WebAuthn and passkey boundaries - Passkey sign-in is primary AAL2 authentication. Password or OTP sessions can complete MFA through `/auth/mfa/passkey/*` with user verification required. - Organization policy `attestationMode` selects none, indirect, or direct enterprise attestation during passkey registration. - WebAuthn credential parameters advertise ES256, RS256, and EdDSA. Syncable passkeys remain AAL2 even after MFA. - `urn:vonvon:aal3` is not currently issued. WebAuthn UV and BE/BS flags do not prove the non-exportable hardware key required by NIST; AAL3 requests fail explicitly and the current maximum mapping is AAL2. # Organizations > Model Organization, OrgUnit, project, and access approval boundaries. Locale: en Canonical: https://vonvon.id/organizations Markdown: https://vonvon.id/organizations/index.md Source: https://vonvon.id/organizations/index.mdx ## Organization hierarchy An Instance contains top-level Organizations. An Organization can contain one level of SubOrg; each organization remains a policy, membership, branding, RBAC, and isolation boundary. An OrgUnit is a department or team inside one Organization. It never changes TenantContext, issuer, token claims, or the one-level SubOrg limit. - OrgUnit trees support a maximum depth of eight. - Each member can have one primary OrgUnit and additional placements. - The nearest active manager in the OrgUnit ancestor chain is the first access-request approver candidate. ## Projects and access policy Projects group applications and authorization roles. The per-project `access_policy` controls only same-organization users without an effective UserGrant; cross-organization ProjectGrant authorization is unaffected. | Project policy | Same-organization behavior | Self-service path | | --- | --- | --- | | Open | Allowed without a UserGrant. | No access request is needed. | | Restricted | Denied; an administrator grants access directly. | No self-service request is available. | | Approval required | Denied until an AccessRequest is approved. | The user submits an access request. | ## Access request lifecycle AccessRequest moves from `pending` to `approved`, `denied`, `cancelled`, or `expired`. Every outcome is terminal, and pending requests expire lazily after 14 days. Approval creates a traceable UserGrant in the same transaction. The requester cannot approve their own request; resolution falls through from the OrgUnit manager to project and organization managers. | Method | Path | Purpose | Required scope | | --- | --- | --- | --- | | `GET, POST` | `/auth/access-requests` | Create and list the current user's requests. | `session` | | `POST` | `/auth/access-requests/:id/cancel` | Cancel the current user's pending request. | `session` | | `GET` | `/auth/access-approvals` | List requests awaiting the current user's approval. | `session` | | `POST` | `/auth/access-approvals/:id/{approve\|deny}` | Approve or deny a pending request assigned to the current user. | `session` | | `GET` | `/v1/organizations/:orgId/access-requests` | List requests inside one organization. | `access-requests:read` | | `GET` | `/v1/organizations/:orgId/units` | Read the organization unit tree. | `org-units:read` | | `POST, PATCH, DELETE, PUT` | `/v1/organizations/:orgId/units/*` | Create, update, move, archive, and manage members of units. | `org-units:write` | ## Security boundaries - Every OrgUnit and AccessRequest query is scoped by `tenant_id` and `org_id`. Cross-organization or cross-tenant identifiers return 404 without revealing existence. - Membership and project access are separate: invitations join an Organization, while AccessRequest grants an existing active member access to one Project. - SCIM provisions users and memberships but does not map directory groups to OrgUnits or Project roles in the current implementation. - The Management API uses cursor pagination and shared API-key or organization-manager guards. It does not reuse end-user business routes. # OIDC and OAuth > Discovery, authorization, token, logout, and OAuth extension endpoints. Locale: en Canonical: https://vonvon.id/oidc-oauth Markdown: https://vonvon.id/oidc-oauth/index.md Source: https://vonvon.id/oidc-oauth/index.mdx ## Core endpoints | Endpoint | Description | | --- | --- | | `/.well-known/openid-configuration` | OIDC discovery for the current issuer. | | `/.well-known/oauth-protected-resource` | OAuth protected resource metadata for Vonvon-hosted resource endpoints. | | `/jwks` | Instance public signing keys with active and rotating kids. | | `/authorize` | Authorization endpoint with PKCE, state, nonce, PAR, and Hosted Auth handoff. | | `/par` | Pushed Authorization Requests with one-time request\_uri values. | | `/token` | Authorization code, refresh token, client credentials, device code, and token exchange. | | `/userinfo` | Bearer or DPoP access token user claims. | | `/end_session` | RP-initiated logout with registered post logout redirects. | ## Protocol requirements - PKCE uses S256. Plain PKCE is rejected. - Redirect URI matching is exact. Wildcards are not accepted. - Authorization codes are one-time use. - Refresh tokens rotate on every use and replay revokes the token family. - DPoP-bound clients must present a valid DPoP proof for token and resource calls. Authorization request `dpop_jkt` is bound to authorization code exchange. ## Support levels | Level | OAuth and OIDC features | | --- | --- | | Implemented | Authorization code, PKCE S256, refresh rotation, PAR, DPoP proof and `dpop_jkt` binding, dynamic client registration, ID tokens, userinfo, hybrid response types, signed JAR request objects, signed JARM responses, RAR `resource_access` authorization details, token exchange, mTLS client authentication, front-channel and back-channel logout, Device Flow, Session Management, CIBA, Browser-Based Apps profile gates, and FAPI 2.0 profile gates. | | Minimal implementation | OpenID Federation exposes entity metadata and a registration boundary only. Trust-chain resolution, trust anchors, policy processing, and production interoperability are not implemented. | | Planned | Shared Signals, CAEP, RISC, GNAP, UMA, HEART, OpenID4VP, and OpenID4VCI expose reserved routes that return explicit 501 errors. They are not protocol implementations. | | Production evidence | Local implementation and conformance gates are not production certification. Downstream SaaS OIDC uses the generic OIDC baseline, but each SaaS integration still requires real external L4 evidence. | | Deprecated or not supported | Implicit flow, password grant, plain PKCE, and wildcard redirects. | ## Role boundaries | Vonvon role | Current public status | | --- | --- | | OIDC / OAuth identity provider for customer applications | Implemented in local and Worker routes with authorization code, PKCE S256, PAR, DPoP, JAR, JARM, RAR, discovery, JWKS, token, userinfo, introspection, and revocation coverage. | | Upstream enterprise OIDC relying party | Provider-ready for enterprise connections. Production support requires a real IdP configuration and callback L4. | | Social OAuth relying party | Provider-ready for GitHub, Google, Microsoft account, and Apple. See Social login for provider-specific boundaries. | | Downstream SaaS OIDC identity provider | Generic OIDC baseline is available locally. SaaS-specific app templates and real SaaS L4 are still required before production support claims. | ## Client types | Client | Recommended flow | | --- | --- | | Web application | Authorization code with server side token exchange. | | SPA | Authorization code with PKCE S256. | | Native app | Authorization code with PKCE S256 and claimed redirects. | | Machine to machine | Client credentials with scoped access. | ## Guarded or minimal OAuth and OIDC extensions | Level | Status | | --- | --- | | Assertion grants | JWT bearer and SAML bearer assertion grants are not enabled. Registration rejects assertion grant metadata until a trust root exists. | | GNAP, UMA, HEART, OpenID4VP, OpenID4VCI | Only negative route and metadata stubs are exposed. Unsupported operations return explicit `501 unsupported_feature`; no functional protocol support is claimed. | # Enterprise SSO > Configure upstream enterprise IdPs and track downstream SaaS SSO boundaries without overstating production support. Locale: en Canonical: https://vonvon.id/enterprise-sso Markdown: https://vonvon.id/enterprise-sso/index.md Source: https://vonvon.id/enterprise-sso/index.mdx ## Two directions Inbound enterprise SSO means Vonvon acts as the SAML service provider or OIDC relying party for a company IdP. Downstream SaaS SSO means Vonvon acts as the SAML or OIDC identity provider for apps such as Slack, GitHub Enterprise Cloud, Microsoft custom apps, Atlassian, Salesforce, and Zoom. Both directions are separate evidence lines. A local fake IdP or fake SaaS run verifies the implementation baseline, but only a real external IdP or SaaS admin run can support a production-supported claim. ```mermaid flowchart LR IdP -->|SAML / OIDC| vonvonSp["Vonvon SP / RP"] vonvonIdP["Vonvon IdP"] -->|SAML / OIDC| SaaS ``` ## Upstream enterprise IdP status | Provider | Protocols | Status | | --- | --- | --- | | Microsoft Entra ID | SAML, OIDC, SCIM inbound | Provider-ready, missing real IdP L4. | | Okta | SAML, OIDC, SCIM inbound | Provider-ready, missing real IdP L4. | | Google Workspace | SAML, OIDC, SCIM inbound | Provider-ready, missing real IdP L4. | | OneLogin | SAML, OIDC, SCIM inbound | Provider-ready, missing real IdP L4. | | JumpCloud | SAML, OIDC, SCIM inbound | Provider-ready, missing real IdP L4. | | PingOne | SAML, OIDC | Provider-ready, missing real IdP L4. | | PingFederate | SAML, OIDC | Provider-ready, missing real IdP L4. | | AD FS | SAML, OIDC, WS-Fed legacy baseline | Implemented locally; real AD FS WS-Fed L4 missing. | | Shibboleth | SAML, OIDC | Provider-ready, missing real IdP L4. | | Keycloak | SAML, OIDC, LDAP legacy baseline | Implemented locally; native Kerberos bridge is documented-only. | ## Downstream SaaS SSO status | SaaS | Direction | Status | | --- | --- | --- | | Slack | Vonvon as SAML IdP | Provider-ready with local outbound SAML baseline; real Slack admin L4 missing. | | GitHub Enterprise Cloud | Vonvon as SAML IdP | Provider-ready with local outbound SAML baseline; real GitHub Enterprise L4 missing. | | Microsoft custom enterprise app | Vonvon as SAML or OIDC IdP | Provider-ready with generic SAML/OIDC baseline; real Entra custom app L4 missing. | | Atlassian | Vonvon as SAML IdP | Provider-ready with local outbound SAML baseline; real Atlassian admin L4 missing. | | Salesforce | Vonvon as SAML or OIDC IdP | Provider-ready with generic SAML/OIDC baseline; real Salesforce admin L4 missing. | | Zoom | Vonvon as SAML or OIDC IdP | Provider-ready with generic SAML/OIDC baseline; real Zoom admin L4 missing. | ## Legacy protocol boundaries - LDAP direct bind, WS-Federation, SWA password vaulting, header-based SSO, and directory connector framework are implemented locally with fake harness L3. Real AD/LDAP gateway, AD FS WS-Fed, target app vault, and Application Proxy L4 are still missing. - Linked sign-on, native IWA, and Kerberos termination are not supported. - SAML Single Logout is implemented and verified locally at L1/L2 for inbound and outbound SAML. The Slack profile is excluded because Slack does not support SLO; real IdP and downstream SaaS L4 evidence remains UNKNOWN. - Provider-ready rows must not be described as production-supported until real external L4 evidence exists. # Social login > Configure social OAuth providers while keeping provider-ready separate from production support. Locale: en Canonical: https://vonvon.id/social-login Markdown: https://vonvon.id/social-login/index.md Source: https://vonvon.id/social-login/index.mdx ## Provider status Social login is provider-ready until real provider callback L4 exists. Vonvon has local callback, profile mapping, nonce, policy, and organization gate coverage, but real provider secrets and callbacks are required before production-supported claims. | Provider | Protocol | Status | | --- | --- | --- | | GitHub | OAuth authorization code | Provider-ready, missing real GitHub callback L4. | | Google | OIDC authorization code | Provider-ready, missing real Google callback L4. | | Microsoft account | OIDC authorization code | Provider-ready, separate from Microsoft Entra ID. | | Apple | Sign in with Apple | Provider-ready, missing real Apple web configuration and callback L4. | ## Configuration contract - Provider credentials are stored as secret references and are never returned to the browser. - Hosted Auth only displays enabled providers that have credentials and organization policy permission. - Callbacks verify state, nonce, issuer, audience, and email policy before creating or linking users. - GitHub social login is separate from GitHub Enterprise Cloud SAML or SCIM. - Microsoft account login is separate from Microsoft Entra ID enterprise SSO. # Management API > Use scoped API keys to manage organization resources from your backend. Locale: en Canonical: https://vonvon.id/management-api Markdown: https://vonvon.id/management-api/index.md Source: https://vonvon.id/management-api/index.mdx ## Authentication Management API calls use `Authorization: Bearer sk_live_*` or `Authorization: Bearer sk_test_*`. Keys are scoped to the resolved Tenant. Organization resources additionally verify that the requested Organization belongs to that Tenant. ```shell curl https://vonvon.id/v1/users \ -H 'Authorization: Bearer sk_live_xxx' ``` ## Resources | Resource | Capabilities | Status | | --- | --- | --- | | `/v1/users` | Create, read, update, ban, unban, export, and remove users. | Implemented | | `/v1/organizations` | Manage organizations, domains, branding, and settings. | Implemented | | `/v1/organizations/:orgId/memberships` | List, create, update role, and remove members. | Implemented | | `/v1/organizations/:orgId/invitations` | Create (with bulk limit), revoke, and list invitations. | Implemented | | `/v1/sessions` | List, get, and revoke user sessions. | Implemented | | `/v1/applications` | Register OAuth clients, redirect URIs, grants, token policy, and secrets. | Implemented | | `/v1/connections` | Manage upstream enterprise SSO connections. | Implemented | | `/v1/directories` | Create SCIM directories and rotate directory tokens. | Implemented | | `/v1/roles` | Create, read, update, and delete custom roles. | Implemented | | `/v1/permissions` | Create, read, update, and delete permissions. | Implemented | | `/v1/project-grants` | Create, list, get, revoke, and delete project grants. | Implemented | | `/v1/user-grants` | Assign, list, get, revoke, and delete scoped user grants. | Implemented | | `/v1/organizations/:orgId/domains` | List, create, and remove organization domains. | Implemented | | `/v1/organizations/:orgId/custom-hostnames` | Create, inspect, refresh, and remove custom hostnames. | Implemented | | `/v1/organizations/:orgId/audit-events` | Read organization audit events with cursor pagination. | Implemented | | `/v1/webhooks` | Create, list, update, and delete webhook endpoints. | Implemented | | `/v1/api-keys` | Create, list, and revoke organization API keys. | Implemented | | `/v1/organizations/:orgId/scim-targets` | Create, update, sync, and remove outbound SCIM targets. | Implemented | ## Pagination and errors List endpoints return a data array and cursor metadata. Errors use structured JSON with a stable code, a human-readable message, and optional metadata for field validation. # Webhooks > Subscribe to implemented user, organization, membership, invitation, and SAML certificate events through signed HTTP deliveries. Locale: en Canonical: https://vonvon.id/webhooks Markdown: https://vonvon.id/webhooks/index.md Source: https://vonvon.id/webhooks/index.mdx ## Event naming Implemented events follow the pattern `.`. The vonvon-webhook-event header and body type carry the exact event name; every delivery also has a unique `svix-id`. | Object | Actions | | --- | --- | | `user` | created, updated, deleted, restored, banned, unbanned, deactivated | | `organization` | created, updated, deleted, restored | | `organization.auth_policy` | updated | | `organization.delivery_channels` | updated | | `organization.social_providers` | updated | | `organization.outbound_saml_app` | created, deleted | | `organization.scim_target` | created, deleted | | `organizationMembership` | created, updated, deleted, restored | | `organizationInvitation` | created, accepted, revoked | | `connection` | saml\_certificate\_renewed | | `user.*` | Subscription wildcard for all implemented user events. | | `organization.*` | Subscription wildcard for all implemented organization events, including dotted sub-events. | | `organizationMembership.*` | Subscription wildcard for all implemented organization membership events. | | `*` | Subscription wildcard for every implemented event. | ## Payload structure Every webhook delivery is an HTTP POST with `Content-Type: application/json`. The body contains the exact event `type` and its `data`; svix metadata is carried in request headers. ```json { "type": "user.created", "data": { "userId": "user_01abc" } } ``` ## Signature verification Vonvon signs every delivery with HMAC-SHA256. Verify the signature before processing the payload. Reject deliveries older than 5 minutes to prevent replay attacks. | Header | Description | | --- | --- | | `svix-id` | Unique message ID. Use this to deduplicate retried deliveries. | | `svix-timestamp` | Unix seconds when the message was sent. | | `svix-signature` | The svix-signature header starts with the literal v1, prefix, followed by the Base64-encoded HMAC-SHA256 of `${svix-id}.${svix-timestamp}.${raw-body}` using the endpoint signing secret. | ```ts import { verifyWebhook } from '@vonvon-kit/backend' const result = await verifyWebhook(request, { secret: env.VONVON_WEBHOOK_SECRET, }) if (!result.ok) { return new Response('Invalid webhook', { status: 400 }) } const { type, data } = result.value.payload ``` ## Retries and dead letters - Delivery attempts use exponential backoff and end in dead status. Queue-level exhaustion is persisted as an encrypted dead-letter record for Instance Manager inspection and replay. - Delivery is decoupled from the authentication path through Cloudflare Queues. A slow or unavailable endpoint does not affect login latency. - Use the `svix-id` header to deduplicate deliveries on your end. Retries carry the same `svix-id` as the original attempt. ```mermaid flowchart LR Vonvon --> Queue Queue -->|HTTPS| Endpoint Endpoint -->|2xx| ACK Endpoint -->|non-2xx| Retry Retry --> Queue Retry -->|max_retries| dlq["D1 DLQ"] ``` ## Endpoint recovery Use `POST /v1/webhooks/:id/restore` to reactivate a deleted endpoint and `POST /v1/webhooks/:id/rotate-secret` to replace its signing secret. The new `signing_secret` is returned once. Product-level replay by delivery ID or `time range` is not implemented. ```shell curl -X POST https://vonvon.id/v1/webhooks/webhook_xxx/rotate-secret \ -H 'Authorization: Bearer sk_live_xxx' ``` ## Delivery history boundary Vonvon does not expose a pull Events API. Use `GET /v1/webhooks` to manage subscriptions; delivery state and queue dead-letter replay are operational surfaces for Instance Managers, not a tenant event stream. ```shell curl 'https://vonvon.id/v1/webhooks?limit=100' \ -H 'Authorization: Bearer sk_live_xxx' ``` # Branding > Manage the seven stored organization branding fields exposed by the Management API. Hosted Auth runtime application and custom CSS are not shipped. Locale: en Canonical: https://vonvon.id/branding Markdown: https://vonvon.id/branding/index.md Source: https://vonvon.id/branding/index.mdx ## Branding options The Management API stores seven organization-scoped branding values in KV. They are available to authenticated management clients; automatic Hosted Auth and email rendering is not implemented. | Option | Description | | --- | --- | | Primary color | Stored primary color value for management clients. | | Background color | Stored page background value for management clients. | | Accent color | Stored accent color value for management clients. | | Border radius | Stored border-radius value. The API does not constrain it to a named scale. | | Font family | Stored font-family value. The API does not fetch or validate remote fonts. | | Logo (light) | Stored light-theme logo URL. Uploading an organization logo is a separate R2-backed endpoint. | | Logo (dark) | Stored dark-theme logo URL; this API does not apply an automatic fallback. | ## Per-organization branding Each record is keyed by tenant and organization in KV. Reads return all seven fields with null values when no record exists; no runtime rendering or latency guarantee is implied. ## Management API endpoint Update organization branding with a PATCH request using camelCase field names. Omitted fields and `null` currently retain the stored value; send a replacement string to change a field. ```shell curl -X PATCH https://vonvon.id/v1/organizations/org_xxx/branding \ -H 'Authorization: Bearer sk_live_xxx' \ -H 'Content-Type: application/json' \ -d '{ "primaryColor": "#6366f1", "borderRadius": "0.5rem", "fontFamily": "Inter, sans-serif", "logoUrl": "https://cdn.example.com/logo.svg" }' ``` ## Validation boundary - The branding schema accepts only the seven fields listed above; custom CSS is not accepted. - `@import` and `url()` CSS processing is not implemented because the endpoint accepts no stylesheet. - Values are stored as strings; color, radius, font, and URL syntax are not validated beyond JSON types. - Iframe preview and separate draft and published states are not implemented. ## Email template boundary Transactional email uses built-in Mustache templates and global R2 locale packs. Per-organization HTML template upload and automatic branding injection are not implemented. # SCIM API reference > SCIM 2.0 endpoint contract for provisioning users and groups into Vonvon. Locale: en Canonical: https://vonvon.id/scim Markdown: https://vonvon.id/scim/index.md Source: https://vonvon.id/scim/index.mdx ## Base URL Vonvon exposes organization-scoped SCIM 2.0 APIs under `/scim/v2/organizations/{organization_id}`. User and group resources are addressed with the organization ID in the path. Configure your identity provider with the base URL `https://vonvon.id/scim/v2/organizations/{organization_id}` and the directory token shown once when you create or rotate the directory. ```shell curl https://vonvon.id/scim/v2/organizations/{organization_id}/Users \ -H 'Authorization: Bearer scim_xxx' \ -H 'Content-Type: application/scim+json' ``` ## Authentication SCIM calls use a directory bearer token created from the Management API. Vonvon stores only a hash of the token and returns the plaintext token once when it is created or rotated. | Header | Value | Notes | | --- | --- | --- | | `Authorization` | `Bearer scim_xxx` | Required for organization-scoped user and group endpoints. | | `Content-Type` | `application/scim+json` | Required for POST, PUT, and PATCH requests. | | `Accept` | `application/scim+json` | Recommended for all SCIM clients. | ## Endpoints | Endpoint | Methods | Use | | --- | --- | --- | | `/scim/v2/ServiceProviderConfig` | `GET` | ServiceProviderConfig response for SCIM client discovery. | | `/scim/v2/Schemas` | `GET` | User and Group schema metadata. | | `/scim/v2/ResourceTypes` | `GET` | Supported SCIM resource types. | | `/scim/v2/organizations/{organization_id}/Users` | `GET, POST` | Create and list directory users. | | `/scim/v2/organizations/{organization_id}/Users/{id}` | `GET, PUT, PATCH, DELETE` | Read, replace, patch, or deprovision one directory user. | | `/scim/v2/organizations/{organization_id}/Groups` | `GET, POST` | Create and list directory groups. | | `/scim/v2/organizations/{organization_id}/Groups/{id}` | `GET, PUT, PATCH, DELETE` | Read, replace, patch, or remove one directory group. | ## User resource User resources use `userName` as the external identifier. Email values, profile name fields, active state, and enterprise extension fields are preserved in the SCIM raw profile. | SCIM field | Vonvon behavior | | --- | --- | | `id` | Stable Vonvon directory user ID. | | `userName` | Required. Must be unique inside the directory. | | `active` | `false` deprovisions the user and revokes active sessions. | | `emails` | Primary email is used for matching and account linking. | | `urn:ietf:params:scim:schemas:extension:enterprise:2.0:User` | Stored and returned for enterprise attributes such as department. | ```json { "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "userName": "alice@example.com", "active": true, "name": { "givenName": "Alice", "familyName": "Lee" }, "emails": [{ "value": "alice@example.com", "primary": true }] } ``` ## Group resource Group resources use `displayName` as the unique directory group name. Members reference SCIM user IDs and are synchronized idempotently. ```json { "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"], "displayName": "Engineering", "members": [ { "value": "dir_user_123", "display": "alice@example.com" } ] } ``` ## Filtering, sorting, and pagination Vonvon supports SCIM filter grammar with `and`, `or`, `not`, and comparison operators (`eq`, `ne`, `co`, `sw`, `ew`, `gt`, `ge`, `lt`, `le`, `pr`). Unsupported expressions return `invalidFilter`. List responses use SCIM 1-based pagination with `startIndex`, `count`, and `totalResults`, plus optional `sortBy` and `sortOrder`. | Query | Support | | --- | --- | | `filter=userName eq "alice@example.com"` | Find one user by userName. | | `filter=displayName eq "Engineering"` | Find one group by displayName. | | `filter=userName sw "alice" and active eq true` | Combine logical and comparison operators. | | `sortBy=userName&sortOrder=ascending` | Sort Users or Groups list results. | | `startIndex=1&count=100` | Return up to 100 resources from the first result. | | `attributes=userName,emails.value` | Return only schemas, id, and the requested SCIM attributes. | | `excludedAttributes=emails,meta` | Return the default resource without the excluded SCIM attributes. | ## Bulk operations `POST /scim/v2/organizations/{organization_id}/Bulk` accepts a `BulkRequest` with up to 100 operations and a 1 MiB payload limit. Responses return per-operation HTTP status in a `BulkResponse` even when some operations fail. ```json { "schemas": ["urn:ietf:params:scim:api:messages:2.0:BulkRequest"], "Operations": [ { "method": "POST", "path": "/Users", "bulkId": "user1", "data": { "userName": "alice@example.com", "active": true } } ] } ``` ## ETag and If-Match Resource GET responses include `ETag: W/""` from `meta.version`. `PUT` and `PATCH` require an `If-Match` header matching the current version; missing headers return `428` and mismatches return `412`. ## PATCH operations PATCH requests use `urn:ietf:params:scim:api:messages:2.0:PatchOp`. Vonvon supports `add`, `replace`, and `remove` for writable profile fields and group memberships. ```json { "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [ { "op": "replace", "path": "active", "value": false } ] } ``` ## Error responses SCIM errors return `application/scim+json` with the SCIM Error schema, HTTP status, detail, and optional `scimType`. ```json { "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"], "status": "409", "scimType": "uniqueness", "detail": "userName already exists" } ``` ## ServiceProviderConfig capabilities - `ServiceProviderConfig` advertises `sort.supported=true`, `bulk.supported=true`, and `etag.supported=true`. - Vonvon exposes inbound SCIM Service Provider endpoints for external IdPs. Downstream SaaS SCIM push clients have local fake-SaaS evidence, but production support still requires real SaaS admin L4. ## Deprovisioning behavior - Directory token rotation keeps the previous token valid for a short grace window. - `active=false` and `DELETE /Users/{id}` deprovision the user and revoke active sessions. - Group membership updates are idempotent. Unknown members can be resolved after the user arrives from the identity provider. # SAML SSO > Connect enterprise identity providers using SAML 2.0. Locale: en Canonical: https://vonvon.id/saml Markdown: https://vonvon.id/saml/index.md Source: https://vonvon.id/saml/index.mdx ## Service provider endpoints | Endpoint | Description | | --- | --- | | `/sso/hrd` | Find the enterprise connection for a verified email domain. | | `/sso/saml/{connection}/metadata` | SP metadata XML for the configured connection. | | `/sso/saml/{connection}/login` | SP-initiated AuthnRequest redirect to the IdP. | | `/sso/saml/{connection}/acs` | ACS endpoint for signed or encrypted SAML responses. | ## Security model SAML is provider-ready: local and fake-IdP evidence covers the current Worker routes, and production readiness still requires a real IdP connection without recording SAMLResponse values or provider secrets. - IdP certificates are stored on the connection. Browser-supplied KeyInfo is ignored. - Assertions are validated for issuer, audience, recipient, time window, InResponseTo, and replay. - EncryptedAssertion uses the configured SP decrypt key, then still requires the decrypted assertion signature to verify. - XML security prechecks, a recursive structural schema allowlist, and the signature/reference structure allowlist are implemented and verified locally. The structural allowlist validates the SAML Response before signature verification and each decrypted Assertion again; it is a closed grammar for the consumed SAML profile, not a general XSD engine. Inbound and outbound SAML Single Logout is covered locally at L1/L2. The Slack profile is excluded because Slack does not support SLO, and real provider L4 evidence remains UNKNOWN. - Vonvon currently acts as the SAML service provider for enterprise IdPs. Outbound SAML IdP for downstream SaaS apps has local fake-SaaS evidence, but production support still requires real SaaS admin L4. - JIT provisioning can create or update users when the connection policy allows it. - SCIM remains the recommended source for deprovisioning and group membership lifecycle. # SDKs > TypeScript packages and locally verified native SDKs for server, web, mobile, and desktop. Locale: en Canonical: https://vonvon.id/sdks Markdown: https://vonvon.id/sdks/index.md Source: https://vonvon.id/sdks/index.mdx ## Status legend Every SDK below carries one of two status labels, aligned with the Vonvon platform support matrix. Neither label is a production-readiness claim. | Status | Meaning | | --- | --- | | Current package | TypeScript package maintained in the Vonvon repository with source, tests, and workspace wiring. | | Implemented and verified locally | Native SDK that compiles and passes its unit test suite on a local toolchain. A round-trip against a real production IdP still requires manual verification. | ## Server-side SDKs Web-standard runtimes (Cloudflare Workers, Node.js, Bun, Deno) share @vonvon-kit/backend via Web Crypto. Other languages get native SDKs. All do networkless JWT verification, request authentication, and webhook verification. | Runtime or language | Package or directory | Status | Responsibility | | --- | --- | --- | --- | | Cloudflare Workers | `@vonvon-kit/backend` | Current package | Networkless JWT verification, request authentication, and webhook verification. | | Node.js | `@vonvon-kit/backend` | Current package | Networkless JWT verification, request authentication, and webhook verification. | | Bun | `@vonvon-kit/backend` | Current package | Networkless JWT verification, request authentication, and webhook verification. | | Deno | `@vonvon-kit/backend` | Current package | Networkless JWT verification, request authentication, and webhook verification. | | Go | `sdk/go` | Implemented and verified locally | Networkless JWT verification, request authentication, and webhook verification. | | Java | `sdk/java` | Implemented and verified locally | Networkless JWT verification, request authentication, and webhook verification. | | Rust | `sdk/rust` | Implemented and verified locally | Networkless JWT verification, request authentication, and webhook verification. | | PHP | `sdk/php` | Implemented and verified locally | Networkless JWT verification, request authentication, and webhook verification. | | Ruby | `sdk/ruby` | Implemented and verified locally | Networkless JWT verification, request authentication, and webhook verification. | | Python | `sdk/python` | Implemented and verified locally | Networkless JWT verification, request authentication, and webhook verification. | | .NET | `sdk/dotnet` | Implemented and verified locally | Networkless JWT verification, request authentication, and webhook verification. | ## Client SDKs: web frameworks | Framework | Package or directory | Status | Responsibility | | --- | --- | --- | --- | | Vanilla JS / Web | `@vonvon-kit/core` | Current package | Browser client, session store, token cache, and Management API helpers. | | React | `@vonvon-kit/react` | Current package | Provider, hooks, control components, user UI, and organization UI. | | Next.js | `@vonvon-kit/nextjs` | Current package | Middleware, App Router helpers, Pages Router helpers, and React re-exports. | | Vue | `@vonvon-kit/vue` | Current package | Provider, hooks or composables, and prebuilt user and organization components. | | Nuxt | `@vonvon-kit/nuxt` | Current package | Provider, hooks or composables, and prebuilt user and organization components. | | Svelte / SvelteKit | `@vonvon-kit/svelte` | Current package | Provider, hooks or composables, and prebuilt user and organization components. | | Angular | `@vonvon-kit/angular` | Current package | Provider, hooks or composables, and prebuilt user and organization components. | | Remix | `@vonvon-kit/remix` | Current package | Provider, hooks or composables, and prebuilt user and organization components. | | Astro | `@vonvon-kit/astro` | Current package | Provider, hooks or composables, and prebuilt user and organization components. | | SolidJS | `@vonvon-kit/solid` | Current package | Provider, hooks or composables, and prebuilt user and organization components. | ## Client SDKs: mobile | Platform | Package or directory | Status | Responsibility | | --- | --- | --- | --- | | React Native | `@vonvon-kit/react-native` | Current package | Hosted redirect, deep link callback, PKCE S256, and secure storage adapter contract. | | Expo | `@vonvon-kit/expo` | Current package | Expo Router integration, AuthSession, and SecureStore adapter. | | Flutter | `sdk/flutter` | Implemented and verified locally | Hosted redirect, app link callback, and secure storage adapter. | | iOS | `sdk/ios` | Implemented and verified locally | ASWebAuthenticationSession, Keychain storage, and PKCE S256. | | Android | `sdk/android` | Implemented and verified locally | Custom Tabs, App Links, Keystore-backed storage, and PKCE S256. | ## Client SDKs: desktop | Platform | Package or directory | Status | Responsibility | | --- | --- | --- | --- | | macOS | `sdk/macos` | Implemented and verified locally | ASWebAuthenticationSession, Keychain storage, and PKCE S256. | | Windows | `sdk/windows` | Implemented and verified locally | WebView2 redirect, DPAPI secure storage, and PKCE S256. | | Linux | `sdk/linux` | Implemented and verified locally | System browser redirect, Secret Service storage, and PKCE S256. | | Electron | `@vonvon-kit/electron` | Current package | Main and renderer bridge, safeStorage, and loopback or custom scheme callbacks. | | Tauri | `@vonvon-kit/tauri` | Current package | Rust backend bridge, OS keychain, and PKCE S256. | ## Shared native contract - Native public clients use authorization code with PKCE S256. Implicit flow and password grant are not supported. - Redirect callbacks are claimed app links, universal links, custom schemes, or platform equivalent callback URLs. - Storage adapters keep refresh tokens or session cache values in platform secure storage and never store client secrets. - JS and TS native SDKs (React Native, Expo) use a React provider plus hooks model: VonvonProvider, useSignIn, and useSignOut. - Other native SDKs (iOS, Android, Flutter, desktop) use a functional configure, signIn, and handleRedirect API surface. ## Package guides Each SDK has a dedicated reference page with install steps, API tables, and copy-paste examples. The SDK packages group in the sidebar lists every reference page; the most used web packages are: - [@vonvon-kit/core](/sdks/core) — browser session, tokens, Management API helpers. - [@vonvon-kit/backend](/sdks/backend) — networkless JWT verify, request auth, webhooks. - [@vonvon-kit/react](/sdks/react) — provider, hooks, control and UI components. - [@vonvon-kit/nextjs](/sdks/nextjs) — middleware and App/Pages Router server helpers. - [@vonvon-kit/react-native](/sdks/react-native) — Hosted Auth redirect, PKCE S256, secure token storage for native apps. # @vonvon-kit/core > Browser client for session state, short-lived JWT access, and Management API helpers. Locale: en Canonical: https://vonvon.id/sdks/core Markdown: https://vonvon.id/sdks/core/index.md Source: https://vonvon.id/sdks/core/index.mdx ## Install and configure Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry. For a developer app on another origin, configure `mode: 'oidc'` with issuer, clientId, and an exact redirectUri. Use same-origin mode only when the application routes Core auth endpoints on its exact origin. Vonvon has no publishable-key credential. ```ts import { VonvonClient } from '@vonvon-kit/core' const vonvon = new VonvonClient({ mode: 'oidc', issuer: 'https://vonvon.id', clientId: 'client_abc123', redirectUri: 'https://app.example.com/auth/callback', }) const authorization = await vonvon.createAuthorizationUrl({ returnUrl: '/dashboard' }) if (!authorization.ok) throw new Error(authorization.error.message) window.location.assign(authorization.value) ``` ## Session lifecycle - `load()` reads `/v1/me` and hydrates user, session, and active organization. - `signInPassword()` establishes a cookie session through Hosted Auth password flow. - `getToken()` returns a short-lived JWT for API calls. Verify networklessly on your backend with JWKS. - `setActiveOrganization()` switches org context and clears the token cache before reloading state. - `signOut()` revokes the browser session cookie. ## Management API helpers Management API helpers are `server-side or exact same-origin only`. Construct a separate client with `sk_live_*` or sk\_test\_\* on the server; OIDC browser mode rejects these operations. ```ts const management = new VonvonClient({ apiUrl: 'https://vonvon.id', secretKey: process.env.VONVON_SECRET_KEY, }) const keys = await management.listApiKeys() if (!keys.ok) throw new Error(keys.error.message) const created = await management.createApiKey({ name: 'CI deploy', scopes: ['read'] }) if (!created.ok) throw new Error(created.error.message) const revoked = await management.revokeApiKey({ id: created.value.id }) if (!revoked.ok) throw new Error(revoked.error.message) ``` ## Exported API | Export | Kind | Purpose | | --- | --- | --- | | `VonvonClient` | class | Top-level browser client: load, signIn, getToken, setActiveOrganization, signOut, and Management API helpers | | `VonvonStore` | class | Framework-agnostic reactive store; subscribe with useSyncExternalStore in framework bindings | | `TokenManager` | class | Short-lived JWT cache and scheduled refresh (advanced use and testing) | | `VonvonApiClient` | class | HTTP client for /v1/me and token endpoints | | `VonvonNetworkError` | class | Thrown on transport failures: network error, non-JSON response, 5xx with no structured body | | `makeVonvonError` | function | Construct a structured VonvonError for local validation failures without a network round-trip | | `isVonvonErrorShape` | function | Type guard: checks whether an unknown value conforms to VonvonError shape from the wire | | `decodeTokenClaims` | function | Decode JWT payload claims for expiry scheduling only; does not verify the signature | | `isTokenExpiring` | function | Returns true when the token expires within the leeway window (default 10 s) | | `SESSION_STATUS` | as const tuple | Valid session status values: active, pending, expired, removed, ended, revoked | | `CLIENT_STATUS` | as const tuple | Valid client status values: loading, ready, degraded, error | | `PACKAGE` | string constant | Package name identifier '@vonvon-kit/core' | ## Types | Type | Description | | --- | --- | | `VonvonUser` | Read-only view of the authenticated user (no secrets or hashes) | | `VonvonOrganization` | Public organization view | | `VonvonOrganizationMembership` | User membership in an org with role and permissions | | `VonvonSession` | Session view including status, expiry, and active org | | `VonvonApiKey` | API key without secret (list view) | | `VonvonApiKeyWithSecret` | API key returned once at creation; includes the key field | | `VonvonPage` | Cursor-paginated response envelope | | `CreateApiKeyInput` | Input for createApiKey | | `SignInPasswordInput` | Input for signInPassword | | `SignInResult` | Result from signInPassword: next step or redirect URL | | `SessionStatus` | Union of SESSION\_STATUS values | | `ClientStatus` | Union of CLIENT\_STATUS values | | `VonvonState` | Full SDK state snapshot subscribed from VonvonStore | | `VonvonStateListener` | State change listener callback type | | `Unsubscribe` | Return type of VonvonStore.subscribe | | `GetTokenOptions` | Options for getToken: skipCache, leewaySeconds, signal | | `VonvonClientOptions` | Discriminated VonvonClient constructor options: same-origin accepts apiUrl, secretKey, fetcher, and now; oidc requires issuer, clientId, and redirectUri. | | `TokenResponse` | Raw token endpoint response shape | | `ClientStateResponse` | Raw /v1/me response shape | | `DecodedTokenClaims` | JWT payload claims returned by decodeTokenClaims | ## Related docs Framework bindings: [@vonvon-kit/react](/sdks/react). Server verification: [@vonvon-kit/backend](/sdks/backend). # @vonvon-kit/backend > Networkless JWT verification, request authentication, and webhook signature validation for edge and server runtimes. Locale: en Canonical: https://vonvon.id/sdks/backend Markdown: https://vonvon.id/sdks/backend/index.md Source: https://vonvon.id/sdks/backend/index.mdx ## Runtime support Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry. - Cloudflare Workers (primary target) - Vercel Edge Runtime and Node.js server runtimes - Any Web Crypto compatible runtime (Bun, Deno) ## authenticateRequest Verify a Bearer or explicit application JWT from an incoming `Request`. A same-origin Core browser session is first exchanged through `/v1/sessions/token`; its opaque refresh cookie is never verified locally. ```ts import { authenticateRequest } from '@vonvon-kit/backend' const state = await authenticateRequest(request, { jwtKey: env.VONVON_JWKS_PUBLIC_KEY, issuer: 'https://vonvon.id', sessionTokenExchange: { endpoint: '/v1/sessions/token' }, }) if (state.isSignedIn) { console.log(state.userId) } ``` ## verifyToken Low-level access token verification. Pass `jwtKey` from JWKS to skip network round-trips on cold start. Expected failures return a Result type, not an exception. ```ts import { verifyToken } from '@vonvon-kit/backend' const result = await verifyToken(token, { jwtKey: env.VONVON_JWKS_PUBLIC_KEY, issuer: 'https://vonvon.id', audience: 'my-api', }) if (!result.ok) return new Response('Unauthorized', { status: 401 }) ``` ## verifyWebhook Validates Svix-style webhook signatures (`svix-id`, `svix-timestamp`, `svix-signature`) with a five-minute replay window. ```ts import { verifyWebhook } from '@vonvon-kit/backend' const result = await verifyWebhook(request, { secret: env.VONVON_WEBHOOK_SECRET, }) if (!result.ok) { return new Response('Invalid webhook', { status: 400 }) } const { type, data } = result.value.payload ``` ## Exported API | Export | Kind | Purpose | | --- | --- | --- | | `authenticateRequest` | function | Verify Bearer or explicit app JWT credentials, with optional same-origin Core session exchange | | `exchangeSessionToken` | function | Forward Core opaque cookies only to an exact same-origin session-token endpoint; the value is never verified locally | | `verifyToken` | function | Low-level access token verification: signature, exp, nbf, iss, aud, azp | | `verifyWebhook` | function | Svix-style HMAC-SHA256 webhook signature validation with 5-minute replay window | | `toVerifyKeySet` | function | Convert JwtKey (JWK, JWKS, or CryptoKey) to VerifyKeySet for verification | | `JwksCache` | class | Optional network-fetching JWKS cache with configurable TTL (default 3600 s); use only when jwtKey is not pre-loaded | | `AppError` | class | Thrown for unrecoverable SDK errors: missing JWT key, JWKS fetch failure, invalid options, session-token exchange failure | | `BACKEND_ERROR_CODES` | as const tuple | All BackendErrorCode values: missing\_jwt\_key, jwks\_fetch\_failed, invalid\_options, session\_token\_exchange\_failed | | `PACKAGE` | string constant | Package name identifier '@vonvon-kit/backend' | ## Types | Type | Description | | --- | --- | | `JwtKey` | Accepted public key forms: PublicJwk, Jwks, or { alg, publicKey: CryptoKey } | | `JwksCacheOptions` | Constructor options for JwksCache: jwksUri, ttlSec, fetchFn | | `VerifyTokenOptions` | Options for verifyToken: jwtKey, issuer, audience, authorizedParties, clockToleranceSec, now | | `VerifyTokenError` | Structured error returned when token verification fails (expected failure; not thrown) | | `AuthenticateRequestOptions` | Options for authenticateRequest: jwtKey, issuer, audience, authorizedParties, clockToleranceSec, now, jwtCookieName, sessionTokenExchange | | `RequestState` | Discriminated union of SignedInState and SignedOutState | | `SignedInState` | Valid signed JWT state with userId, optional sessionId, and verified claims | | `SignedOutState` | No valid token present; reason field indicates cause | | `VerifyWebhookOptions` | Options for verifyWebhook: secret, toleranceSec (replay window seconds) | | `WebhookVerifyError` | Structured error for missing headers, invalid signatures, replay, or invalid payloads | | `VerifiedWebhook` | Verified message metadata and a typed type/data payload envelope | | `BackendErrorCode` | Union of BACKEND\_ERROR\_CODES values | ## Security boundaries - Uses public JWKS only. Never loads instance signing private keys. - Verification uses Web Crypto via @vonvon-kit/crypto. - Expected failures return Result types; unexpected errors throw AppError. # @vonvon-kit/react > React 19 provider, hooks, control components, and hosted UI building blocks for customer applications. Locale: en Canonical: https://vonvon.id/sdks/react Markdown: https://vonvon.id/sdks/react/index.md Source: https://vonvon.id/sdks/react/index.mdx ## Provider setup Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry. ```tsx import { VonvonProvider } from '@vonvon-kit/react' export function AppProviders({ children }: { children: React.ReactNode }) { return ( {children} ) } ``` ## Hooks | Hook | Returns | | --- | --- | | `useAuth` | isLoaded, isSignedIn, userId, signOut, getToken | | `useUser` | isLoaded, isSignedIn, user: VonvonUser \| null | | `useSession` | isLoaded, isSignedIn, session: VonvonSession \| null | | `useSessionList` | isLoaded, sessions, setActive for multi-session switcher | | `useSignIn` | Hosted Auth sign-in flow helpers | | `useOrganization` | isLoaded, organization: VonvonOrganization \| null, membership | | `useOrganizationList` | isLoaded, userMemberships for organization switcher UI | | `useAPIKeys` | Management API key list and actions for console embeds | | `useVonvonContext` | Raw VonvonContextValue (advanced use; prefer typed hooks above) | ## Control components | Component | Props type | Purpose | | --- | --- | --- | | `SignedIn` | `SignedInProps` | Renders children only when the session is loaded and signed in | | `SignedOut` | `SignedOutProps` | Renders children only when loaded and not signed in | | `Protect` | `ProtectProps` | Permission and role gate with optional fallback UI | | `VonvonLoaded` | `VonvonLoadedProps` | Renders children after SDK hydration completes (isLoaded true) | | `VonvonLoading` | `VonvonLoadingProps` | Renders children while SDK is still loading (isLoaded false) | | `VonvonFailed` | `VonvonFailedProps` | Renders children when SDK load fails with an unrecoverable error (status error) | | `VonvonDegraded` | `VonvonDegradedProps` | Renders children when SDK is loaded but in degraded state (status degraded) | | `AuthenticateWithRedirectCallback` | `AuthenticateWithRedirectCallbackProps` | OAuth redirect callback handler: reloads session state and redirects to afterSignInUrl or calls onSuccess | | `RedirectToSignIn` | `RedirectToSignInProps` | Mounts and immediately redirects to the sign-in page | | `RedirectToSignUp` | `RedirectToSignUpProps` | Mounts and immediately redirects to the sign-up page | | `RedirectToUserProfile` | `RedirectToUserProfileProps` | Mounts and immediately redirects to the user profile page | | `RedirectToOrganizationProfile` | `RedirectToOrganizationProfileProps` | Mounts and immediately redirects to the organization profile page | | `RedirectToCreateOrganization` | `RedirectToCreateOrganizationProps` | Mounts and immediately redirects to the create organization page | | `SignInButton` | `SignInButtonProps` | Unstyled button that navigates to the sign-in URL on click | | `SignUpButton` | `SignUpButtonProps` | Unstyled button that navigates to the sign-up URL on click | | `SignOutButton` | `SignOutButtonProps` | Unstyled button that calls signOut on click | ## UI components | Component | Use | | --- | --- | | `SignIn` | Embedded sign-in panel (Hosted Auth) | | `SignUp` | Embedded sign-up panel | | `UserAvatar` | User avatar image with fallback initials | | `UserButton` | Avatar menu with session switch and sign-out | | `UserProfile` | Account security and profile management | | `OrganizationSwitcher` | Active org selector | | `OrganizationProfile` | Members, roles, SSO admin embed | | `CreateOrganization` | Org creation flow | | `OrganizationList` | List of user organizations for switcher UI | ## Not yet implemented The following are on the public roadmap but are not exported from the current package. Do not assume availability in this version. | Symbol | Design commitment | | --- | --- | | `GoogleOneTap` | One-tap sign-in embed | | `Waitlist` | Waitlist sign-up gate component | | Billing components | Usage and plan management embeds | | `useReverification` | Step-up authentication hook | ## Appearance Pass `appearance` to prebuilt UI components such as `SignIn`, `UserProfile`, or `OrganizationSwitcher`, not to `VonvonProvider`. The exported Appearance types provide typed CSS-variable and element-class overrides; all visible copy uses Lingui runtime descriptors. # @vonvon-kit/nextjs > Next.js middleware, App Router and Pages Router server helpers, plus React SDK re-exports. Locale: en Canonical: https://vonvon.id/sdks/nextjs Markdown: https://vonvon.id/sdks/nextjs/index.md Source: https://vonvon.id/sdks/nextjs/index.mdx ## Middleware Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry. `vonvonMiddleware()` verifies a Bearer or explicit application JWT on the Edge Runtime. For a same-origin Core session, it exchanges the opaque cookie through `/v1/sessions/token`, verifies the returned JWT, and injects auth state into downstream server request headers. ```ts import { vonvonMiddleware } from '@vonvon-kit/nextjs' export default vonvonMiddleware({ jwtKey: JSON.parse(process.env.VONVON_JWKS_PUBLIC_KEY!), issuer: 'https://vonvon.id', // This same-origin path must be routed to Vonvon Core. sessionTokenExchange: { endpoint: '/v1/sessions/token' }, }) export const config = { matcher: ['/dashboard(.*)', '/api/protected(.*)'], } ``` ## App Router server helpers ```ts import { auth, currentUser } from '@vonvon-kit/nextjs' export default async function DashboardPage() { const { userId } = await auth() if (!userId) return null const user = await currentUser() return

Welcome {user?.email}

} ``` ## Pages Router ```ts import { getAuth } from '@vonvon-kit/nextjs' export const getServerSideProps = async (ctx) => { const { userId } = await getAuth(ctx.req) if (!userId) return { redirect: { destination: '/sign-in', permanent: false } } return { props: {} } } ``` ## Exported API | Export | Kind | Purpose | | --- | --- | --- | | `vonvonMiddleware` | function | Edge Runtime middleware: verifies JWT, injects auth headers | | `auth` | function | App Router: returns AuthObject with userId, orgId, orgRole | | `getAuth` | function | Pages Router: returns AuthObject from IncomingMessage | | `currentUser` | function | App Router: fetches full user object using server auth context | | `vonvonClient` | function | Returns server-side VonvonApiClient bound to current request auth | | `VONVON_AUTH_HEADER` | string constant | Header name for auth state between middleware and server components | | `VonvonMiddlewareOptions` | type | Options for vonvonMiddleware: jwtKey, issuer, jwtCookieName, sessionTokenExchange, protectedRoutes, publicRoutes | | `VonvonServerClientOptions` | type | Options for vonvonClient | | `AuthObject` | type | Authenticated state: userId, orgId, orgRole, sessionId | | `UnauthenticatedAuthObject` | type | Unauthenticated state with null fields | | `AuthResult` | type | Union of AuthObject and UnauthenticatedAuthObject | | `PaginationParams` | type | Cursor and limit params for Management API list calls | | `PaginatedResponse` | type | Paginated response envelope | ## Re-exports - Re-exports all [@vonvon-kit/react](/sdks/react) client components and hooks via `export * from "@vonvon-kit/react"`. - Re-exports from [@vonvon-kit/backend](/sdks/backend): `verifyToken`, `verifyWebhook`, `authenticateRequest`, `JwksCache`, `toVerifyKeySet`, `AppError`, `BACKEND_ERROR_CODES`, and all their associated types. - Server helpers never expose signing secrets to client bundles. # @vonvon-kit/react-native > React Native provider and hooks for Hosted Auth redirect, PKCE S256, deep-link callback, and secure token storage adapters. Locale: en Canonical: https://vonvon.id/sdks/react-native Markdown: https://vonvon.id/sdks/react-native/index.md Source: https://vonvon.id/sdks/react-native/index.mdx ## Status Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry. Package status is **Current package**. It implements a native token-session contract: Hosted Auth redirect with PKCE S256, state and nonce validation on the deep-link callback, verified ID token claims, authorization code exchange, and secure session persistence through an injected storage adapter. A real IdP round-trip on production infrastructure is still pending manual verification. This page documents implemented behavior; it is not a readiness claim. ## Provider setup Inject a `TokenCache` (platform secure storage) and a `BrowserInterface` (in-app browser) into `VonvonProvider`. The SDK does not hard-bind any native module; Expo apps can use the ready-made adapters from [@vonvon-kit/expo](/sdks/expo). ```tsx import { VonvonProvider } from '@vonvon-kit/react-native' import type { BrowserInterface, TokenCache } from '@vonvon-kit/react-native' import * as Keychain from 'react-native-keychain' const tokenCache: TokenCache = { async getToken(key) { const result = await Keychain.getGenericPassword({ service: key }) return result ? result.password : null }, async saveToken(key, value) { await Keychain.setGenericPassword('vonvon', value, { service: key }) }, async deleteToken(key) { await Keychain.resetGenericPassword({ service: key }) }, } const browser: BrowserInterface = { async openAuthSession(url, redirectUri) { // Open url with your in-app browser library, wait for the redirectUri // deep link, then return { type: 'success', url } or { type: 'cancel' }. throw new Error('Implement with your preferred in-app browser library.') }, } export function App() { return ( ) } ``` ## Sign in `signIn()` builds the PKCE S256 authorize URL, stores the verifier, OAuth state, and nonce in the token cache, opens the browser adapter, and exchanges the returned code for a verified native session. Browser failure, state mismatch, ID token verification, and token exchange errors surface as `signInState.status === 'error'`. ```tsx import { useSignIn } from '@vonvon-kit/react-native' function SignInScreen() { const { signIn, signInState } = useSignIn() return ( ) } ``` ## createOrganization and createSession ```tsx const org = createOrganization() // org() is CreateOrganizationReturn if (org().isSignedIn) { console.log(org().organization?.name, org().membership?.role) const activated = await org().setActive('org_new_id') if (!activated.ok) throw new Error(activated.error.message) } const session = createSession() if (session().isSignedIn) { const token = await session().getToken() if (!token.ok) throw new Error(token.error.message) // use token.value for backend requests } ``` ## Headless components ```tsx import { SignInButton, SignOutButton, Protect } from '@vonvon-kit/solid' // Navigates to /sign-in by default Log in // Signs out all sessions; pass sessionId to target one Log out // Role and permission gate Admins only

}>
``` ## Exported API | Export | Kind | Purpose | | --- | --- | --- | | `VonvonProvider` | component | Creates VonvonClient, calls load() on mount, tears down via onCleanup | | `createAuth` | primitive | isLoaded, isSignedIn, userId, sessionId, session, getToken, signOut as Accessors | | `createUser` | primitive | Accessor wrapping discriminated union on isLoaded / isSignedIn / user | | `createOrganization` | primitive | Accessor returning organization, membership, and setActive for the active org | | `createSession` | primitive | Accessor returning session and getToken for the active session | | `SignInButton` | component | Headless button navigating to the sign-in URL on click | | `SignOutButton` | component | Headless button that calls signOut on click | | `Protect` | component | Role and permission gate with fallback prop | ## Token storage - The Vonvon Worker keeps an opaque refresh credential in an `HttpOnly` cookie. It is not a JWT, and the SDK never reads it from JavaScript or stores it in `localStorage`. - `getToken()` returns a short-lived JWT (60 s) cached only in memory by `TokenManager` in `@vonvon-kit/core`. # @vonvon-kit/angular > Angular 17+ standalone provider, RxJS service, functional route guards, and standalone components on top of @vonvon-kit/core. Locale: en Canonical: https://vonvon.id/sdks/angular Markdown: https://vonvon.id/sdks/angular/index.md Source: https://vonvon.id/sdks/angular/index.mdx ## Status Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry. Package status is **Current package**. Angular 17+ standalone provider, RxJS service, functional guards, and standalone components are implemented. A real IdP round-trip on production infrastructure is still pending manual verification. ## Provider setup Call `provideVonvon` in `app.config.ts`. It registers the `VONVON_CLIENT` injection token and wires an `APP_INITIALIZER` that calls `client.load()` on bootstrap. No NgModule required. ```ts // app.config.ts import { ApplicationConfig } from '@angular/core' import { provideVonvon } from '@vonvon-kit/angular' export const appConfig: ApplicationConfig = { providers: [ // Core auth endpoints must be served on this application's exact origin. provideVonvon({ mode: 'same-origin' }), ], } ``` ## VonvonAuthService ```ts import { Component, inject } from '@angular/core' import { AsyncPipe } from '@angular/common' import { VonvonAuthService } from '@vonvon-kit/angular' @Component({ selector: 'app-header', standalone: true, imports: [AsyncPipe], template: ` @if (auth.isLoaded$ | async) { @if (auth.isSignedIn$ | async) { {{ (auth.user$ | async)?.primaryEmailAddress }} } @else { Sign in } } `, }) export class HeaderComponent { readonly auth = inject(VonvonAuthService) } ``` ## Angular signals bridge Use `toSignal()` from `@angular/core/rxjs-interop` to convert observables to signals for template reads. ```ts import { toSignal } from '@angular/core/rxjs-interop' import { inject } from '@angular/core' import { VonvonAuthService } from '@vonvon-kit/angular' @Component({ standalone: true, ... }) export class MyComponent { readonly #auth = inject(VonvonAuthService) readonly user = toSignal(this.#auth.user$, { initialValue: null }) } ``` ## Route guards ```ts import { Routes } from '@angular/router' import { authGuard, hasOrganizationGuard, hasPermissionGuard } from '@vonvon-kit/angular' export const routes: Routes = [ { path: 'dashboard', canActivate: [authGuard()], loadComponent: () => import('./dashboard.component').then(m => m.DashboardComponent), }, { path: 'admin', canActivate: [hasPermissionGuard('org:settings:write')], loadComponent: () => import('./admin.component').then(m => m.AdminComponent), }, ] ``` ## Standalone components ```html Log in Log out ``` ## Exported API | Export | Kind | Purpose | | --- | --- | --- | | `provideVonvon` | function | Registers VONVON\_CLIENT singleton and APP\_INITIALIZER; accepts ProvideVonvonOptions | | `VONVON_CLIENT` | InjectionToken | The raw VonvonClient instance; prefer VonvonAuthService for most use cases | | `VonvonAuthService` | service | RxJS observables: state$, isLoaded$, isSignedIn$, user$, session$, organization$; plus getToken, signOut, setActiveOrganization, setActiveSession | | `authGuard` | CanActivateFn factory | Redirects to /sign-in when user is not signed in | | `hasOrganizationGuard` | CanActivateFn factory | Redirects when no active organization context is present | | `hasPermissionGuard` | CanActivateFn factory | Redirects when user lacks the specified permission | | `SignInButton` | standalone component | Selector vonvon-sign-in-button; inputs signInUrl, redirectUrl, ariaLabel | | `SignOutButton` | standalone component | Selector vonvon-sign-out-button; inputs sessionId, redirectUrl, ariaLabel; shows aria-busy while signing out | # @vonvon-kit/astro > Astro integration with SSR middleware, server helpers, and island client singleton for static and server-rendered Astro sites. Locale: en Canonical: https://vonvon.id/sdks/astro Markdown: https://vonvon.id/sdks/astro/index.md Source: https://vonvon.id/sdks/astro/index.mdx ## Status Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry. Package status is **Current package**. The Astro integration, SSR middleware, server helpers, and island client singleton are implemented. A real IdP round-trip on production infrastructure is still pending manual verification. ## Integration setup Register `vonvonIntegration` in `astro.config.mjs`. The integration injects server middleware with `addMiddleware` at order `pre`, keeps verification material server-side, and exposes only the serializable browser mode configuration required by client islands. ```js // astro.config.mjs import { defineConfig } from 'astro/config' import { vonvonIntegration } from '@vonvon-kit/astro' import node from '@astrojs/node' const jwtKeyJson = process.env.VONVON_JWKS_PUBLIC_KEY if (!jwtKeyJson) throw new Error('Missing VONVON_JWKS_PUBLIC_KEY') export default defineConfig({ output: 'server', adapter: node({ mode: 'standalone' }), integrations: [ vonvonIntegration({ // This combined SSR setup requires Core routes on the application's exact origin. browser: { mode: 'same-origin' }, jwtKey: JSON.parse(jwtKeyJson), issuer: 'https://app.example.com', sessionTokenExchange: { endpoint: '/v1/sessions/token' }, protectedRoutes: ['/dashboard', '/account'], signInUrl: '/sign-in', }), ], }) ``` ## Manual middleware (alternative) ```ts // src/middleware.ts import { sequence } from 'astro:middleware' import { createVonvonMiddleware } from '@vonvon-kit/astro' export const onRequest = sequence( createVonvonMiddleware({ jwtKey: JSON.parse(import.meta.env.VONVON_JWKS_PUBLIC_KEY), issuer: 'https://vonvon.id', sessionTokenExchange: { endpoint: '/v1/sessions/token' }, protectedRoutes: ['/dashboard', '/account'], signInUrl: '/sign-in', }), ) ``` ## Server-side auth in .astro pages ```astro --- // src/pages/dashboard.astro import { getAuth, currentUser } from '@vonvon-kit/astro/server' const auth = getAuth(Astro.locals) if (!auth.userId) return Astro.redirect('/sign-in') const user = await currentUser(Astro.locals, { secretKey: import.meta.env.VONVON_SECRET_KEY, }) ---

Welcome, {user?.primaryEmailAddress}

``` ## Client island ```tsx // src/components/SignOutButton.tsx import { getClient } from '@vonvon-kit/astro/client' export default function SignOutButton() { const client = getClient() const handleSignOut = async () => { const result = await client.signOut() if (!result.ok) throw new Error(result.error.message) window.location.href = '/' } return } ``` ## Astro.locals typing Add the type reference to `src/env.d.ts` to get full type coverage on `Astro.locals.vonvonAuth`. ```ts import '@vonvon-kit/astro/locals' ``` ## Exported API | Export | Kind | Module | | --- | --- | --- | | `vonvonIntegration` | Astro integration factory | `@vonvon-kit/astro` | | `createVonvonMiddleware` | middleware factory | `@vonvon-kit/astro` | | `getAuth, currentUser, vonvonClient` | server helpers | `@vonvon-kit/astro/server` | | `getClient, initClient, resetClient` | island client singleton | `@vonvon-kit/astro/client` | ## Security notes - `jwtKey` is a JWKS public key; it is safe for networkless verification and does not contain a private key. - `secretKey` (`sk_live_xxx`) must stay server-side only; never pass it to an island or client bundle. - Protected routes redirect via `Response.redirect` in middleware before any page handler runs; no client-side JS is required. - Forward Core opaque cookies only to an exact same-origin session-token endpoint; the value is never verified locally # @vonvon-kit/remix > Remix loader and action server helpers, cookie session storage, and OAuth callback handler with React SDK re-exports. Locale: en Canonical: https://vonvon.id/sdks/remix Markdown: https://vonvon.id/sdks/remix/index.md Source: https://vonvon.id/sdks/remix/index.mdx ## Status Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry. Package status is **Current package**. Remix loader/action auth helpers, cookie session integration, and OAuth callback handler are implemented. A real IdP round-trip on production infrastructure is still pending manual verification. ## Session storage setup ```ts // app/sessions.server.ts import { createVonvonSessionStorage } from '@vonvon-kit/remix' export const sessionStorage = createVonvonSessionStorage({ secret: process.env.SESSION_SECRET!, // required: cookie signing secret // cookieName: '__vonvon_session', maxAge: 2592000, secure: true }) ``` ## Reading auth in loaders `getAuth` verifies a Bearer token, explicit application JWT cookie, optional same-origin Core exchange, or the configured Remix session JWT, then returns an `AuthResult`. `requireAuth` throws a 302 redirect to `redirectPath` when unauthenticated. ```ts import { getAuth, requireAuth } from '@vonvon-kit/remix' import { json, redirect } from '@remix-run/node' import type { LoaderFunctionArgs } from '@remix-run/node' import { sessionStorage } from '~/sessions.server' const jwtKey = JSON.parse(process.env.VONVON_JWKS_PUBLIC_KEY!) const authOptions = { jwtKey, sessionStorage, sessionTokenExchange: { endpoint: '/v1/sessions/token' }, } // Optional check export async function loader({ request }: LoaderFunctionArgs) { const auth = await getAuth(request, authOptions) if (!auth.userId) return redirect('/login') return json({ userId: auth.userId, orgId: auth.orgId }) } // Guard: throws redirect automatically when unauthenticated export async function protectedLoader({ request }: LoaderFunctionArgs) { const auth = await requireAuth(request, authOptions, { redirectPath: '/login' }) return json({ userId: auth.userId }) } ``` ## OAuth callback handler `handleCallback` validates the `state` parameter to prevent CSRF, exchanges the authorization code, and returns a `Response` with `Set-Cookie`. ```ts // app/routes/auth.callback.ts import { handleCallback } from '@vonvon-kit/remix' import type { ActionFunctionArgs } from '@remix-run/node' import { sessionStorage } from '~/sessions.server' export async function action({ request }: ActionFunctionArgs) { const result = await handleCallback(request, { clientId: process.env.VONVON_CLIENT_ID!, redirectUri: process.env.VONVON_REDIRECT_URI!, sessionStorage, defaultReturnTo: '/dashboard', }) if (!result.ok) throw new Response(result.error, { status: 400 }) return result.response // 302 redirect + Set-Cookie } ``` ## Client provider (root.tsx) ```tsx import { VonvonProvider } from '@vonvon-kit/remix' // re-export from @vonvon-kit/react import { Outlet } from '@remix-run/react' export default function App() { return ( ) } ``` ## Management API client ```ts import { vonvonClient } from '@vonvon-kit/remix' const client = vonvonClient({ secretKey: process.env.VONVON_SECRET_KEY! }) export async function loader() { const result = await client.getUser('user_abc') if (!result.ok) throw new Response(result.error.message, { status: result.error.status }) return json(result.value) } ``` ## Exported API | Export | Kind | Purpose | | --- | --- | --- | | `createVonvonSessionStorage` | function | Remix cookie session storage for Vonvon tokens | | `getAuth` | function | Verify JWT or session token; returns AuthResult | | `requireAuth` | function | Like getAuth but throws a redirect response when unauthenticated | | `handleCallback` | function | OAuth callback: validates state, exchanges code, sets session cookie | | `vonvonClient` | function | Returns a server-side Management API client bound to the secret key | | `getTokenFromSession, setTokensInSession, clearTokensFromSession` | functions | Low-level token helpers for custom session handling | ## Re-exports Re-exports all [@vonvon-kit/react](/sdks/react) client components and hooks. `root.tsx` needs only one import for both provider and client components. ## PKCE and security - Public clients use Authorization Code with PKCE S256. No client secret is stored. - `handleCallback` validates the `state` parameter against the session to prevent CSRF. - Access tokens are stored in `HttpOnly` session cookies; they are never written to `localStorage`. # @vonvon-kit/expo > Expo SecureStore and WebBrowser adapters for @vonvon-kit/react-native plus an Expo Router route guard hook. Locale: en Canonical: https://vonvon.id/sdks/expo Markdown: https://vonvon.id/sdks/expo/index.md Source: https://vonvon.id/sdks/expo/index.mdx ## Status Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry. Package status is **Current package**. Expo SecureStore and WebBrowser adapters, VonvonProvider, and Expo Router route guard hook are implemented. A real IdP round-trip on production infrastructure is still pending manual verification. All PKCE logic, token exchange, and session management live in [@vonvon-kit/react-native](/sdks/react-native); this package provides adapters and Expo Router integration only. ## Provider setup Inject `createSecureStoreAdapter` and `createExpoWebBrowserAdapter` into `VonvonProvider`. No top-level Expo module import is needed; adapters accept module instances so CI typecheck stays clean without Expo peer deps installed. ```tsx import * as SecureStore from 'expo-secure-store' import * as WebBrowser from 'expo-web-browser' import { VonvonProvider, createSecureStoreAdapter, createExpoWebBrowserAdapter } from '@vonvon-kit/expo' const tokenCache = createSecureStoreAdapter({ secureStore: SecureStore }) const browser = createExpoWebBrowserAdapter({ webBrowser: WebBrowser }) export default function App() { return ( ) } ``` ## Sign in and sign out ```tsx import { useSignIn, useSignOut } from '@vonvon-kit/expo' import { Button } from 'react-native' function SignInScreen() { const { signIn, signInState } = useSignIn() return (