authjs-corepass-provider 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CorePass
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,450 @@
1
+ # authjs-corepass-provider
2
+
3
+ CorePass provider + server helpers for Auth.js (`@auth/core`) implementing the **pending-by-default** registration flow:
4
+
5
+ - Browser completes WebAuthn attestation (registration)
6
+ - Server stores a **pending registration** (no Auth.js user/account/authenticator yet)
7
+ - CorePass mobile app finalizes the account by calling **`POST /passkey/data`** with an **Ed448-signed** payload
8
+
9
+ ## What you get
10
+
11
+ - **Provider**: `CorePass()` (wraps Auth.js WebAuthn with passkey-friendly defaults)
12
+ - **Server helpers**: `createCorePassServer()` exposing handlers:
13
+ - `startRegistration(req)`
14
+ - `finishRegistration(req)`
15
+ - `enrichRegistration(req)` (your `/passkey/data`)
16
+ - **DB extension schema**: `db/corepass-schema.sql`
17
+
18
+ ## Flows
19
+
20
+ ### Registration flow (pending-by-default)
21
+
22
+ ```mermaid
23
+ sequenceDiagram
24
+ autonumber
25
+ actor B as Browser
26
+ participant S as Your backend
27
+ participant KV as Challenge store
28
+ participant DB as CorePass store
29
+ actor A as CorePass app
30
+
31
+ B->>S: POST /webauthn/start { email? }
32
+ Note over B,S: Pending TTL default is 10 minutes (pendingTtlSeconds=600)
33
+ S->>KV: put reg:sid {challenge,email} ttl
34
+ S-->>B: 200 CreationOptions + Set-Cookie corepass.sid
35
+ B->>B: navigator.credentials.create()
36
+ B->>S: POST /webauthn/finish { attestation, email? }
37
+ S->>KV: get+delete reg:sid
38
+ S->>S: verifyRegistrationResponse()
39
+ S->>DB: createPendingRegistration(credentialId, publicKey, counter, aaguid, email?)
40
+ S-->>B: 200 { pending:true, enrichToken, credentialId }
41
+
42
+ A->>S: POST /passkey/data {coreId, credentialId, timestamp, userData} + X-Signature (Ed448)
43
+ S->>S: validateCoreIdMainnet + timestamp window
44
+ S->>S: verify Ed448 signature over canonical JSON
45
+ S->>DB: load+delete pending by credentialId
46
+ S->>S: create/link Auth.js user+account+authenticator
47
+ S->>DB: upsert CorePass identity/profile (provided_till, flags)
48
+ S->>S: (optional) POST registration webhook { coreId, refId? } (registrationWebhookRetries, default 3)
49
+ Note over S: If registrationWebhookSecret is set, include HMAC headers:\nX-Webhook-Timestamp + X-Webhook-Signature
50
+ S-->>A: 200 ok
51
+ ```
52
+
53
+ ### Login flow (standard Auth.js WebAuthn authenticate)
54
+
55
+ CorePass login is normal WebAuthn: it uses the Auth.js WebAuthn callback path (`action=authenticate`), and resolves the user by stored authenticators.
56
+
57
+ ```mermaid
58
+ sequenceDiagram
59
+ autonumber
60
+ actor B as Browser
61
+ participant Auth as Auth.js (@auth/core)
62
+ participant DB as Adapter DB
63
+
64
+ B->>Auth: GET /auth/webauthn-options?action=authenticate (provider=corepass)
65
+ Auth->>DB: listAuthenticatorsByUserId (optional) / challenge cookie
66
+ Auth-->>B: 200 RequestOptions + challenge cookie
67
+ B->>B: navigator.credentials.get()
68
+ B->>Auth: POST /auth/callback/corepass { action:"authenticate", data }
69
+ Auth->>DB: getAuthenticator(credentialId) + verifyAuthenticationResponse()
70
+ Auth->>DB: updateAuthenticatorCounter()
71
+ Auth-->>B: session established
72
+ Note over Auth: (optional) POST login webhook { coreId, refId? } (loginWebhookRetries, default 3)
73
+ ```
74
+
75
+ ## Install
76
+
77
+ ```bash
78
+ npm install authjs-corepass-provider
79
+ ```
80
+
81
+ You also need:
82
+
83
+ - `@auth/core` (peer dependency)
84
+ - `@simplewebauthn/browser` in your frontend
85
+
86
+ ## Auth.js configuration
87
+
88
+ ```ts
89
+ import { Auth } from "@auth/core"
90
+ import CorePass from "authjs-corepass-provider/provider"
91
+
92
+ export const auth = (req: Request) =>
93
+ Auth(req, {
94
+ providers: [CorePass()],
95
+ adapter: /* your Auth.js adapter */,
96
+ })
97
+ ```
98
+
99
+ ## CorePass endpoints
100
+
101
+ You mount these where you want in your app (framework-specific). The handlers are plain Web API `Request -> Response`.
102
+
103
+ ```ts
104
+ import { createCorePassServer } from "authjs-corepass-provider"
105
+
106
+ const corepass = createCorePassServer({
107
+ adapter: /* Auth.js adapter (must implement WebAuthn + user methods) */,
108
+ // store must implement CorePassStore:
109
+ // - pending registrations (default flow)
110
+ // - coreId <-> userId identity mapping
111
+ // - profile metadata (o18y/o21y/kyc/provided_till)
112
+ //
113
+ // Built-ins:
114
+ // - d1CorePassStore(db) for Cloudflare D1
115
+ // - postgresCorePassStore(pg) for Postgres (node-postgres, etc)
116
+ // - supabaseCorePassStore(supabase) for Supabase client
117
+ store: /* CorePassStore implementation */,
118
+ challengeStore: /* CorePassChallengeStore implementation (KV/Redis/etc) */,
119
+ rpID: "example.com",
120
+ rpName: "Example",
121
+ expectedOrigin: "https://example.com",
122
+
123
+ // default: pending registrations are required
124
+ allowImmediateFinalize: false,
125
+ })
126
+
127
+ // Optional: login webhook (call from Auth.js events.signIn)
128
+ // events: {
129
+ // async signIn({ user, account }) {
130
+ // if (account?.provider === "corepass" && account?.type === "webauthn" && user?.id) {
131
+ // await corepass.postLoginWebhook({ userId: user.id })
132
+ // }
133
+ // }
134
+ // }
135
+ //
136
+ // Optional: logout webhook (call from Auth.js events.signOut)
137
+ // events: {
138
+ // async signOut({ session }) {
139
+ // // You must be able to map the logout event to a userId.
140
+ // // How you obtain userId depends on your Auth.js setup/session strategy.
141
+ // // If you have it:
142
+ // // await corepass.postLogoutWebhook({ userId })
143
+ // }
144
+ // }
145
+
146
+ export async function POST(req: Request) {
147
+ const url = new URL(req.url)
148
+ if (url.pathname === "/webauthn/start") return corepass.startRegistration(req)
149
+ if (url.pathname === "/webauthn/finish") return corepass.finishRegistration(req)
150
+ if (url.pathname === "/passkey/data") return corepass.enrichRegistration(req)
151
+ return new Response("Not found", { status: 404 })
152
+ }
153
+ ```
154
+
155
+ ### “Unified” server factory helpers (same DB client)
156
+
157
+ If you want to avoid manually wiring `store: d1CorePassStore(...)` etc, you can use the factories:
158
+
159
+ - `createCorePassServerD1({ db, ... })`
160
+ - `createCorePassServerPostgres({ pg, ... })`
161
+ - `createCorePassServerSupabase({ supabase, ... })`
162
+ - `createCorePassServerCloudflareD1Kv({ db, kv, ... })`
163
+ - `createCorePassServerPostgresRedis({ pg, redis, ... })`
164
+ - `createCorePassServerSupabaseUpstash({ supabase, redis, ... })`
165
+ - `createCorePassServerSupabaseVercelKv({ supabase, kv, ... })`
166
+
167
+ This does **not** create an Auth.js adapter for you (adapters are separate packages), but it ensures the CorePass
168
+ store uses the same DB client you pass in.
169
+
170
+ ## `challengeStore` (what it is, and what it supports)
171
+
172
+ `challengeStore` is **not an Auth.js provider** and it is **not tied to WebAuthn/Passkey provider IDs**.
173
+ It’s a minimal storage interface used by this package’s custom endpoints to persist the WebAuthn challenge
174
+ between:
175
+
176
+ - `POST /webauthn/start` (generate challenge)
177
+ - `POST /webauthn/finish` (verify attestation against expected challenge)
178
+
179
+ It must support **TTL** (seconds) and **delete on use**.
180
+
181
+ ### How to wire it to real systems
182
+
183
+ You create/access your KV/Redis client *in your runtime* and pass it into the helper:
184
+
185
+ - Cloudflare Workers: use an **`env` binding**
186
+ - Node/Next.js/etc: create a **Redis client** using URL/token from env vars
187
+
188
+ ### Example: in-memory (development only)
189
+
190
+ ```ts
191
+ import { memoryChallengeStore } from "authjs-corepass-provider"
192
+ ```
193
+
194
+ ### Example: Redis / Upstash
195
+
196
+ ```ts
197
+ import { redisChallengeStore } from "authjs-corepass-provider"
198
+ import { Redis } from "ioredis"
199
+
200
+ const redis = new Redis(process.env.REDIS_URL!)
201
+
202
+ const challengeStore = redisChallengeStore({
203
+ set: (key, value, { ex }) => redis.set(key, value, "EX", ex),
204
+ get: (key) => redis.get(key),
205
+ del: (key) => redis.del(key),
206
+ })
207
+ ```
208
+
209
+ ### Example: Cloudflare KV
210
+
211
+ ```ts
212
+ import { kvChallengeStore } from "authjs-corepass-provider"
213
+
214
+ // wrangler.jsonc:
215
+ // {
216
+ // "kv_namespaces": [
217
+ // { "binding": "COREPASS_KV", "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }
218
+ // ]
219
+ // }
220
+ //
221
+ // Worker handler: env.COREPASS_KV is a KVNamespace binding.
222
+ //
223
+ // const challengeStore = kvChallengeStore(env.COREPASS_KV)
224
+ ```
225
+
226
+ ### Example: Vercel KV (Redis)
227
+
228
+ ```ts
229
+ import { vercelKvChallengeStore } from "authjs-corepass-provider"
230
+ import { kv } from "@vercel/kv"
231
+
232
+ // Vercel manages connection details via environment variables.
233
+ // See Vercel KV setup for the required env vars.
234
+
235
+ const challengeStore = vercelKvChallengeStore({
236
+ set: (key, value, { ex }) => kv.set(key, value, { ex }),
237
+ get: (key) => kv.get<string>(key),
238
+ del: (key) => kv.del(key),
239
+ })
240
+ ```
241
+
242
+ ### Example: Upstash Redis REST (`@upstash/redis`)
243
+
244
+ ```ts
245
+ import { upstashRedisChallengeStore } from "authjs-corepass-provider"
246
+ import { Redis } from "@upstash/redis"
247
+
248
+ const redis = new Redis({
249
+ url: process.env.UPSTASH_REDIS_REST_URL!,
250
+ token: process.env.UPSTASH_REDIS_REST_TOKEN!,
251
+ })
252
+
253
+ const challengeStore = upstashRedisChallengeStore({
254
+ set: (key, value, { ex }) => redis.set(key, value, { ex }),
255
+ get: (key) => redis.get<string>(key),
256
+ del: (key) => redis.del(key),
257
+ })
258
+ ```
259
+
260
+ ### Example: Cloudflare Durable Objects
261
+
262
+ Durable Objects are a good fit if you want low-latency ephemeral state close to your Worker.
263
+
264
+ ```ts
265
+ import { durableObjectChallengeStore } from "authjs-corepass-provider"
266
+
267
+ // Your Durable Object must implement these routes:
268
+ // - POST /challenge/put { key, value, ttlSeconds }
269
+ // - GET /challenge/get?key=...
270
+ // - POST /challenge/delete { key }
271
+ //
272
+ // Then:
273
+ // const challengeStore = durableObjectChallengeStore(env.COREPASS_DO.get(id))
274
+ ```
275
+
276
+ ### Example: DynamoDB (AWS SDK v3)
277
+
278
+ If you already run on AWS and want a managed KV with TTL:
279
+
280
+ ```ts
281
+ import { dynamoChallengeStore } from "authjs-corepass-provider"
282
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
283
+ import { DynamoDBDocumentClient, PutCommand, GetCommand, DeleteCommand } from "@aws-sdk/lib-dynamodb"
284
+
285
+ const TableName = process.env.COREPASS_CHALLENGE_TABLE!
286
+ const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}))
287
+ const nowSec = () => Math.floor(Date.now() / 1000)
288
+
289
+ // This library doesn't hard-depend on AWS SDK; you provide a small adapter:
290
+ const challengeStore = dynamoChallengeStore({
291
+ put: ({ key, value, expiresAt }) =>
292
+ ddb.send(new PutCommand({ TableName, Item: { pk: key, value, expiresAt } })),
293
+ get: async (key) => {
294
+ const res = await ddb.send(new GetCommand({ TableName, Key: { pk: key } }))
295
+ const item = res.Item as { value?: string; expiresAt?: number } | undefined
296
+ if (!item?.value || typeof item.expiresAt !== "number") return null
297
+ if (item.expiresAt < nowSec()) return null
298
+ return { value: item.value, expiresAt: item.expiresAt }
299
+ },
300
+ delete: (key) => ddb.send(new DeleteCommand({ TableName, Key: { pk: key } })),
301
+ })
302
+ ```
303
+
304
+ ### Example: SQL / D1
305
+
306
+ Use a table like:
307
+
308
+ ```sql
309
+ CREATE TABLE IF NOT EXISTS corepass_challenges (
310
+ key TEXT PRIMARY KEY,
311
+ value TEXT NOT NULL,
312
+ expires_at INTEGER NOT NULL
313
+ );
314
+ CREATE INDEX IF NOT EXISTS idx_corepass_challenges_expires_at ON corepass_challenges(expires_at);
315
+ ```
316
+
317
+ Then implement:
318
+
319
+ ```ts
320
+ import type { CorePassChallengeStore } from "authjs-corepass-provider"
321
+
322
+ export function sqlChallengeStore(db: {
323
+ exec: (sql: string, params?: unknown[]) => Promise<unknown>
324
+ get: (sql: string, params?: unknown[]) => Promise<{ value: string; expires_at: number } | null>
325
+ }): CorePassChallengeStore {
326
+ const nowSec = () => Math.floor(Date.now() / 1000)
327
+
328
+ return {
329
+ async put(key, value, ttlSeconds) {
330
+ const expiresAt = nowSec() + ttlSeconds
331
+ await db.exec(
332
+ "INSERT OR REPLACE INTO corepass_challenges (key, value, expires_at) VALUES (?1, ?2, ?3)",
333
+ [key, value, expiresAt]
334
+ )
335
+ },
336
+ async get(key) {
337
+ const row = await db.get(
338
+ "SELECT value, expires_at FROM corepass_challenges WHERE key = ?1",
339
+ [key]
340
+ )
341
+ if (!row) return null
342
+ if (row.expires_at < nowSec()) {
343
+ await db.exec("DELETE FROM corepass_challenges WHERE key = ?1", [key])
344
+ return null
345
+ }
346
+ return row.value
347
+ },
348
+ async delete(key) {
349
+ await db.exec("DELETE FROM corepass_challenges WHERE key = ?1", [key])
350
+ },
351
+ }
352
+ }
353
+ ```
354
+
355
+ ## Database
356
+
357
+ Apply your adapter’s default Auth.js schema, then apply:
358
+
359
+ - `db/corepass-schema.sql` (SQLite/D1)
360
+ - `db/corepass-schema.postgres.sql` (PostgreSQL/Supabase)
361
+
362
+ This adds:
363
+
364
+ - `corepass_pending_registrations`
365
+ - `corepass_identities` (CoreID → Auth.js `userId` mapping)
366
+ - `corepass_profiles` (CorePass metadata like `o18y`, `kyc`, `provided_till`)
367
+
368
+ ## Options
369
+
370
+ - **`allowedAaguids`**: defaults to CorePass AAGUID `636f7265-7061-7373-6964-656e74696679`. Set to `false` to allow any authenticator.
371
+ - **`pubKeyCredAlgs`**: defaults to `[-257, -7, -8]` (RS256, ES256, Ed25519).
372
+ - **`allowImmediateFinalize`**: if enabled, `finishRegistration` may finalize immediately if `coreId` is provided in the browser payload. This is **disabled by default** because it weakens the CoreID ownership guarantee (the default flow requires the Ed448-signed `/passkey/data` request).
373
+ - **`emailRequired`**: defaults to `false` (email can arrive later via `/passkey/data`). If no email is ever provided, the library creates the Auth.js user with a deterministic synthetic email and updates it once a real email is received.
374
+ - **`requireO18y`**: defaults to `false`. If enabled, `/passkey/data` must include `userData.o18y=true` or finalization is rejected. Not enforced for immediate-finalize.
375
+ - **`requireO21y`**: defaults to `false`. If enabled, `/passkey/data` must include `userData.o21y=true` or finalization is rejected. Not enforced for immediate-finalize.
376
+ - **`requireKyc`**: defaults to `false`. If enabled, `/passkey/data` must include `userData.kyc=true` or finalization is rejected. Not enforced for immediate-finalize.
377
+ - **`enableRefId`**: defaults to `false`. When enabled, the server generates and stores a `refId` (UUIDv4) for the CoreID identity and can include it in webhooks. When disabled, no `refId` is generated or stored.
378
+ - **Registration webhook options**:
379
+ - **`postRegistrationWebhooks`**: defaults to `false`.
380
+ - **`registrationWebhookUrl`**: required if `postRegistrationWebhooks: true`.
381
+ - **`registrationWebhookSecret`**: optional. If set, requests are HMAC-signed (SHA-256) using `timestamp + "\\n" + body` and include `X-Webhook-Timestamp` (unix seconds) and `X-Webhook-Signature` (`sha256=<hex>`).
382
+ - **`registrationWebhookRetries`**: defaults to `3` (range `1-10`). Retries happen on non-2xx responses or network errors.
383
+ - **Login webhook options**:
384
+ - **`postLoginWebhooks`**: defaults to `false`.
385
+ - **`loginWebhookUrl`**: required if `postLoginWebhooks: true`.
386
+ - **`loginWebhookSecret`**: optional. Same signing format/headers as registration.
387
+ - **`loginWebhookRetries`**: defaults to `3` (range `1-10`). Retries happen on non-2xx responses or network errors.
388
+ - **Logout webhook options**:
389
+ - **`postLogoutWebhooks`**: defaults to `false`.
390
+ - **`logoutWebhookUrl`**: required if `postLogoutWebhooks: true`.
391
+ - **`logoutWebhookSecret`**: optional. Same signing format/headers as registration.
392
+ - **`logoutWebhookRetries`**: defaults to `3` (range `1-10`). Retries happen on non-2xx responses or network errors.
393
+ - **`pendingTtlSeconds`**: defaults to `600` (10 minutes). Pending registrations expire after this and are dropped.
394
+ - **`timestampWindowMs`**: defaults to `600000` (10 minutes). Enrichment `timestamp` must be within this window.
395
+
396
+ ## Enrichment payload (`/passkey/data`)
397
+
398
+ The CorePass app sends:
399
+
400
+ - **Body**: `{ coreId, credentialId, timestamp, userData }`
401
+ - **Header**: `X-Signature` (Ed448 signature)
402
+
403
+ ### Canonical payload + signature input
404
+
405
+ For signature verification, the server **does not** use the raw request body bytes. Instead it:
406
+
407
+ - **Canonicalizes JSON**: recursively sorts object keys alphabetically and serializes with `JSON.stringify(...)` (so it is **minified**, no whitespace).
408
+ - **Builds signature input** as:
409
+
410
+ ```text
411
+ signatureInput = "POST\n" + signaturePath + "\n" + canonicalJsonBody
412
+ ```
413
+
414
+ Then it verifies `X-Signature` (Ed448) over `UTF-8(signatureInput)`.
415
+
416
+ This means the CorePass signer must sign the **same canonical JSON string** (alphabetically ordered + minified) and the same `signaturePath` (defaults to `/passkey/data`, configurable via `signaturePath`).
417
+
418
+ ### Timestamp units
419
+
420
+ `timestamp` is required and must be a **Unix timestamp in microseconds**.
421
+
422
+ `userData` fields:
423
+
424
+ | Field | Type | Example | Notes |
425
+ | - | - | - | - |
426
+ | `email` | `string` | `user@example.com` | Optional. If provided later, Auth.js user email is updated. |
427
+ | `o18y` | `boolean (or 0/1)` | `true` | Stored in `corepass_profiles.o18y`. |
428
+ | `o21y` | `boolean (or 0/1)` | `false` | Stored in `corepass_profiles.o21y`. |
429
+ | `kyc` | `boolean (or 0/1)` | `true` | Stored in `corepass_profiles.kyc`. |
430
+ | `kycDoc` | `string` | `PASSPORT` | Stored in `corepass_profiles.kyc_doc`. |
431
+ | `dataExp` | `number` | `43829` | Minutes. Converted to `provided_till`. |
432
+
433
+ `refId` is **not part of CorePass `/passkey/data`**. If you need an external correlation id, enable `enableRefId` and deliver it via your webhooks.
434
+
435
+ ### `provided_till` calculation
436
+
437
+ `provided_till` is stored as a **Unix timestamp in seconds**:
438
+
439
+ ```text
440
+ provided_till = floor(now_sec) + dataExpMinutes * 60
441
+ ```
442
+
443
+ ## Notes on Auth.js internals
444
+
445
+ Auth.js’ built-in WebAuthn flow normally creates the user/account/authenticator during the WebAuthn callback. CorePass intentionally delays this until enrichment, so it uses custom endpoints instead of Auth.js’ built-in “register” callback path.
446
+
447
+ ## Upstream references
448
+
449
+ - Auth.js contributing guide: `https://raw.githubusercontent.com/nextauthjs/.github/main/CONTRIBUTING.md`
450
+ - Auth.js built-in Passkey provider: `https://raw.githubusercontent.com/nextauthjs/next-auth/main/packages/core/src/providers/passkey.ts`
@@ -0,0 +1,46 @@
1
+ -- CorePass extension tables for Auth.js (PostgreSQL)
2
+ --
3
+ -- Apply this *in addition to* your adapter's default schema.
4
+ --
5
+ -- Tables added:
6
+ -- - corepass_identities: maps CoreID to your Auth.js userId
7
+ -- - corepass_profiles: stores CorePass-specific user metadata
8
+ -- - corepass_pending_registrations: stores passkey registrations until /passkey/data enrichment finalizes them
9
+
10
+ CREATE TABLE IF NOT EXISTS corepass_identities (
11
+ core_id TEXT PRIMARY KEY,
12
+ user_id TEXT NOT NULL UNIQUE,
13
+ ref_id UUID,
14
+ created_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT),
15
+ updated_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT)
16
+ );
17
+
18
+ CREATE TABLE IF NOT EXISTS corepass_profiles (
19
+ user_id TEXT PRIMARY KEY,
20
+ core_id TEXT NOT NULL UNIQUE,
21
+ o18y BOOLEAN,
22
+ o21y BOOLEAN,
23
+ kyc BOOLEAN,
24
+ kyc_doc TEXT,
25
+ provided_till BIGINT,
26
+ created_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT),
27
+ updated_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT)
28
+ );
29
+
30
+ CREATE TABLE IF NOT EXISTS corepass_pending_registrations (
31
+ token UUID PRIMARY KEY,
32
+ credential_id TEXT NOT NULL UNIQUE,
33
+ credential_public_key TEXT NOT NULL,
34
+ counter BIGINT NOT NULL DEFAULT 0,
35
+ credential_device_type TEXT NOT NULL,
36
+ credential_backed_up BOOLEAN NOT NULL DEFAULT FALSE,
37
+ transports TEXT,
38
+ email TEXT,
39
+ ref_id UUID,
40
+ aaguid UUID,
41
+ created_at BIGINT NOT NULL DEFAULT (EXTRACT(EPOCH FROM NOW())::BIGINT),
42
+ expires_at BIGINT NOT NULL
43
+ );
44
+
45
+ CREATE INDEX IF NOT EXISTS idx_corepass_pending_expires_at
46
+ ON corepass_pending_registrations(expires_at);
@@ -0,0 +1,50 @@
1
+ -- CorePass extension tables for Auth.js
2
+ --
3
+ -- This file intentionally does NOT include the Auth.js default tables.
4
+ -- Apply this *in addition to* your adapter's default schema.
5
+ --
6
+ -- Tables added:
7
+ -- - corepass_identities: maps CoreID to your Auth.js userId (adapter-generated or otherwise)
8
+ -- - corepass_profiles: stores CorePass-specific user metadata
9
+ -- - corepass_pending_registrations: stores passkey registrations until /passkey/data enrichment finalizes them
10
+
11
+ -- CoreID -> userId mapping
12
+ CREATE TABLE IF NOT EXISTS corepass_identities (
13
+ core_id TEXT PRIMARY KEY,
14
+ user_id TEXT NOT NULL UNIQUE,
15
+ ref_id TEXT,
16
+ created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
17
+ updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
18
+ );
19
+
20
+ -- CorePass user metadata (optional)
21
+ CREATE TABLE IF NOT EXISTS corepass_profiles (
22
+ user_id TEXT PRIMARY KEY,
23
+ core_id TEXT NOT NULL UNIQUE,
24
+ o18y INTEGER,
25
+ o21y INTEGER,
26
+ kyc INTEGER,
27
+ kyc_doc TEXT,
28
+ provided_till INTEGER,
29
+ created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
30
+ updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
31
+ );
32
+
33
+ -- Pending registrations (default CorePass flow)
34
+ CREATE TABLE IF NOT EXISTS corepass_pending_registrations (
35
+ token TEXT PRIMARY KEY,
36
+ credential_id TEXT NOT NULL UNIQUE, -- base64 credential id
37
+ credential_public_key TEXT NOT NULL, -- base64 public key
38
+ counter INTEGER NOT NULL DEFAULT 0,
39
+ credential_device_type TEXT NOT NULL,
40
+ credential_backed_up INTEGER NOT NULL DEFAULT 0,
41
+ transports TEXT,
42
+ email TEXT,
43
+ ref_id TEXT,
44
+ aaguid TEXT,
45
+ created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
46
+ expires_at INTEGER NOT NULL
47
+ );
48
+
49
+ CREATE INDEX IF NOT EXISTS idx_corepass_pending_expires_at
50
+ ON corepass_pending_registrations(expires_at);
@@ -0,0 +1,33 @@
1
+ // src/provider.ts
2
+ import WebAuthn, {
3
+ DEFAULT_WEBAUTHN_TIMEOUT
4
+ } from "@auth/core/providers/webauthn";
5
+ function CorePass(config = {}) {
6
+ return WebAuthn({
7
+ id: "corepass",
8
+ name: "CorePass",
9
+ authenticationOptions: {
10
+ timeout: DEFAULT_WEBAUTHN_TIMEOUT,
11
+ userVerification: "required"
12
+ },
13
+ registrationOptions: {
14
+ timeout: DEFAULT_WEBAUTHN_TIMEOUT,
15
+ authenticatorSelection: {
16
+ residentKey: "required",
17
+ userVerification: "required"
18
+ }
19
+ },
20
+ verifyAuthenticationOptions: {
21
+ requireUserVerification: true
22
+ },
23
+ verifyRegistrationOptions: {
24
+ requireUserVerification: true
25
+ },
26
+ ...config
27
+ });
28
+ }
29
+
30
+ export {
31
+ CorePass
32
+ };
33
+ //# sourceMappingURL=chunk-72ZEQHQO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider.ts"],"sourcesContent":["import WebAuthn, {\n\tDEFAULT_WEBAUTHN_TIMEOUT,\n\ttype WebAuthnConfig,\n} from \"@auth/core/providers/webauthn\"\n\n/**\n * CorePass Auth.js provider.\n *\n * This provider is a thin wrapper around Auth.js' built-in WebAuthn provider with\n * Passkey-friendly defaults. CorePass' pending-registration + enrichment flow is\n * implemented via the server helpers exported from this package (see `createCorePassServer`).\n */\nexport default function CorePass(\n\tconfig: Partial<WebAuthnConfig> = {}\n): WebAuthnConfig {\n\treturn WebAuthn({\n\t\tid: \"corepass\",\n\t\tname: \"CorePass\",\n\t\tauthenticationOptions: {\n\t\t\ttimeout: DEFAULT_WEBAUTHN_TIMEOUT,\n\t\t\tuserVerification: \"required\",\n\t\t},\n\t\tregistrationOptions: {\n\t\t\ttimeout: DEFAULT_WEBAUTHN_TIMEOUT,\n\t\t\tauthenticatorSelection: {\n\t\t\t\tresidentKey: \"required\",\n\t\t\t\tuserVerification: \"required\",\n\t\t\t},\n\t\t},\n\t\tverifyAuthenticationOptions: {\n\t\t\trequireUserVerification: true,\n\t\t},\n\t\tverifyRegistrationOptions: {\n\t\t\trequireUserVerification: true,\n\t\t},\n\t\t...config,\n\t})\n}\n"],"mappings":";AAAA,OAAO;AAAA,EACN;AAAA,OAEM;AASQ,SAAR,SACN,SAAkC,CAAC,GAClB;AACjB,SAAO,SAAS;AAAA,IACf,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,uBAAuB;AAAA,MACtB,SAAS;AAAA,MACT,kBAAkB;AAAA,IACnB;AAAA,IACA,qBAAqB;AAAA,MACpB,SAAS;AAAA,MACT,wBAAwB;AAAA,QACvB,aAAa;AAAA,QACb,kBAAkB;AAAA,MACnB;AAAA,IACD;AAAA,IACA,6BAA6B;AAAA,MAC5B,yBAAyB;AAAA,IAC1B;AAAA,IACA,2BAA2B;AAAA,MAC1B,yBAAyB;AAAA,IAC1B;AAAA,IACA,GAAG;AAAA,EACJ,CAAC;AACF;","names":[]}