@jskit-ai/auth-provider-supabase-core 0.1.156 → 0.1.157

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.
package/package.json CHANGED
@@ -1,19 +1,18 @@
1
1
  {
2
2
  "name": "@jskit-ai/auth-provider-supabase-core",
3
- "version": "0.1.156",
3
+ "version": "0.1.157",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
7
7
  },
8
8
  "exports": {
9
- "./server/providers/AuthSupabaseServiceProvider": "./src/server/providers/AuthSupabaseServiceProvider.js",
10
- "./server/providers/AuthProviderServiceProvider": "./src/server/providers/AuthProviderServiceProvider.js",
9
+ "./server/providers/AuthSupabaseProvider": "./src/server/providers/AuthSupabaseProvider.js",
11
10
  "./server/lib/index": "./src/server/lib/index.js",
12
11
  "./client": "./src/client/index.js"
13
12
  },
14
13
  "dependencies": {
15
- "@jskit-ai/auth-core": "0.1.157",
16
- "@jskit-ai/kernel": "0.1.159",
14
+ "@jskit-ai/auth-core": "0.1.158",
15
+ "@jskit-ai/kernel": "0.1.160",
17
16
  "@supabase/supabase-js": "^2.57.4",
18
17
  "jose": "^6.1.0",
19
18
  "json-rest-schema": "^1.0.17",
@@ -21,47 +20,22 @@
21
20
  },
22
21
  "jskit": {
23
22
  "kind": "runtime",
24
- "options": {
25
- "auth-supabase-url": {
26
- "required": true,
27
- "values": [],
28
- "promptLabel": "Supabase URL",
29
- "promptHint": "https://YOUR-PROJECT.supabase.co"
30
- },
31
- "auth-supabase-publishable-key": {
32
- "required": true,
33
- "values": [],
34
- "promptLabel": "Supabase publishable key",
35
- "promptHint": "sb_publishable_..."
36
- },
37
- "app-public-url": {
38
- "required": true,
39
- "values": [],
40
- "defaultValue": "http://localhost:5173",
41
- "promptLabel": "App public URL",
42
- "promptHint": "Browser URL used for auth redirects"
43
- }
44
- },
45
23
  "capabilities": {
46
24
  "provides": [
47
- "auth.provider.supabase",
48
- "auth.provider"
25
+ "auth.service"
49
26
  ],
50
27
  "requires": [
51
- "auth.access"
28
+ "auth.extensions",
29
+ "runtime.config",
30
+ "runtime.env"
52
31
  ]
53
32
  },
54
33
  "runtime": {
55
34
  "server": {
56
- "providerEntrypoint": "src/server/providers/AuthSupabaseServiceProvider.js",
57
35
  "providers": [
58
36
  {
59
- "entrypoint": "src/server/providers/AuthSupabaseServiceProvider.js",
60
- "export": "AuthSupabaseServiceProvider"
61
- },
62
- {
63
- "entrypoint": "src/server/providers/AuthProviderServiceProvider.js",
64
- "export": "AuthProviderServiceProvider"
37
+ "entrypoint": "src/server/providers/AuthSupabaseProvider.js",
38
+ "export": "AuthSupabaseProvider"
65
39
  }
66
40
  ]
67
41
  }
@@ -70,12 +44,8 @@
70
44
  "apiSummary": {
71
45
  "surfaces": [
72
46
  {
73
- "subpath": "./server/providers/AuthSupabaseServiceProvider",
74
- "summary": "Exports the Supabase auth provider service provider."
75
- },
76
- {
77
- "subpath": "./server/providers/AuthProviderServiceProvider",
78
- "summary": "Exports the generic auth provider registration service provider."
47
+ "subpath": "./server/providers/AuthSupabaseProvider",
48
+ "summary": "Provides the selected Supabase authentication service through explicit runtime capabilities."
79
49
  },
80
50
  {
81
51
  "subpath": "./server/lib/index",
@@ -86,91 +56,8 @@
86
56
  "summary": "Exports no runtime API today (reserved client entrypoint)."
87
57
  }
88
58
  ],
89
- "containerTokens": {
90
- "server": [
91
- "authService"
92
- ],
93
- "client": []
94
- }
59
+ "capability": "auth.service"
95
60
  }
96
- },
97
- "mutations": {
98
- "dependencies": {
99
- "runtime": {
100
- "@jskit-ai/auth-core": "0.1.157",
101
- "@jskit-ai/kernel": "0.1.159",
102
- "dotenv": "^16.4.5",
103
- "@supabase/supabase-js": "^2.57.4",
104
- "jose": "^6.1.0"
105
- },
106
- "dev": {}
107
- },
108
- "packageJson": {
109
- "scripts": {}
110
- },
111
- "procfile": {},
112
- "files": [],
113
- "text": [
114
- {
115
- "file": ".env",
116
- "op": "upsert-env",
117
- "key": "AUTH_PROVIDER",
118
- "value": "supabase",
119
- "reason": "Select Supabase as the auth provider.",
120
- "category": "runtime-config",
121
- "id": "auth-provider"
122
- },
123
- {
124
- "file": ".env",
125
- "op": "upsert-env",
126
- "key": "AUTH_SUPABASE_URL",
127
- "value": "${option:auth-supabase-url}",
128
- "reason": "Configure Supabase project URL for auth.",
129
- "category": "runtime-config",
130
- "id": "auth-supabase-url"
131
- },
132
- {
133
- "file": ".env",
134
- "op": "upsert-env",
135
- "key": "AUTH_SUPABASE_PUBLISHABLE_KEY",
136
- "value": "${option:auth-supabase-publishable-key}",
137
- "reason": "Configure Supabase publishable key for auth.",
138
- "category": "runtime-config",
139
- "id": "auth-supabase-publishable-key"
140
- },
141
- {
142
- "file": ".env",
143
- "op": "upsert-env",
144
- "key": "APP_PUBLIC_URL",
145
- "value": "${option:app-public-url}",
146
- "reason": "Configure application public URL for auth redirect flows.",
147
- "category": "runtime-config",
148
- "id": "auth-app-public-url"
149
- },
150
- {
151
- "op": "append-text",
152
- "file": "config/server.js",
153
- "position": "bottom",
154
- "skipIfContains": "config.auth.profileMode =",
155
- "value": "\nconfig.auth ||= {};\nconfig.auth.profileMode = \"provider\";\n",
156
- "reason": "Use provider-owned auth profiles until a users package enables users-backed sync.",
157
- "category": "runtime-config",
158
- "id": "auth-profile-mode-provider"
159
- },
160
- {
161
- "op": "append-text",
162
- "file": "config/server.js",
163
- "position": "bottom",
164
- "skipIfContains": [
165
- "config.auth.oauth = {",
166
- "config.auth = {\n oauth: {"
167
- ],
168
- "value": "\nconfig.auth ||= {};\nconfig.auth.oauth = {\n providers: [],\n defaultProvider: \"\"\n};\n",
169
- "reason": "Append app-owned OAuth provider visibility config for stock auth screens.",
170
- "category": "runtime-config",
171
- "id": "auth-oauth-app-config"
172
- }
173
- ]
174
61
  }
175
62
  }
176
63
  }
@@ -0,0 +1,67 @@
1
+ ---
2
+ id: auth/supabase-auth
3
+ title: Supabase authentication
4
+ summary: Add Supabase authentication through normal npm composition, explicit environment values, and app-owned auth policy.
5
+ keywords: auth, authentication, oauth, sessions, supabase
6
+ requires: @jskit-ai/auth-provider-supabase-core
7
+ ---
8
+
9
+ # Supabase authentication
10
+
11
+ ## Use when
12
+
13
+ Use this pattern when the product deliberately chooses Supabase as its identity
14
+ provider. Install the runtime package normally and configure the application
15
+ environment and visible OAuth providers explicitly.
16
+
17
+ ## Do not use when
18
+
19
+ Do not use this pattern for local credential storage, a custom identity
20
+ provider, or an application whose authentication decision is still open.
21
+
22
+ ## Product decisions
23
+
24
+ Decide the public application URL, allowed sign-in methods, visible OAuth
25
+ providers, callback URLs, account-linking policy, and where deployment secrets
26
+ live. Obtain the project URL and publishable key from the selected Supabase
27
+ project.
28
+
29
+ ## Invariants
30
+
31
+ - Secrets and environment-specific values stay outside committed source.
32
+ - The application owns auth/profile and OAuth visibility policy.
33
+ - The installed runtime package supplies the provider implementation and its
34
+ npm dependency closure.
35
+ - No setup questionnaire or source mutation is required.
36
+
37
+ ## Framework APIs
38
+
39
+ Use `@jskit-ai/auth-provider-supabase-core` for the provider and
40
+ `@jskit-ai/auth-core` policy contracts. Configure `config.auth` through the
41
+ application's ordinary server configuration.
42
+
43
+ ## Example files
44
+
45
+ `example/package.json` declares the selected provider. `example/.env.example`
46
+ names required environment inputs without credentials. `example/config/server.js`
47
+ shows the app-owned profile and OAuth visibility decisions.
48
+
49
+ ## Variation points
50
+
51
+ Change the OAuth provider list, default provider, profile projection mode,
52
+ public URL, and deployment secret source. Compose account/profile packages only
53
+ when the product needs their behavior.
54
+
55
+ ## Verification
56
+
57
+ - Start the application with missing values and confirm concise diagnostics.
58
+ - Complete sign-in, callback, refresh, sign-out, and rejected-session cases.
59
+ - Verify allowed redirect origins and negative cross-origin behavior.
60
+ - Run the application auth and browser tests.
61
+
62
+ ## Avoid
63
+
64
+ - committing provider keys
65
+ - silently selecting Supabase for the user
66
+ - generated configuration fragments with hidden overwrite rules
67
+ - setup receipts, provenance, replay logs, or questionnaire answers
@@ -0,0 +1,4 @@
1
+ AUTH_PROVIDER=supabase
2
+ AUTH_SUPABASE_URL=https://your-project.supabase.co
3
+ AUTH_SUPABASE_PUBLISHABLE_KEY=replace-me
4
+ APP_PUBLIC_URL=http://localhost:5173
@@ -0,0 +1,11 @@
1
+ const config = {
2
+ auth: {
3
+ profileMode: "provider",
4
+ oauth: {
5
+ providers: [],
6
+ defaultProvider: ""
7
+ }
8
+ }
9
+ };
10
+
11
+ export default config;
@@ -0,0 +1,7 @@
1
+ {
2
+ "private": true,
3
+ "type": "module",
4
+ "dependencies": {
5
+ "@jskit-ai/auth-provider-supabase-core": "0.1.157"
6
+ }
7
+ }
@@ -0,0 +1,127 @@
1
+ import {
2
+ attachDeferredAuthService,
3
+ createDeferredAuthService
4
+ } from "@jskit-ai/auth-core/server/deferredAuthService";
5
+ import { normalizeEmail } from "@jskit-ai/auth-core/server/utils";
6
+ import { defineProvider } from "@jskit-ai/kernel/shared/capabilities";
7
+ import { resolveAllowedOriginsFromSurfaceDefinitions } from "@jskit-ai/kernel/shared/support/returnToPath";
8
+ import { createService } from "../lib/service.js";
9
+
10
+ const PROFILE_MODES = Object.freeze(["provider", "users"]);
11
+
12
+ function normalizeRecord(value) {
13
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
14
+ }
15
+
16
+ function splitCsv(value) {
17
+ return String(value || "").split(",").map((entry) => entry.trim()).filter(Boolean);
18
+ }
19
+
20
+ function oauthConfig(config) {
21
+ const oauth = normalizeRecord(normalizeRecord(config.auth).oauth);
22
+ return {
23
+ providers: Array.isArray(oauth.providers)
24
+ ? oauth.providers.map((entry) => String(entry || "").trim()).filter(Boolean)
25
+ : splitCsv(oauth.providers),
26
+ defaultProvider: String(oauth.defaultProvider || "").trim()
27
+ };
28
+ }
29
+
30
+ function providerConfig(env, config) {
31
+ const configuredOauth = oauthConfig(config);
32
+ const envProviders = splitCsv(env.AUTH_OAUTH_PROVIDERS);
33
+ return {
34
+ id: "supabase",
35
+ supabaseUrl: String(env.AUTH_SUPABASE_URL || env.SUPABASE_URL || "").trim(),
36
+ supabasePublishableKey: String(
37
+ env.AUTH_SUPABASE_PUBLISHABLE_KEY || env.SUPABASE_PUBLISHABLE_KEY || ""
38
+ ).trim(),
39
+ jwtAudience: String(env.AUTH_JWT_AUDIENCE || "authenticated").trim(),
40
+ oauthProviders: envProviders.length > 0 ? envProviders : configuredOauth.providers,
41
+ oauthDefaultProvider: String(env.AUTH_OAUTH_DEFAULT_PROVIDER || "").trim() || configuredOauth.defaultProvider
42
+ };
43
+ }
44
+
45
+ function profileMode(config) {
46
+ const mode = String(normalizeRecord(config.auth).profileMode || "provider").trim().toLowerCase();
47
+ if (!PROFILE_MODES.includes(mode)) {
48
+ throw new Error(`Unsupported config.auth.profileMode "${mode}". Supported values: ${PROFILE_MODES.join(", ")}.`);
49
+ }
50
+ return mode;
51
+ }
52
+
53
+ function createProviderProfileProjector() {
54
+ return Object.freeze({
55
+ async findByIdentity() { return null; },
56
+ async syncIdentityProfile(profileLike) {
57
+ const profile = normalizeRecord(profileLike);
58
+ const authProviderUserSid = String(profile.authProviderUserSid || "").trim();
59
+ const email = normalizeEmail(profile.email || "");
60
+ if (!authProviderUserSid || !email) {
61
+ throw new TypeError("Provider identity profile requires authProviderUserSid and email.");
62
+ }
63
+ return {
64
+ id: authProviderUserSid,
65
+ authProvider: String(profile.authProvider || "supabase").trim() || "supabase",
66
+ authProviderUserSid,
67
+ email,
68
+ displayName: String(profile.displayName || email.split("@")[0] || "User").trim(),
69
+ profileSource: "auth-provider"
70
+ };
71
+ }
72
+ });
73
+ }
74
+
75
+ function assertSelectedProvider(env) {
76
+ const selected = String(env.AUTH_PROVIDER || "").trim().toLowerCase();
77
+ if (selected && selected !== "supabase") {
78
+ throw new Error(`AUTH_PROVIDER is "${selected}", but the installed auth provider is Supabase.`);
79
+ }
80
+ }
81
+
82
+ const AuthSupabaseProvider = defineProvider({
83
+ id: "auth.supabase",
84
+ requires: {
85
+ config: "runtime.config",
86
+ env: "runtime.env",
87
+ extensions: "auth.extensions"
88
+ },
89
+ optional: {
90
+ identity: "users.identity"
91
+ },
92
+ provides: {
93
+ authService: "auth.service"
94
+ },
95
+ setup({ env }) {
96
+ assertSelectedProvider(env);
97
+ return { authService: createDeferredAuthService() };
98
+ },
99
+ boot({ config, env, extensions, identity }, { outputs }) {
100
+ assertSelectedProvider(env);
101
+ const mode = profileMode(config);
102
+ if (mode === "users" && !identity) {
103
+ throw new Error('Supabase auth profileMode "users" requires the users.identity capability.');
104
+ }
105
+ const projector = mode === "users" ? identity.profileProjector : createProviderProfileProjector();
106
+ const appPublicUrl = String(env.APP_PUBLIC_URL || "").trim();
107
+ const baseService = createService({
108
+ authProvider: providerConfig(env, config),
109
+ appPublicUrl,
110
+ authAllowedReturnToOrigins: resolveAllowedOriginsFromSurfaceDefinitions(config.surfaceDefinitions || {}, {
111
+ seedOrigins: [appPublicUrl]
112
+ }),
113
+ nodeEnv: String(env.NODE_ENV || "development").trim() || "development",
114
+ userSettingsRepository: mode === "users" ? identity.repositories.userSettings : null,
115
+ userProfilesRepository: mode === "users" ? identity.repositories.userProfiles : null,
116
+ userProfileSyncService: projector,
117
+ profileProjectionEnabled: mode === "users",
118
+ devAuthBypassEnabled: env.AUTH_DEV_BYPASS_ENABLED,
119
+ devAuthBypassSecret: String(env.AUTH_DEV_BYPASS_SECRET || "").trim(),
120
+ devAuthAccessTtlSeconds: env.AUTH_DEV_ACCESS_TTL_SECONDS,
121
+ devAuthRefreshTtlSeconds: env.AUTH_DEV_REFRESH_TTL_SECONDS
122
+ });
123
+ attachDeferredAuthService(outputs.authService, extensions.decorateService(baseService));
124
+ }
125
+ });
126
+
127
+ export { AuthSupabaseProvider };
@@ -3,8 +3,7 @@ import { readFile } from "node:fs/promises";
3
3
  import test from "node:test";
4
4
 
5
5
  const EXPECTED_EXPORTS = Object.freeze({
6
- "./server/providers/AuthSupabaseServiceProvider": "./src/server/providers/AuthSupabaseServiceProvider.js",
7
- "./server/providers/AuthProviderServiceProvider": "./src/server/providers/AuthProviderServiceProvider.js",
6
+ "./server/providers/AuthSupabaseProvider": "./src/server/providers/AuthSupabaseProvider.js",
8
7
  "./server/lib/index": "./src/server/lib/index.js",
9
8
  "./client": "./src/client/index.js"
10
9
  });