Skip to content

Conversation

@lorenzocorallo
Copy link
Member

No description provided.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

Walkthrough

Adds passkey authentication: new DB migration creating auth_passkey with indexes and FK, registers @better-auth/passkey plugin in auth config, renames auth schema exports from plural to singular, updates references, and bumps package version.

Changes

Cohort / File(s) Summary
Database Migrations
drizzle/0006_uneven_lord_hawal.sql, drizzle/meta/0006_snapshot.json, drizzle/meta/_journal.json
Adds auth_passkey table (id, name, public_key, user_id, credential_id, counter, device_type, backed_up, transports, created_at, aaguid) with FK to public.auth_users(id) (ON DELETE CASCADE). Adds indexes on passkey.user_id and passkey.credential_id; adds/ensures indexes on existing auth tables and sets created_at/updated_at NOT NULL on verifications. Adds migration snapshot and journal entry.
Package Files
package.json, package/package.json
Bumps version 0.15.2 → 0.15.3 and adds dependency @better-auth/passkey@^1.4.17.
Auth Config & Adapter
src/auth/index.ts
Imports passkey from @better-auth/passkey, registers passkey plugin in auth configuration with rpID/rpName, and changes drizzleAdapter schema mapping to spread ...SCHEMA.AUTH.
Auth Schema (renames + passkey)
src/db/schema/auth/auth.ts
Renames exported schema constants from plural → singular (usersuser, sessionssession, accountsaccount, verificationsverification). Adds passkey table and relation exports (userRelations, sessionRelations, accountRelations, passkeyRelations). Adds indices and reference callbacks, updates foreign keys to reference user.id, and sets explicit onDelete: "cascade" for relevant FKs.
Schema Reference Updates
src/db/schema/tg/link.ts, src/routers/tg/link.ts
Updates imports and references from usersuser to match renamed auth schema exports.
sequenceDiagram
    participant Client as "Client (Browser)"
    participant Server as "App Server\n(auth plugin)"
    participant DB as "Database"
    participant PasskeyLib as "passkey lib\n(`@better-auth/passkey`)"

    rect rgba(63,81,181,0.5)
    Client->>Server: Initiate passkey registration/login
    Server->>PasskeyLib: Create challenge / verify response
    PasskeyLib-->>Server: Challenge / Verification result
    Server->>DB: Insert/Update `auth_passkey` record (credential, user_id, counters, meta)
    DB-->>Server: ACK
    Server-->>Client: Success response (auth completed)
    end
