feat(auth): add login tokens for authentication#2703
feat(auth): add login tokens for authentication#2703Orvanix wants to merge 12 commits intoseerr-team:developfrom
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds login-token authentication: DB column and migrations, middleware and /auth/token endpoint, per-user token lifecycle routes, settings flag to allow self-generation, admin and user UI for token management, and OpenAPI documentation updates. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User/Client
participant FE as Frontend
participant Server as Server
participant DB as Database
User->>FE: Click "Generate Token"
FE->>Server: POST /user/{userId}/settings/token
activate Server
Server->>Server: Generate random token (randomUUID)
Server->>Server: Hash token (SHA-256)
Server->>DB: Store hashed loginToken
DB-->>Server: Confirmation
Server-->>FE: Return plaintext token
deactivate Server
FE->>User: Display token once (copy option)
sequenceDiagram
participant Browser as Client/Browser
participant FE as Frontend Server
participant Auth as Auth Route (/auth/token)
participant DB as Database
Browser->>FE: GET /some-page?token=abc123
FE->>Auth: POST /auth/token { token: "abc123" }
activate Auth
Auth->>Auth: Hash token (SHA-256)
Auth->>DB: Query user WHERE loginToken = hash
DB-->>Auth: Return user
Auth-->>FE: Set session (cookie) / return user
deactivate Auth
FE-->>Browser: 307 Redirect to same URL without token query
Browser->>FE: Follow redirect with session cookie
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📝 Coding Plan
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@server/entity/User.ts`:
- Around line 103-104: User.filteredFields does not include the new sensitive
persisted field loginToken, so any explicit select of loginToken can leak its
value via User.filter(); update the static User.filteredFields array to include
"loginToken" (or add it to whatever filtering set is used) so that User.filter()
and similar redaction logic will omit the loginToken value; ensure you update
any related serialization or toJSON paths that rely on filteredFields to prevent
accidental exposure.
In `@server/migration/sqlite/1773570023580-AddLoginToken.ts`:
- Line 9: The SQLite migration reintroduces an unintended UNIQUE constraint
named UQ_f90ab5a4ed54905a4bb51a7148b on "auth" inside the CREATE TABLE for
temporary_user_push_subscription; either remove that constraint from the
migration (delete occurrences of CONSTRAINT "UQ_f90ab5a4ed54905a4bb51a7148b"
UNIQUE ("auth") and any related ALTER/CREATE statements in
1773570023580-AddLoginToken.ts) to match the entity UserPushSubscription (which
only has `@Unique`(['endpoint','user'])) and the Postgres migration
1743023615532-UpdateWebPush.ts, or alternatively add `@Unique`(['auth']) to
server/entity/UserPushSubscription.ts so the schema intentionally includes the
auth uniqueness—choose and apply one consistent fix across the migration and
entity.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 420dbdd9-4213-4e9d-b2ed-1dd44af4c75b
📒 Files selected for processing (16)
docs/using-seerr/settings/users.mddocs/using-seerr/users/editing-users.mdseerr-api.ymlserver/entity/User.tsserver/interfaces/api/settingsInterfaces.tsserver/lib/settings/index.tsserver/middleware/auth.tsserver/migration/postgres/1773570292337-AddLoginToken.tsserver/migration/sqlite/1773570023580-AddLoginToken.tsserver/routes/auth.tsserver/routes/user/usersettings.tssrc/components/Settings/CopyButton.tsxsrc/components/Settings/SettingsUsers/index.tsxsrc/components/UserProfile/UserSettings/UserGeneralSettings/index.tsxsrc/context/SettingsContext.tsxsrc/pages/_app.tsx
…ed with TypeORM-generated ones
There was a problem hiding this comment.
🧹 Nitpick comments (2)
server/migration/sqlite/1773589719678-AddLoginToken.ts (1)
6-42: Redundantuser_push_subscriptiontable recreation.The migration recreates
user_push_subscriptiontwice inup()(lines 7-20 and 29-42). Since the only schema change is addingloginTokento theusertable, the second recreation (lines 29-42) after the user table modification appears unnecessary—the foreign key reference touserremains unchanged.This is likely auto-generated output from TypeORM's migration tooling. Consider simplifying to reduce migration runtime:
Simplified up() structure
public async up(queryRunner: QueryRunner): Promise<void> { - await queryRunner.query(`DROP INDEX "IDX_03f7958328e311761b0de675fb"`); - await queryRunner.query( - `CREATE TABLE "temporary_user_push_subscription" ...` - ); - // ... copy, drop, rename user_push_subscription ... - await queryRunner.query( - `CREATE INDEX "IDX_03f7958328e311761b0de675fb" ON "user_push_subscription" ("userId") ` - ); + // Only recreate user table with loginToken column await queryRunner.query( `CREATE TABLE "temporary_user" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, ... "loginToken" varchar, ...)` ); await queryRunner.query( `INSERT INTO "temporary_user"(...) SELECT ... FROM "user"` ); await queryRunner.query(`DROP TABLE "user"`); await queryRunner.query(`ALTER TABLE "temporary_user" RENAME TO "user"`); - // Remove second user_push_subscription recreation (lines 29-42) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/migration/sqlite/1773589719678-AddLoginToken.ts` around lines 6 - 42, The migration's up() duplicates the recreation of the user_push_subscription table (the CREATE/INSERT/DROP/ALTER/CREATE INDEX sequence that builds temporary_user_push_subscription and renames it to user_push_subscription appears twice); remove the second redundant block so the user_push_subscription changes happen only once and keep only the user table modification that adds loginToken (ensure the DROP INDEX/CREATE INDEX for "IDX_03f7958328e311761b0de675fb" is paired once). Locate the duplicated SQL sequences referencing temporary_user_push_subscription, user_push_subscription, and the index and delete the second occurrence, leaving a single, correct sequence in up().server/migration/postgres/1773589831561-AddLoginToken.ts (1)
6-10: Consider adding an index onloginTokenfor authentication lookups.The migration correctly adds the nullable
loginTokencolumn. However, since token-based authentication performs lookups by the hashed token value (POST /api/v1/auth/token), queries will scan the user table without an index. For larger user bases, this could impact authentication latency.Optional: Add index for token lookups
public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query( `ALTER TABLE "user" ADD "loginToken" character varying` ); + await queryRunner.query( + `CREATE INDEX "IDX_user_loginToken" ON "user" ("loginToken") WHERE "loginToken" IS NOT NULL` + ); }Note: If the expected user count is small (<1000), this optimization may not be necessary.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/migration/postgres/1773589831561-AddLoginToken.ts` around lines 6 - 10, The migration AddLoginToken.up currently adds the loginToken column but omits an index, causing full-table scans for token lookups; update the up method (where QueryRunner.query is used) to also create a non-unique index on "loginToken" (e.g., CREATE INDEX IF NOT EXISTS idx_user_login_token ON "user" ("loginToken")) and add the corresponding DROP INDEX (or DROP INDEX IF EXISTS) in the down method so the migration is reversible, ensuring token-based auth queries use the new index.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@server/migration/postgres/1773589831561-AddLoginToken.ts`:
- Around line 6-10: The migration AddLoginToken.up currently adds the loginToken
column but omits an index, causing full-table scans for token lookups; update
the up method (where QueryRunner.query is used) to also create a non-unique
index on "loginToken" (e.g., CREATE INDEX IF NOT EXISTS idx_user_login_token ON
"user" ("loginToken")) and add the corresponding DROP INDEX (or DROP INDEX IF
EXISTS) in the down method so the migration is reversible, ensuring token-based
auth queries use the new index.
In `@server/migration/sqlite/1773589719678-AddLoginToken.ts`:
- Around line 6-42: The migration's up() duplicates the recreation of the
user_push_subscription table (the CREATE/INSERT/DROP/ALTER/CREATE INDEX sequence
that builds temporary_user_push_subscription and renames it to
user_push_subscription appears twice); remove the second redundant block so the
user_push_subscription changes happen only once and keep only the user table
modification that adds loginToken (ensure the DROP INDEX/CREATE INDEX for
"IDX_03f7958328e311761b0de675fb" is paired once). Locate the duplicated SQL
sequences referencing temporary_user_push_subscription, user_push_subscription,
and the index and delete the second occurrence, leaving a single, correct
sequence in up().
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ce6f3e42-a511-468d-91d5-5a9ca25ac6dd
📒 Files selected for processing (2)
server/migration/postgres/1773589831561-AddLoginToken.tsserver/migration/sqlite/1773589719678-AddLoginToken.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/i18n/locale/en.json`:
- Line 1049: Update the tooltip value for key
"components.Settings.SettingsUsers.allowSelfLoginTokenTip" to accurately reflect
that token actions are governed by the MANAGE_USERS permission and cover more
than just generation (e.g., creation, management, and revocation), for example:
indicate that when disabled, only users with the MANAGE_USERS permission can
manage or create login tokens on behalf of other users. Ensure the new text
replaces the existing msg and mentions the MANAGE_USERS permission rather than
saying "admins" and broadens "generate" to "manage/create/revoke" (or similar
concise phrasing).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: eb1ccd3b-b0db-466e-96d2-d2ab6156e3f0
📒 Files selected for processing (1)
src/i18n/locale/en.json
Description
Adds login token authentication to Seerr, allowing users to authenticate via a direct URL without entering credentials.
This feature is intended for environments where providing secure credentials is difficult or impractical. For example, if you would like to use seerr on a home management tablet.
Features
Token Management
allowSelfLoginTokenis disabledAdmin Setting
allowSelfLoginTokenin Users settings controls whether users can generate their own tokensMANAGE_USERSpermission) can manage tokens on behalf of usersToken Login
https://your-instance/?token=your-tokenPOST /api/v1/auth/tokenHeader-based API Access
X-Login-Tokenheader support extended to use the newloginTokencolumnAPI Endpoints
/api/v1/user/{id}/settings/token/api/v1/user/{id}/settings/token/api/v1/user/{id}/settings/token/api/v1/auth/tokenUsage
```
https://your-instance/?token=your-uuid-token
```
Security Considerations
allowSelfLoginTokenis disabled by default — token management is admin-only unless explicitly enabledselect: falsecolumn option prevents the hash from appearing in any API responseHow Has This Been Tested?
/?token=...) logs in the user and redirects correctlyallowSelfLoginTokensetting correctly shows or hides the token section for non-admin usersScreenshots / Logs (if applicable)
Fresh generated Token

AI / Claude
Claude was used to help with implementation ideas, documentation, and drafting this summary and commit messages. It was also used to review parts of the code and suggest adjustments to better follow the conventions used in Seerr.
Checklist:
pnpm buildpnpm i18n:extractSummary by CodeRabbit
New Features
Documentation