@maravilla-labs/platform 0.1.35 → 0.1.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,192 @@
1
+ /**
2
+ * @fileoverview Typed schema for `maravilla.config.{ts,yaml,json}` files.
3
+ *
4
+ * Declares your project's auth settings (resources, groups, relations,
5
+ * registration fields, OAuth providers, security policy, branding) alongside
6
+ * your code. The Maravilla adapter reads this at build time and reconciles
7
+ * the settings into delivery on deploy.
8
+ *
9
+ * ```typescript
10
+ * import { defineConfig } from '@maravilla-labs/platform/config';
11
+ *
12
+ * export default defineConfig({
13
+ * auth: {
14
+ * resources: [
15
+ * { name: 'todos', title: 'Todos', actions: ['read', 'write'],
16
+ * policy: 'auth.user_id == node.owner' },
17
+ * ],
18
+ * },
19
+ * });
20
+ * ```
21
+ *
22
+ * Omitted sections leave the DB alone — partial adoption is explicitly
23
+ * supported. List-based sections (`resources`, `groups`, `relations`,
24
+ * `oauth`) are upserted and never auto-delete DB-only entries. Singleton
25
+ * sections (`registration`, `security`, `branding`) are replaced wholesale
26
+ * when declared.
27
+ */
28
+ /**
29
+ * String value that may either be a literal secret or a reference to an
30
+ * environment variable on the **tenant** (resolved server-side at
31
+ * reconcile time, never shipped plaintext in the manifest).
32
+ *
33
+ * Accepted forms:
34
+ * - `"literal-value"` — inline (not recommended for real secrets)
35
+ * - `"${env.VAR_NAME}"` — string-template form
36
+ * - `{ env: "VAR_NAME" }` — object form
37
+ */
38
+ type SecretRef = string | {
39
+ env: string;
40
+ };
41
+ interface ResourceDefinition {
42
+ /** URL-safe slug. Used as the resource key in code (e.g. the KV namespace). */
43
+ name: string;
44
+ /** Human-readable title for the admin UI. */
45
+ title: string;
46
+ /** Optional longer description. */
47
+ description?: string;
48
+ /** Actions this resource supports, e.g. `['read', 'write', 'delete']`. */
49
+ actions: string[];
50
+ /**
51
+ * Optional raisin-rel policy expression. Evaluated on every KV/DB/
52
+ * realtime/media op that targets this resource. Leave empty to skip
53
+ * Layer 2 for this resource — tenant + owner isolation still applies.
54
+ */
55
+ policy?: string;
56
+ }
57
+ interface GroupPermissionDefinition {
58
+ /** Must match a `ResourceDefinition.name`. */
59
+ resource_name: string;
60
+ /** Actions this group is granted on the resource. */
61
+ actions: string[];
62
+ }
63
+ interface GroupDefinition {
64
+ /** Unique group name per tenant. */
65
+ name: string;
66
+ /** Optional description for the admin UI. */
67
+ description?: string;
68
+ /** Resource permissions granted to the group. Replaces the group's current permissions when declared. */
69
+ permissions?: GroupPermissionDefinition[];
70
+ }
71
+ interface RelationTypeDefinition {
72
+ /** Uppercase identifier used in policies (`... VIA 'STEWARDS'`). */
73
+ relation_name: string;
74
+ /** Human-readable title. */
75
+ title: string;
76
+ description?: string;
77
+ /** Grouping for the admin UI (e.g. `"family"`, `"work"`). */
78
+ category?: string;
79
+ icon?: string;
80
+ color?: string;
81
+ /** Name of the inverse relation type, if one exists. */
82
+ inverse_relation_name?: string;
83
+ /** When true, membership in this relation implies stewardship rights. */
84
+ implies_stewardship?: boolean;
85
+ /** When true, the relation can only target users flagged as minors. */
86
+ requires_minor?: boolean;
87
+ /** When true, the relation is symmetric (A→B implies B→A). */
88
+ bidirectional?: boolean;
89
+ }
90
+ interface RegistrationFieldDefinition {
91
+ /** Field key used as the form field name + in profile data. */
92
+ key: string;
93
+ /** Display label. */
94
+ label: string;
95
+ /** One of: text, email, phone, date, number, select, boolean, url, textarea. */
96
+ field_type: string;
97
+ required: boolean;
98
+ show_on_register: boolean;
99
+ /** Optional validation metadata — passed through to the UI. */
100
+ validation?: Record<string, unknown>;
101
+ }
102
+ interface RegistrationConfig {
103
+ /** Ordered list of custom registration fields. Declaring this replaces the full list. */
104
+ fields: RegistrationFieldDefinition[];
105
+ }
106
+ interface OAuthProviderDefinition {
107
+ enabled: boolean;
108
+ client_id: string;
109
+ /** Prefer `{ env: "VAR_NAME" }` or `"${env.VAR_NAME}"`. */
110
+ client_secret: SecretRef;
111
+ scopes: string[];
112
+ /** Only for `custom_oidc`. */
113
+ discovery_url?: string;
114
+ }
115
+ interface OAuthProvidersConfig {
116
+ google?: OAuthProviderDefinition;
117
+ github?: OAuthProviderDefinition;
118
+ okta?: OAuthProviderDefinition;
119
+ custom_oidc?: OAuthProviderDefinition;
120
+ }
121
+ interface PasswordPolicyDefinition {
122
+ min_length: number;
123
+ require_uppercase: boolean;
124
+ require_number: boolean;
125
+ require_special: boolean;
126
+ }
127
+ interface SessionConfigDefinition {
128
+ access_token_ttl_secs: number;
129
+ refresh_token_ttl_secs: number;
130
+ max_sessions_per_user: number;
131
+ require_email_verification: boolean;
132
+ }
133
+ interface SecurityConfig {
134
+ password_policy?: PasswordPolicyDefinition;
135
+ session?: SessionConfigDefinition;
136
+ }
137
+ interface BrandingConfig {
138
+ app_name?: string;
139
+ logo_url?: string;
140
+ primary_color?: string;
141
+ secondary_color?: string;
142
+ welcome_message?: string;
143
+ welcome_subtitle?: string;
144
+ /** `"centered"`, `"split-left"`, `"split-right"`, or `"fullscreen"`. */
145
+ layout?: string;
146
+ background_image_url?: string;
147
+ /** 0–100 percentage. */
148
+ background_focal_point?: {
149
+ x: number;
150
+ y: number;
151
+ };
152
+ background_gradient?: string;
153
+ /** `"light"`, `"dark"`, or `"auto"`. */
154
+ color_mode?: string;
155
+ font_family?: string;
156
+ terms_url?: string;
157
+ privacy_url?: string;
158
+ /** Raw CSS merged into the hosted auth pages. */
159
+ custom_css?: string;
160
+ }
161
+ interface AuthConfigBlock {
162
+ resources?: ResourceDefinition[];
163
+ groups?: GroupDefinition[];
164
+ relations?: RelationTypeDefinition[];
165
+ registration?: RegistrationConfig;
166
+ oauth?: OAuthProvidersConfig;
167
+ security?: SecurityConfig;
168
+ branding?: BrandingConfig;
169
+ }
170
+ interface MaravillaConfig {
171
+ /** All project-level auth settings. Every field is optional — partial adoption is supported. */
172
+ auth?: AuthConfigBlock;
173
+ }
174
+ /**
175
+ * Identity function that returns the config unchanged — exists purely so the
176
+ * TypeScript compiler can infer `MaravillaConfig` and give you IntelliSense
177
+ * on every field.
178
+ *
179
+ * @example
180
+ * ```typescript
181
+ * import { defineConfig } from '@maravilla-labs/platform/config';
182
+ *
183
+ * export default defineConfig({
184
+ * auth: {
185
+ * resources: [{ name: 'todos', title: 'Todos', actions: ['read', 'write'] }],
186
+ * },
187
+ * });
188
+ * ```
189
+ */
190
+ declare function defineConfig(config: MaravillaConfig): MaravillaConfig;
191
+
192
+ export { type AuthConfigBlock, type BrandingConfig, type GroupDefinition, type GroupPermissionDefinition, type MaravillaConfig, type OAuthProviderDefinition, type OAuthProvidersConfig, type PasswordPolicyDefinition, type RegistrationConfig, type RegistrationFieldDefinition, type RelationTypeDefinition, type ResourceDefinition, type SecretRef, type SecurityConfig, type SessionConfigDefinition, defineConfig };
package/dist/config.js ADDED
@@ -0,0 +1,8 @@
1
+ // src/config.ts
2
+ function defineConfig(config) {
3
+ return config;
4
+ }
5
+ export {
6
+ defineConfig
7
+ };
8
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["/**\n * @fileoverview Typed schema for `maravilla.config.{ts,yaml,json}` files.\n *\n * Declares your project's auth settings (resources, groups, relations,\n * registration fields, OAuth providers, security policy, branding) alongside\n * your code. The Maravilla adapter reads this at build time and reconciles\n * the settings into delivery on deploy.\n *\n * ```typescript\n * import { defineConfig } from '@maravilla-labs/platform/config';\n *\n * export default defineConfig({\n * auth: {\n * resources: [\n * { name: 'todos', title: 'Todos', actions: ['read', 'write'],\n * policy: 'auth.user_id == node.owner' },\n * ],\n * },\n * });\n * ```\n *\n * Omitted sections leave the DB alone — partial adoption is explicitly\n * supported. List-based sections (`resources`, `groups`, `relations`,\n * `oauth`) are upserted and never auto-delete DB-only entries. Singleton\n * sections (`registration`, `security`, `branding`) are replaced wholesale\n * when declared.\n */\n\n/**\n * String value that may either be a literal secret or a reference to an\n * environment variable on the **tenant** (resolved server-side at\n * reconcile time, never shipped plaintext in the manifest).\n *\n * Accepted forms:\n * - `\"literal-value\"` — inline (not recommended for real secrets)\n * - `\"${env.VAR_NAME}\"` — string-template form\n * - `{ env: \"VAR_NAME\" }` — object form\n */\nexport type SecretRef = string | { env: string };\n\n// ── Resources + policies ──\n\nexport interface ResourceDefinition {\n /** URL-safe slug. Used as the resource key in code (e.g. the KV namespace). */\n name: string;\n /** Human-readable title for the admin UI. */\n title: string;\n /** Optional longer description. */\n description?: string;\n /** Actions this resource supports, e.g. `['read', 'write', 'delete']`. */\n actions: string[];\n /**\n * Optional raisin-rel policy expression. Evaluated on every KV/DB/\n * realtime/media op that targets this resource. Leave empty to skip\n * Layer 2 for this resource — tenant + owner isolation still applies.\n */\n policy?: string;\n}\n\n// ── Groups ──\n\nexport interface GroupPermissionDefinition {\n /** Must match a `ResourceDefinition.name`. */\n resource_name: string;\n /** Actions this group is granted on the resource. */\n actions: string[];\n}\n\nexport interface GroupDefinition {\n /** Unique group name per tenant. */\n name: string;\n /** Optional description for the admin UI. */\n description?: string;\n /** Resource permissions granted to the group. Replaces the group's current permissions when declared. */\n permissions?: GroupPermissionDefinition[];\n}\n\n// ── Relations ──\n\nexport interface RelationTypeDefinition {\n /** Uppercase identifier used in policies (`... VIA 'STEWARDS'`). */\n relation_name: string;\n /** Human-readable title. */\n title: string;\n description?: string;\n /** Grouping for the admin UI (e.g. `\"family\"`, `\"work\"`). */\n category?: string;\n icon?: string;\n color?: string;\n /** Name of the inverse relation type, if one exists. */\n inverse_relation_name?: string;\n /** When true, membership in this relation implies stewardship rights. */\n implies_stewardship?: boolean;\n /** When true, the relation can only target users flagged as minors. */\n requires_minor?: boolean;\n /** When true, the relation is symmetric (A→B implies B→A). */\n bidirectional?: boolean;\n}\n\n// ── Registration fields ──\n\nexport interface RegistrationFieldDefinition {\n /** Field key used as the form field name + in profile data. */\n key: string;\n /** Display label. */\n label: string;\n /** One of: text, email, phone, date, number, select, boolean, url, textarea. */\n field_type: string;\n required: boolean;\n show_on_register: boolean;\n /** Optional validation metadata — passed through to the UI. */\n validation?: Record<string, unknown>;\n}\n\nexport interface RegistrationConfig {\n /** Ordered list of custom registration fields. Declaring this replaces the full list. */\n fields: RegistrationFieldDefinition[];\n}\n\n// ── OAuth providers ──\n\nexport interface OAuthProviderDefinition {\n enabled: boolean;\n client_id: string;\n /** Prefer `{ env: \"VAR_NAME\" }` or `\"${env.VAR_NAME}\"`. */\n client_secret: SecretRef;\n scopes: string[];\n /** Only for `custom_oidc`. */\n discovery_url?: string;\n}\n\nexport interface OAuthProvidersConfig {\n google?: OAuthProviderDefinition;\n github?: OAuthProviderDefinition;\n okta?: OAuthProviderDefinition;\n custom_oidc?: OAuthProviderDefinition;\n}\n\n// ── Security ──\n\nexport interface PasswordPolicyDefinition {\n min_length: number;\n require_uppercase: boolean;\n require_number: boolean;\n require_special: boolean;\n}\n\nexport interface SessionConfigDefinition {\n access_token_ttl_secs: number;\n refresh_token_ttl_secs: number;\n max_sessions_per_user: number;\n require_email_verification: boolean;\n}\n\nexport interface SecurityConfig {\n password_policy?: PasswordPolicyDefinition;\n session?: SessionConfigDefinition;\n}\n\n// ── Branding ──\n\nexport interface BrandingConfig {\n app_name?: string;\n logo_url?: string;\n primary_color?: string;\n secondary_color?: string;\n welcome_message?: string;\n welcome_subtitle?: string;\n /** `\"centered\"`, `\"split-left\"`, `\"split-right\"`, or `\"fullscreen\"`. */\n layout?: string;\n background_image_url?: string;\n /** 0–100 percentage. */\n background_focal_point?: { x: number; y: number };\n background_gradient?: string;\n /** `\"light\"`, `\"dark\"`, or `\"auto\"`. */\n color_mode?: string;\n font_family?: string;\n terms_url?: string;\n privacy_url?: string;\n /** Raw CSS merged into the hosted auth pages. */\n custom_css?: string;\n}\n\n// ── Top-level shape ──\n\nexport interface AuthConfigBlock {\n resources?: ResourceDefinition[];\n groups?: GroupDefinition[];\n relations?: RelationTypeDefinition[];\n registration?: RegistrationConfig;\n oauth?: OAuthProvidersConfig;\n security?: SecurityConfig;\n branding?: BrandingConfig;\n}\n\nexport interface MaravillaConfig {\n /** All project-level auth settings. Every field is optional — partial adoption is supported. */\n auth?: AuthConfigBlock;\n}\n\n/**\n * Identity function that returns the config unchanged — exists purely so the\n * TypeScript compiler can infer `MaravillaConfig` and give you IntelliSense\n * on every field.\n *\n * @example\n * ```typescript\n * import { defineConfig } from '@maravilla-labs/platform/config';\n *\n * export default defineConfig({\n * auth: {\n * resources: [{ name: 'todos', title: 'Todos', actions: ['read', 'write'] }],\n * },\n * });\n * ```\n */\nexport function defineConfig(config: MaravillaConfig): MaravillaConfig {\n return config;\n}\n"],"mappings":";AAwNO,SAAS,aAAa,QAA0C;AACrE,SAAO;AACT;","names":[]}
package/dist/index.d.ts CHANGED
@@ -598,6 +598,354 @@ interface PresenceService {
598
598
  lastSeen?: number;
599
599
  }>>;
600
600
  }
601
+ /**
602
+ * Authenticated user record from the platform auth service.
603
+ */
604
+ interface AuthUser {
605
+ /** Unique user ID (prefixed with "usr_") */
606
+ id: string;
607
+ /** User's email address */
608
+ email: string;
609
+ /** Whether the email has been verified */
610
+ email_verified: boolean;
611
+ /** Account status */
612
+ status: 'active' | 'suspended' | 'deactivated';
613
+ /** Authentication provider ("email", "google", "github", etc.) */
614
+ provider: string;
615
+ /** Group IDs the user belongs to */
616
+ groups: string[];
617
+ /** Unix timestamp when the user was created */
618
+ created_at: number;
619
+ /** Unix timestamp when the user was last updated */
620
+ updated_at: number;
621
+ /** Unix timestamp of last login (if any) */
622
+ last_login_at?: number;
623
+ }
624
+ /**
625
+ * Snapshot of whoever is currently bound to the request as the caller.
626
+ *
627
+ * Populated by {@link AuthService.login} (implicit), {@link AuthService.setCurrentUser}
628
+ * (explicit), or left anonymous if neither has run for this request.
629
+ * This is exactly what per-resource policies see as `auth.*` when they run.
630
+ */
631
+ interface AuthCaller {
632
+ /** Caller's user id, or `""` if anonymous */
633
+ user_id: string;
634
+ /** Caller's email, or `""` if anonymous */
635
+ email: string;
636
+ /** Admin flag from the session */
637
+ is_admin: boolean;
638
+ /** Role names (project-scoped) */
639
+ roles: string[];
640
+ /** `true` when no identity is bound to this request */
641
+ is_anonymous: boolean;
642
+ }
643
+ /**
644
+ * Session returned after successful login or token refresh.
645
+ */
646
+ interface AuthSession {
647
+ /** Short-lived JWT access token (default 15 min) */
648
+ access_token: string;
649
+ /** Single-use opaque refresh token (default 30 days) */
650
+ refresh_token: string;
651
+ /** Access token lifetime in seconds */
652
+ expires_in: number;
653
+ /** The authenticated user */
654
+ user: AuthUser;
655
+ }
656
+ /**
657
+ * Custom registration field defined in project auth settings.
658
+ */
659
+ interface AuthField {
660
+ /** Field key (used as form field name) */
661
+ key: string;
662
+ /** Display label */
663
+ label: string;
664
+ /** Field type: text, email, phone, date, number, select, boolean, url, textarea */
665
+ field_type: string;
666
+ /** Whether the field is required */
667
+ required: boolean;
668
+ /** Whether the field appears on the registration form */
669
+ show_on_register: boolean;
670
+ }
671
+ /**
672
+ * Options for registering a new user.
673
+ */
674
+ interface RegisterOptions {
675
+ /** User's email address */
676
+ email: string;
677
+ /** Password (minimum 8 characters) */
678
+ password: string;
679
+ /** Optional profile data (custom fields) */
680
+ profile?: Record<string, any>;
681
+ }
682
+ /**
683
+ * Options for logging in.
684
+ */
685
+ interface LoginOptions {
686
+ /** User's email address */
687
+ email: string;
688
+ /** User's password */
689
+ password: string;
690
+ }
691
+ /**
692
+ * Filter options for listing users.
693
+ */
694
+ interface UserListFilter {
695
+ /** Max results per page (default 50) */
696
+ limit?: number;
697
+ /** Number of results to skip */
698
+ offset?: number;
699
+ /** Filter by account status */
700
+ status?: 'active' | 'suspended' | 'deactivated';
701
+ /** Filter by email (partial match) */
702
+ email_contains?: string;
703
+ /** Filter by group ID */
704
+ group_id?: string;
705
+ }
706
+ /**
707
+ * Paginated user list response.
708
+ */
709
+ interface UserListResponse {
710
+ /** Users in this page */
711
+ users: AuthUser[];
712
+ /** Total number of matching users */
713
+ total: number;
714
+ /** Page size */
715
+ limit: number;
716
+ /** Offset */
717
+ offset: number;
718
+ }
719
+ /**
720
+ * Options for updating a user.
721
+ */
722
+ interface UpdateUserOptions {
723
+ /** New email address */
724
+ email?: string;
725
+ /** New status */
726
+ status?: 'active' | 'suspended' | 'deactivated';
727
+ /** Profile data to merge */
728
+ profile?: Record<string, any>;
729
+ }
730
+ /**
731
+ * Auth service for end-user authentication and user management.
732
+ *
733
+ * @example
734
+ * ```typescript
735
+ * const platform = getPlatform();
736
+ *
737
+ * // Register a new user
738
+ * const user = await platform.auth.register({
739
+ * email: 'user@example.com',
740
+ * password: 'securePassword123'
741
+ * });
742
+ *
743
+ * // Login
744
+ * const session = await platform.auth.login({
745
+ * email: 'user@example.com',
746
+ * password: 'securePassword123'
747
+ * });
748
+ * // session.access_token — short-lived JWT
749
+ * // session.refresh_token — single-use refresh token
750
+ *
751
+ * // Validate a token (e.g. from Authorization header or cookie)
752
+ * const user = await platform.auth.validate(session.access_token);
753
+ *
754
+ * // Protect a route with withAuth middleware
755
+ * export default {
756
+ * fetch: platform.auth.withAuth(async (request) => {
757
+ * // request.user is guaranteed to be set
758
+ * return new Response(`Hello ${request.user.email}`);
759
+ * })
760
+ * };
761
+ * ```
762
+ */
763
+ interface AuthService {
764
+ /**
765
+ * Register a new user with email and password.
766
+ * @returns The created user (not yet email-verified)
767
+ */
768
+ register(options: RegisterOptions): Promise<AuthUser>;
769
+ /**
770
+ * Authenticate a user and create a session.
771
+ * @returns Session with access token, refresh token, and user info
772
+ */
773
+ login(options: LoginOptions): Promise<AuthSession>;
774
+ /**
775
+ * Validate an access token and return the authenticated user.
776
+ * @param accessToken - JWT access token from login or refresh
777
+ * @throws If the token is invalid or expired
778
+ */
779
+ validate(accessToken: string): Promise<AuthUser>;
780
+ /**
781
+ * Refresh a session using a refresh token (single-use).
782
+ * @param refreshToken - The refresh token from a previous login/refresh
783
+ * @returns New session with fresh access and refresh tokens
784
+ */
785
+ refresh(refreshToken: string): Promise<AuthSession>;
786
+ /**
787
+ * Revoke a specific session.
788
+ */
789
+ logout(sessionId: string): Promise<void>;
790
+ /**
791
+ * Get a user by ID.
792
+ * @returns The user, or null if not found
793
+ */
794
+ getUser(userId: string): Promise<AuthUser | null>;
795
+ /**
796
+ * List users with optional filtering and pagination.
797
+ */
798
+ listUsers(filter?: UserListFilter): Promise<UserListResponse>;
799
+ /**
800
+ * Update a user's email, status, or profile data.
801
+ */
802
+ updateUser(userId: string, update: UpdateUserOptions): Promise<AuthUser>;
803
+ /**
804
+ * Delete a user and all their sessions.
805
+ */
806
+ deleteUser(userId: string): Promise<void>;
807
+ /**
808
+ * Create an email verification token.
809
+ * @returns The verification token (caller decides how to deliver it)
810
+ */
811
+ sendVerification(userId: string): Promise<{
812
+ token: string;
813
+ }>;
814
+ /**
815
+ * Verify an email address using a verification token.
816
+ */
817
+ verifyEmail(token: string): Promise<void>;
818
+ /**
819
+ * Create a password reset token for an email address.
820
+ * @returns The reset token (caller decides how to deliver it)
821
+ */
822
+ sendPasswordReset(email: string): Promise<{
823
+ token: string;
824
+ }>;
825
+ /**
826
+ * Reset a password using a reset token.
827
+ */
828
+ resetPassword(token: string, newPassword: string): Promise<void>;
829
+ /**
830
+ * Change a user's password (requires old password).
831
+ */
832
+ changePassword(userId: string, oldPassword: string, newPassword: string): Promise<void>;
833
+ /**
834
+ * Get the configured registration fields for this project.
835
+ */
836
+ getFieldConfig(): Promise<{
837
+ fields: AuthField[];
838
+ }>;
839
+ /**
840
+ * Start an OAuth flow by generating an authorization URL.
841
+ * Redirect the user to the returned URL to begin authentication.
842
+ *
843
+ * @param provider - Provider name: "google", "github", "okta", or "custom_oidc"
844
+ * @param options - Optional configuration
845
+ * @returns Object with `auth_url` (redirect target) and `state` (for CSRF verification)
846
+ */
847
+ getOAuthUrl(provider: string, options?: {
848
+ redirectUri?: string;
849
+ }): Promise<{
850
+ auth_url: string;
851
+ state: string;
852
+ }>;
853
+ /**
854
+ * Complete an OAuth flow by exchanging the authorization code.
855
+ * Call this after the provider redirects back with a code and state.
856
+ *
857
+ * @param provider - Provider name
858
+ * @param params - The code and state from the OAuth callback
859
+ * @returns Either a session (user authenticated) or a link_required result
860
+ */
861
+ handleOAuthCallback(provider: string, params: {
862
+ code: string;
863
+ state: string;
864
+ }): Promise<AuthSession | {
865
+ type: 'LinkRequired';
866
+ email: string;
867
+ provider: string;
868
+ provider_id: string;
869
+ existing_user_id: string;
870
+ }>;
871
+ /**
872
+ * Middleware helper that validates auth and injects `request.user`.
873
+ * Returns 401 JSON response if no valid token is found.
874
+ *
875
+ * Extracts token from `Authorization: Bearer <token>` header
876
+ * or `__session` cookie.
877
+ *
878
+ * @example
879
+ * ```typescript
880
+ * export default {
881
+ * fetch: platform.auth.withAuth(async (request) => {
882
+ * const data = await platform.db.items.find({ owner: request.user.id });
883
+ * return Response.json(data);
884
+ * })
885
+ * };
886
+ * ```
887
+ */
888
+ withAuth<T extends (request: Request & {
889
+ user: AuthUser;
890
+ }) => Promise<Response>>(handler: T): (request: Request) => Promise<Response>;
891
+ /**
892
+ * Explicitly bind the caller for the remainder of this request.
893
+ * Pass a JWT to validate + bind, or `null` / `""` to clear.
894
+ *
895
+ * `login()` already binds implicitly on success; reach for `setCurrentUser`
896
+ * when you receive a JWT from an inbound `Authorization` header or cookie
897
+ * and want subsequent KV/DB/realtime/media ops to run as that user.
898
+ *
899
+ * Not available on remote clients — throws.
900
+ */
901
+ setCurrentUser(token: string | null): Promise<void>;
902
+ /**
903
+ * Snapshot of the currently bound caller. Returns an anonymous caller
904
+ * (`is_anonymous: true`) when no identity has been bound.
905
+ *
906
+ * Not available on remote clients — throws.
907
+ */
908
+ getCurrentUser(): AuthCaller;
909
+ /**
910
+ * Ask the policy engine whether the bound caller would be allowed to
911
+ * perform `action` on `resourceId`, given the supplied `node` payload.
912
+ * Returns a boolean — never throws on denial.
913
+ *
914
+ * The check runs the exact same evaluator that gates direct KV/DB/
915
+ * realtime/media ops, so `can(...)` is authoritative.
916
+ *
917
+ * @example
918
+ * ```typescript
919
+ * const ok = await platform.auth.can("delete", "documents", {
920
+ * owner: doc.owner,
921
+ * status: doc.status,
922
+ * });
923
+ * if (!ok) return new Response("Forbidden", { status: 403 });
924
+ * ```
925
+ *
926
+ * Not available on remote clients — throws.
927
+ */
928
+ can(action: string, resourceId: string, node?: Record<string, unknown> | null): Promise<boolean>;
929
+ }
930
+ /**
931
+ * Per-request opt-out toggle for the Layer 2 policy evaluator.
932
+ *
933
+ * Flipping `setEnabled(false)` disables **per-resource policies only** for
934
+ * the remainder of the current request. Layer 1 (tenant + owner isolation)
935
+ * is always enforced — no call can ever escape its tenant. Every flip is
936
+ * audit-logged server-side with the caller's identity.
937
+ *
938
+ * Intended for trusted in-app flows (first-run seeders, admin jobs). Do not
939
+ * toggle based on untrusted input.
940
+ *
941
+ * Not available on remote clients — throws.
942
+ */
943
+ interface PolicyService {
944
+ /** Disable or re-enable Layer 2 policy checks for this request. */
945
+ setEnabled(enabled: boolean): void;
946
+ /** `true` when Layer 2 is active for this request. */
947
+ isEnabled(): boolean;
948
+ }
601
949
  interface Platform {
602
950
  /** Environment containing all available platform services */
603
951
  env: PlatformEnv;
@@ -605,6 +953,10 @@ interface Platform {
605
953
  media?: MediaService;
606
954
  /** Realtime service for pub/sub channels and presence */
607
955
  realtime: RealtimeService;
956
+ /** Auth service for end-user authentication, identity binding, and authorization checks */
957
+ auth: AuthService;
958
+ /** Per-request Layer 2 policy toggle (Layer 1 isolation always applies) */
959
+ policy: PolicyService;
608
960
  }
609
961
 
610
962
  interface RenEvent {
@@ -967,4 +1319,4 @@ declare function getPlatform(options?: {
967
1319
  */
968
1320
  declare function clearPlatformCache(): void;
969
1321
 
970
- export { type Database, type DbFindOptions, type KvListResult, type KvNamespace, MediaLocalParticipant, type MediaParticipant, type MediaParticipantInfo, MediaRoom, MediaRoomEvent, type MediaRoomInfo, type MediaRoomInfoSettings, type MediaRoomOptions, type MediaService, type MediaTokenResult, type MediaTrackPublication, type Platform, type PlatformEnv, type PresenceMember, type PresenceService, RealtimeClient, type RealtimeClientOptions, type RealtimeEvent, type RealtimeService, RemoteMediaService, RenClient, type RenClientOptions, type RenEvent, type Storage$1 as Storage, type StoragePutStreamSource, type TrackKind, type TrackSource, type VideoResolution, attachTrack, clearPlatformCache, detachTrack, getOrCreateClientId, getPlatform, renFetch, storageDelete, storageUpload };
1322
+ export { type AuthCaller, type AuthField, type AuthService, type AuthSession, type AuthUser, type Database, type DbFindOptions, type KvListResult, type KvNamespace, type LoginOptions, MediaLocalParticipant, type MediaParticipant, type MediaParticipantInfo, MediaRoom, MediaRoomEvent, type MediaRoomInfo, type MediaRoomInfoSettings, type MediaRoomOptions, type MediaService, type MediaTokenResult, type MediaTrackPublication, type Platform, type PlatformEnv, type PolicyService, type PresenceMember, type PresenceService, RealtimeClient, type RealtimeClientOptions, type RealtimeEvent, type RealtimeService, type RegisterOptions, RemoteMediaService, RenClient, type RenClientOptions, type RenEvent, type Storage$1 as Storage, type StoragePutStreamSource, type TrackKind, type TrackSource, type UpdateUserOptions, type UserListFilter, type UserListResponse, type VideoResolution, attachTrack, clearPlatformCache, detachTrack, getOrCreateClientId, getPlatform, renFetch, storageDelete, storageUpload };