Loading
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding passkey plugin support to the authentication system.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@package.json`:
- Line 41: Update the better-auth dependency in package.json to match the
`@better-auth/passkey` peer requirement: change "better-auth" from ^1.4.15 to
^1.4.17 so it aligns with "@better-auth/passkey": "^1.4.17" and avoids a
peerDependency mismatch; ensure both dependency entries (better-auth and
`@better-auth/passkey`) use the same major.minor.patch range (^1.4.17) and run
npm/yarn install to validate.

In `@src/auth/index.ts`:
- Around line 23-29: The drizzle adapter schema in src/auth/index.ts is missing
the passkey table mapping needed by `@better-auth/passkey`; update the
configuration passed to drizzleAdapter (the database: drizzleAdapter(db, {
provider: "pg", schema: { ... } }) call) to include a passkey property, e.g. add
passkey: SCHEMA.AUTH.passkey alongside user, account, session, and verification
so the adapter knows about the passkey table.
- Around line 18-21: Replace the hard-coded rpID in the passkey(...) call with a
derived hostname from the configured base URL: read env.PUBLIC_URL, parse it
(e.g., via new URL(env.PUBLIC_URL).hostname) and use that value as rpID (with a
safe fallback to the current request origin or a default if parsing fails).
Update the passkey(...) invocation in src/auth/index.ts so rpID is computed at
runtime from env.PUBLIC_URL rather than the literal "polinetwork.org", keeping
rpName as-is.
🧹 Nitpick comments (3)
drizzle/0006_uneven_lord_hawal.sql (1)

1-13: Consider making created_at NOT NULL for consistency.

The created_at column (line 11) is nullable, while other auth tables (auth_users, auth_sessions, auth_accounts) define created_at as NOT NULL. If there's no specific reason for passkeys to have a nullable creation timestamp, consider adding the NOT NULL constraint for consistency.

Suggested fix
-	"created_at" timestamp,
+	"created_at" timestamp NOT NULL,
src/db/schema/auth/auth.ts (2)

85-103: Table naming inconsistency: "passkey" (singular) vs other tables using plural.

Other tables use plural names ("users", "sessions", "accounts", "verifications"), but this table uses singular "passkey". Consider using "passkeys" for consistency across the auth schema.

Suggested fix
 export const passkey = createTable.auth(
-  "passkey",
+  "passkeys",
   {

Note: This would require updating the SQL migration to use auth_passkeys as well.


99-100: Consider making createdAt non-nullable to align with other auth tables.

The passkey table has a nullable createdAt, while all other auth tables (user, session, account, verification) use .notNull(). The absence of updatedAt follows the better-auth passkey plugin schema (which does not include this field), so that's intentional. However, the nullable createdAt inconsistency should be addressed:

Suggested fix
   transports: text("transports"),
-  createdAt: timestamp("created_at"),
+  createdAt: timestamp("created_at").notNull(),
   aaguid: text("aaguid"),

},
"dependencies": {
"@azure/identity": "^4.13.0",
"@better-auth/passkey": "^1.4.17",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

@better-auth/passkey peerDependencies better-auth compatibility

💡 Result:

@better-auth/passkey is version-pinned to better-auth via peerDependencies, so you generally need to keep the versions in lockstep.

For example, @better-auth/passkey@1.4.15 declares these peer deps (must be satisfied by your app, not installed automatically):

  • better-auth: 1.4.15
  • @better-auth/core: 1.4.15
  • @better-auth/utils: 0.3.0
  • @better-fetch/fetch: 1.1.21
  • better-call: 1.1.8
  • nanostores: ^1.0.1 [1]

Compatibility rule of thumb: use the same version number for better-auth, @better-auth/core, and @better-auth/passkey (e.g., all 1.4.15). If your project is on a newer better-auth (Snyk reports 1.4.17 as latest for better-auth), you’ll likely need the matching @better-auth/passkey release (or you’ll hit peer-dep conflicts). [2]

Where to check your exact version’s peer deps: the plugin’s published package.json on jsDelivr for that specific version. [1]

Sources: [1] [2]


Align better-auth version with @better-auth/passkey.

@better-auth/passkey is version-pinned to better-auth via peerDependencies and requires matching versions. Adding @better-auth/passkey ^1.4.17 while better-auth is ^1.4.15 risks a peer dependency mismatch—if better-auth resolves to 1.4.15, the passkey package will fail due to incompatible peer dependencies. Update better-auth to ^1.4.17 to ensure version alignment.

🤖 Prompt for AI Agents
In `@package.json` at line 41, Update the better-auth dependency in package.json
to match the `@better-auth/passkey` peer requirement: change "better-auth" from
^1.4.15 to ^1.4.17 so it aligns with "@better-auth/passkey": "^1.4.17" and
avoids a peerDependency mismatch; ensure both dependency entries (better-auth
and `@better-auth/passkey`) use the same major.minor.patch range (^1.4.17) and run
npm/yarn install to validate.

Comment on lines 18 to 21
passkey({
rpID: "polinetwork.org",
rpName: "PoliNetwork APS",
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's read the src/auth/index.ts file to see the full context
cat -n src/auth/index.ts | head -50

Repository: PoliNetworkOrg/backend

Length of output: 1888


🏁 Script executed:

# Check if env.PUBLIC_URL is defined and used elsewhere
rg "PUBLIC_URL" -A 2 -B 2

Repository: PoliNetworkOrg/backend

Length of output: 533


🏁 Script executed:

# Look for `@better-auth` package info and configuration patterns
cat -n package.json | grep -A 5 -B 5 "better-auth"

Repository: PoliNetworkOrg/backend

Length of output: 782


🏁 Script executed:

# Search for any other passkey or rpID configuration in the codebase
rg "rpID|rpName" -A 2 -B 2

Repository: PoliNetworkOrg/backend

Length of output: 280


🌐 Web query:

@better-auth passkey rpID configuration requirements

💡 Result:

For Better Auth’s @better-auth/passkey plugin, rpID must follow standard WebAuthn “Relying Party ID” rules:

  • Set rpID to a domain name that matches your auth server’s origin domain (the public site/domain users are authenticating to). localhost is allowed for local development. [1]
  • It must be an “effective domain” (registrable domain) or a subdomain of it: you may drop labels from the left, e.g. www.example.comwww.example.com or example.com. [1]
  • It cannot be a public suffix / eTLD (e.g., com is invalid). [1]
  • Related config: set origin to the exact origin where your Better Auth server is hosted (scheme + host + optional port), with no trailing slash. [1]

Sources: Better Auth Passkey plugin docs. [1]


🌐 Web query:

WebAuthn rpID effective domain matching requirement

💡 Result:

In WebAuthn, the RP ID (rpId) must domain-match the calling page’s origin as follows:

  • The RP ID is a valid domain string (host name only — no scheme, port, or path). [1]
  • By default, the RP ID is the caller origin’s effective domain. [1]
  • If you override it, the RP ID must be either:
    • exactly equal to the origin’s effective domain, or
    • a registrable domain suffix of the origin’s effective domain (i.e., you can broaden scope to a parent registrable domain, but you can’t choose a sibling or an unrelated domain). [1]
  • The origin’s scheme must be https (port is unrestricted). [1]
  • During authentication, the authenticator/browser ensures the ceremony uses the same rpId the credential was registered under. [1][2]

Example (from the spec): for origin https://login.example.com:1337, valid RP IDs are login.example.com (default) and example.com, but not m.login.example.com and not com. [1]

Sources:
[1] W3C WebAuthn Level 2 Recommendation (RP ID / effective domain + registrable-suffix rules) (w3.org)
[2] MDN: PublicKeyCredentialRequestOptions.rpId (must match credential’s rpId; defaults to origin domain) (developer.mozilla.org)

Citations:


🏁 Script executed:

# Check if there's any documentation or comments about passkey/rpID in the repo
rg "passkey|rpID|rpName" -B 5 -A 5 --type ts --type js

Repository: PoliNetworkOrg/backend

Length of output: 3073


🏁 Script executed:

# Check the betterAuth configuration docs or types
rg "baseURL|trustedOrigins" src/auth/index.ts -B 3 -A 3

Repository: PoliNetworkOrg/backend

Length of output: 239


🏁 Script executed:

# Look for any test files or configuration examples
find . -name "*.ts" -o -name "*.js" | xargs grep -l "passkey" 2>/dev/null | head -10

Repository: PoliNetworkOrg/backend

Length of output: 114


Derive rpID from baseURL to prevent WebAuthn registration failures across environments.

Hard-coding rpID to "polinetwork.org" conflicts with the configurable baseURL (set to env.PUBLIC_URL). Per WebAuthn spec, rpID must match the origin's effective domain—if PUBLIC_URL points to localhost, a staging domain, or any other host during development/preview, the hard-coded rpID will cause passkey registration to fail. Extract the hostname from env.PUBLIC_URL to make rpID match the active origin.

Example fix
+const rpID = new URL(env.PUBLIC_URL).hostname
+
 passkey({
-  rpID: "polinetwork.org",
+  rpID,
   rpName: "PoliNetwork APS",
 }),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
passkey({
rpID: "polinetwork.org",
rpName: "PoliNetwork APS",
}),
const rpID = new URL(env.PUBLIC_URL).hostname
passkey({
rpID,
rpName: "PoliNetwork APS",
}),
🤖 Prompt for AI Agents
In `@src/auth/index.ts` around lines 18 - 21, Replace the hard-coded rpID in the
passkey(...) call with a derived hostname from the configured base URL: read
env.PUBLIC_URL, parse it (e.g., via new URL(env.PUBLIC_URL).hostname) and use
that value as rpID (with a safe fallback to the current request origin or a
default if parsing fails). Update the passkey(...) invocation in
src/auth/index.ts so rpID is computed at runtime from env.PUBLIC_URL rather than
the literal "polinetwork.org", keeping rpName as-is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants