Skip to content

Implement Webauthn #8393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 113 commits into
base: master
Choose a base branch
from
Open

Implement Webauthn #8393

wants to merge 113 commits into from

Conversation

robert-oleynik
Copy link
Contributor

@robert-oleynik robert-oleynik commented Feb 12, 2025

URL of deployed dev instance (used for testing):

Steps to test:

If your browser requires TLS:

  • Run gen-ssl-dev-certs.sh
  • Run yarn start-tls

TODOs:

  • Frontend
    • Login with WebAuthn
    • Register Keys
    • View Registered Keys
  • Backend:
    • Authenticate
    • Register
    • List Keys
    • Remove Keys
    • Prevent Blocking
  • Add option to disable passkeys
  • Make a nice UI
  • Bump Schema version to latest number

(Please delete unneeded items, merge only when none are left open)

@robert-oleynik robert-oleynik self-assigned this Feb 12, 2025
Copy link
Contributor

coderabbitai bot commented Feb 12, 2025

📝 Walkthrough

Walkthrough

This update introduces comprehensive support for passkey-based authentication using WebAuthn across the backend, frontend, and database. It adds new API endpoints, data models, DAOs, frontend components, configuration flags, and documentation to enable users to register, authenticate, list, and remove passkeys. Schema migrations and build changes are included.

Changes

Files / Areas Change Summary
app/controllers/AuthenticationController.scala Adds WebAuthn support: new endpoints, data models, controller logic, and refactoring for passkey flows.
app/models/user/WebAuthnCredentials.scala, app/models/user/MultiUser.scala Introduces WebAuthn credential model and DAO; adds method to MultiUserDAO for user lookup by ID.
conf/webknossos.latest.routes, conf/application.conf Adds WebAuthn-related API routes and a feature flag for passkeys.
frontend/javascripts/admin/auth/login_form.tsx, frontend/javascripts/types/api_types.ts Updates login form and API types to support passkeys; conditionally renders passkey login button.
frontend/javascripts/admin/account/account_password_view.tsx Passkeys section is now feature-flagged and dynamically renders passkey management.
frontend/javascripts/admin/auth/passkeys_view.tsx Adds a new React component for managing passkeys (list, add, remove) in the account view.
frontend/javascripts/admin/api/webauthn.ts New module for handling WebAuthn authentication and registration flows on frontend.
package.json, project/Dependencies.scala, build.sbt Adds dependencies for WebAuthn and scripts for TLS; applies dependency overrides in build.
tools/postgres/schema.sql, conf/evolutions/136-add-webauthn-credentials.sql Adds new table and view for WebAuthn credentials; migration script for schema version 136.
conf/evolutions/reversions/136-add-webauthn-credentials.sql Migration reversion script drops WebAuthn credential table and view.
webknossos-datastore/.../TemporaryStore.scala Adds a pop method to TemporaryStore for atomic retrieval and removal.
app/utils/WkConf.scala Adds passkeysEnabled feature flag to configuration.
tools/proxy/proxy.js Adds conditional TLS support to the proxy server for development.
docs/users/index.md, docs/users/passkeys.md Adds documentation for passkeys: overview, usage, and management.
unreleased_changes/8393.md Changelog entry summarizing the addition of passkey authentication.

Suggested labels

backend, frontend, new feature

Suggested reviewers

  • philippotto

Poem

🐇✨
Passkeys hop in, passwords out,
WebAuthn magic, no more doubt!
With routes and models, frontend flair,
Secure logins everywhere.
Database grows, docs explain,
Rabbits cheer this passwordless gain!
🥕🔑


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 766ea06 and c64692f.

📒 Files selected for processing (1)
  • conf/application.conf (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • conf/application.conf

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@robert-oleynik
Copy link
Contributor Author

robert-oleynik commented Jul 8, 2025

Security Considerations

https://w3c.github.io/webauthn/#sctn-security-considerations-rp

  • Visibility Considerations for Embedded Usage - A warning is shown for login views in iframe.
  • Cryptographic Challenges - Java's SecureRandom is used to generate challenges.
  • Attestation Limitations - Passkeys are disabled for non-HTTPS setups.
  • Revoked Attestation Certificates - Attestation Statement are passed and handled to webauthn4j.
  • Credential Loss and Key Mobility - Multiple keys per user are allowed, and different keys are enforced.
  • Unprotected account detection - Only client discoverable credentials are used and therefore, no allowCredentials are supplied.
  • Validating the origin of a credential - Validation is handled webauthn4j

Privacy Considerations

https://w3c.github.io/webauthn/#sctn-privacy-considerations-rp

  • User Handle Contents - ObjectId is used as userHandle instead of the user's e-mail.
  • Username Enumeration - The client must provide the correct userHandle + credential pair. Enumeration should not be possible.
  • Privacy leak via credential IDs - allowCredentials is always empty

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: 0

🧹 Nitpick comments (1)
tools/proxy/proxy.js (1)

2-2: Use Node.js import protocol for built-in modules.

Follow modern Node.js conventions by using the node: protocol for built-in module imports.

-const fs = require("fs");
+const fs = require("node:fs");
-const https = require("https");
+const https = require("node:https");

Also applies to: 7-7

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70a702c and 2eb5c53.

⛔ Files ignored due to path filters (1)
  • frontend/javascripts/test/backend-snapshot-tests/__snapshots__/misc.e2e.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • app/controllers/AuthenticationController.scala (7 hunks)
  • conf/evolutions/reversions/136-add-webauthn-credentials.sql (1 hunks)
  • project/Dependencies.scala (1 hunks)
  • tools/proxy/proxy.js (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • conf/evolutions/reversions/136-add-webauthn-credentials.sql
  • project/Dependencies.scala
🧰 Additional context used
🧠 Learnings (1)
app/controllers/AuthenticationController.scala (6)
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/NeuroglancerPrecomputedMeshFileService.scala:161-166
Timestamp: 2025-04-28T14:18:04.368Z
Learning: In Scala for-comprehensions with the Fox error handling monad, `Fox.fromBool()` expressions should use the `<-` binding operator instead of the `=` assignment operator to properly propagate error conditions. Using `=` will cause validation failures to be silently ignored.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8352
File: app/models/organization/CreditTransactionService.scala:0-0
Timestamp: 2025-01-27T12:06:42.865Z
Learning: In Scala's for-comprehension with Fox (Future-like type), the `<-` operator ensures sequential execution. If any step fails, the entire chain short-circuits and returns early, preventing subsequent operations from executing. This makes it safe to perform validation checks before database operations.
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/MeshFileService.scala:170-173
Timestamp: 2025-04-23T08:51:57.756Z
Learning: In the webknossos codebase, classes extending `FoxImplicits` have access to an implicit conversion from `Option[A]` to `Fox[A]`, where `None` is converted to an empty Fox that fails gracefully in for-comprehensions.
Learnt from: frcroth
PR: scalableminds/webknossos#8657
File: app/models/dataset/DatasetService.scala:380-392
Timestamp: 2025-06-16T09:04:40.603Z
Learning: Trailing commas are supported in Scala starting from version 2.12.2 as part of SIP-27. They are valid in function calls, function definitions, import statements, and other contexts. This means trailing commas after the last parameter or argument do not cause compilation errors in Scala 2.12.2+.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8393
File: app/controllers/AuthenticationController.scala:566-573
Timestamp: 2025-03-21T16:49:17.956Z
Learning: In the webknossos project, prefer using the ?~> operator for error handling with message keys stored in conf/messages rather than extensive custom error handling.
🪛 Biome (1.9.4)
tools/proxy/proxy.js

[error] 2-2: A Node.js builtin module should be imported with the node: protocol.

Using the node: protocol is more explicit and signals that the imported module belongs to Node.js.
Unsafe fix: Add the node: protocol.

(lint/style/useNodejsImportProtocol)


[error] 7-7: A Node.js builtin module should be imported with the node: protocol.

Using the node: protocol is more explicit and signals that the imported module belongs to Node.js.
Unsafe fix: Add the node: protocol.

(lint/style/useNodejsImportProtocol)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
  • GitHub Check: backend-tests
🔇 Additional comments (7)
tools/proxy/proxy.js (1)

174-189: Excellent TLS implementation with proper error handling.

The conditional TLS server startup is well-implemented with comprehensive error handling and helpful error messages. The try-catch block properly guides users to generate SSL certificates when needed.

app/controllers/AuthenticationController.scala (6)

53-189: Well-structured WebAuthn data models with comprehensive documentation.

The WebAuthn case classes are well-defined with proper JSON serialization and excellent documentation referencing the official WebAuthn specification. The clear documentation of omitted fields helps future maintainers understand the implementation decisions.


585-606: Secure WebAuthn authentication start implementation.

The authentication start method properly implements:

  • Feature flag validation
  • Secure random challenge generation
  • Secure cookie configuration with SameSite.Strict
  • Consistent timeout handling using the webauthnTimeout constant
  • Proper temporary storage management

The implementation follows security best practices for WebAuthn.


608-646: Robust WebAuthn authentication finalization with proper validation.

The authentication finalization properly handles:

  • Challenge retrieval and validation using the pop method
  • Comprehensive error handling with appropriate HTTP status codes
  • Signature counter validation to prevent replay attacks
  • Secure user lookup and authentication flow

The implementation correctly follows the WebAuthn specification and security requirements.


648-690: Comprehensive WebAuthn registration start implementation.

The registration start method includes:

  • Proper user data encoding
  • Credential exclusion to prevent duplicate registrations
  • Secure challenge generation and storage
  • Consistent timeout and cookie security settings

The implementation follows WebAuthn best practices for registration initiation.


692-730: Secure WebAuthn registration finalization with proper verification.

The registration finalization correctly implements:

  • Challenge validation using the pop method
  • Proper WebAuthn verification parameters
  • Secure credential record creation
  • Database persistence with error handling

The implementation follows the WebAuthn specification and security requirements.


732-749: Clean WebAuthn key management implementation.

The key listing and removal methods are straightforward and secure:

  • Proper feature flag validation
  • Secure key listing with reduced data exposure
  • Safe key removal with user validation

The implementation provides a clean API for passkey management.

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: 4

♻️ Duplicate comments (1)
app/controllers/AuthenticationController.scala (1)

622-622: Critical: Fix userHandle decoding issue

The userHandle is base64-encoded during registration (line 658) but treated as a plain string during authentication. This will cause authentication failures.

The userHandle needs to be decoded from base64:

-        multiUserId <- ObjectId.fromString(new String(authData.getUserHandle)) ~> "Passkey Authentication Failed"
+        multiUserId <- ObjectId.fromString(new String(Base64.decodeBase64(authData.getUserHandle), StandardCharsets.UTF_8)) ~> "Passkey Authentication Failed"

This issue was previously identified in past reviews and prevents WebAuthn authentication from working.

🧹 Nitpick comments (5)
tools/proxy/proxy.js (2)

2-2: Add node: protocol for fs import.

Consider using the node: protocol for Node.js builtin modules to be more explicit.

-const fs = require("fs");
+const fs = require("node:fs");

7-7: Add node: protocol for https import.

Consider using the node: protocol for Node.js builtin modules to be more explicit.

-const https = require("https");
+const https = require("node:https");
frontend/javascripts/admin/auth/passkeys_view.tsx (3)

2-2: Remove unused imports.

Several imports are unused and should be removed to clean up the code.

-import { Alert, Button, Col, Form, Input, Modal, Row, Table } from "antd";
+import { Button, Input, Modal, Table } from "antd";

3-10: Remove unused imports and variables.

Multiple imports and variables are unused and should be removed.

-import Request from "libs/request";
-import Toast from "libs/toast";
-import messages from "messages";
-import { type RouteComponentProps, withRouter } from "react-router-dom";
-import { logoutUserAction } from "viewer/model/actions/user_actions";
-import Store from "viewer/store";
-const FormItem = Form.Item;
-const { Password } = Input;

Keep only the necessary imports:

+import Toast from "libs/toast";
+import { type RouteComponentProps, withRouter } from "react-router-dom";

23-23: Remove unused constant.

The MIN_PASSWORD_LENGTH constant is not used in this component and should be removed.

-const MIN_PASSWORD_LENGTH = 8;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2eb5c53 and b839a2b.

📒 Files selected for processing (5)
  • app/controllers/AuthenticationController.scala (7 hunks)
  • frontend/javascripts/admin/account/account_password_view.tsx (4 hunks)
  • frontend/javascripts/admin/auth/passkeys_view.tsx (1 hunks)
  • frontend/javascripts/types/api_types.ts (1 hunks)
  • tools/proxy/proxy.js (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/javascripts/types/api_types.ts
🧰 Additional context used
🧠 Learnings (1)
app/controllers/AuthenticationController.scala (6)
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/NeuroglancerPrecomputedMeshFileService.scala:161-166
Timestamp: 2025-04-28T14:18:04.368Z
Learning: In Scala for-comprehensions with the Fox error handling monad, `Fox.fromBool()` expressions should use the `<-` binding operator instead of the `=` assignment operator to properly propagate error conditions. Using `=` will cause validation failures to be silently ignored.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8352
File: app/models/organization/CreditTransactionService.scala:0-0
Timestamp: 2025-01-27T12:06:42.865Z
Learning: In Scala's for-comprehension with Fox (Future-like type), the `<-` operator ensures sequential execution. If any step fails, the entire chain short-circuits and returns early, preventing subsequent operations from executing. This makes it safe to perform validation checks before database operations.
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/MeshFileService.scala:170-173
Timestamp: 2025-04-23T08:51:57.756Z
Learning: In the webknossos codebase, classes extending `FoxImplicits` have access to an implicit conversion from `Option[A]` to `Fox[A]`, where `None` is converted to an empty Fox that fails gracefully in for-comprehensions.
Learnt from: frcroth
PR: scalableminds/webknossos#8657
File: app/models/dataset/DatasetService.scala:380-392
Timestamp: 2025-06-16T09:04:40.603Z
Learning: Trailing commas are supported in Scala starting from version 2.12.2 as part of SIP-27. They are valid in function calls, function definitions, import statements, and other contexts. This means trailing commas after the last parameter or argument do not cause compilation errors in Scala 2.12.2+.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8393
File: app/controllers/AuthenticationController.scala:566-573
Timestamp: 2025-03-21T16:49:17.956Z
Learning: In the webknossos project, prefer using the ?~> operator for error handling with message keys stored in conf/messages rather than extensive custom error handling.
🧬 Code Graph Analysis (1)
frontend/javascripts/admin/account/account_password_view.tsx (2)
frontend/javascripts/admin/account/helpers/settings_title.tsx (1)
  • SettingsTitle (5-21)
frontend/javascripts/admin/account/helpers/settings_card.tsx (1)
  • SettingsCard (11-31)
🪛 Biome (1.9.4)
frontend/javascripts/admin/auth/passkeys_view.tsx

[error] 78-78: Expected an expression but instead found ')'.

Expected an expression here.

(parse)


[error] 77-77: The comma operator is disallowed.

Its use is often confusing and obscures side effects.

(lint/style/noCommaOperator)


[error] 2-2: This import is unused.

Unused imports might be the result of an incomplete refactoring.
Safe fix: Remove the unused import.

(lint/correctness/noUnusedImports)


[error] 2-2: This import is unused.

Unused imports might be the result of an incomplete refactoring.
Safe fix: Remove the unused import.

(lint/correctness/noUnusedImports)


[error] 2-2: This import is unused.

Unused imports might be the result of an incomplete refactoring.
Safe fix: Remove the unused import.

(lint/correctness/noUnusedImports)


[error] 3-3: This import is unused.

Unused imports might be the result of an incomplete refactoring.
Safe fix: Remove the unused import.

(lint/correctness/noUnusedImports)


[error] 5-5: This import is unused.

Unused imports might be the result of an incomplete refactoring.
Safe fix: Remove the unused import.

(lint/correctness/noUnusedImports)


[error] 7-7: This import is unused.

Unused imports might be the result of an incomplete refactoring.
Safe fix: Remove the unused import.

(lint/correctness/noUnusedImports)


[error] 8-8: This import is unused.

Unused imports might be the result of an incomplete refactoring.
Safe fix: Remove the unused import.

(lint/correctness/noUnusedImports)


[error] 9-9: This variable is unused.

Unused variables usually are result of incomplete refactoring, typos and other source of bugs.
Unsafe fix: If this is intentional, prepend FormItem with an underscore.

(lint/correctness/noUnusedVariables)


[error] 10-10: This variable is unused.

Unused variables usually are result of incomplete refactoring, typos and other source of bugs.

(lint/correctness/noUnusedVariables)


[error] 23-23: This variable is unused.

Unused variables usually are result of incomplete refactoring, typos and other source of bugs.
Unsafe fix: If this is intentional, prepend MIN_PASSWORD_LENGTH with an underscore.

(lint/correctness/noUnusedVariables)


[error] 25-25: This variable is unused.

Unused variables usually are result of incomplete refactoring, typos and other source of bugs.

(lint/correctness/noUnusedVariables)


[error] 41-41: This function is unused.

Unused variables usually are result of incomplete refactoring, typos and other source of bugs.

(lint/correctness/noUnusedVariables)


[error] 76-76: This parameter is unused.

Unused variables usually are result of incomplete refactoring, typos and other source of bugs.
Unsafe fix: If this is intentional, prepend id with an underscore.

(lint/correctness/noUnusedVariables)


[error] 76-76: This parameter is unused.

Unused variables usually are result of incomplete refactoring, typos and other source of bugs.
Unsafe fix: If this is intentional, prepend passkey with an underscore.

(lint/correctness/noUnusedVariables)

tools/proxy/proxy.js

[error] 2-2: A Node.js builtin module should be imported with the node: protocol.

Using the node: protocol is more explicit and signals that the imported module belongs to Node.js.
Unsafe fix: Add the node: protocol.

(lint/style/useNodejsImportProtocol)


[error] 7-7: A Node.js builtin module should be imported with the node: protocol.

Using the node: protocol is more explicit and signals that the imported module belongs to Node.js.
Unsafe fix: Add the node: protocol.

(lint/style/useNodejsImportProtocol)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
🔇 Additional comments (20)
frontend/javascripts/admin/account/account_password_view.tsx (4)

3-3: LGTM: Clean feature flag import.

The import of the features module is properly placed and follows the existing import structure.


15-15: LGTM: PasskeysView import is correctly placed.

The import statement is properly structured and follows TypeScript conventions with the .tsx extension.


169-169: LGTM: Feature flag extraction is clean.

The destructuring of passkeysEnabled from the features function is well-placed and follows the existing pattern.


192-204: LGTM: Conditional rendering implementation is clean and well-structured.

The conditional rendering approach using the feature flag is well-implemented. The UI structure mirrors the password section above, maintaining consistency in the interface design.

tools/proxy/proxy.js (6)

9-9: LGTM: Time constant extraction improves readability.

The time5min constant makes the timeout values more readable and maintainable.


12-13: LGTM: Timeout configuration uses the new constant.

The timeout values are now using the extracted constant, which improves maintainability.


32-37: LGTM: TLS webpack arguments are properly structured.

The TLS arguments array is well-formatted and properly structured for webpack-dev-server configuration.


50-50: LGTM: Conditional webpack arguments based on TLS flag.

The conditional logic for including TLS arguments is clean and follows the expected pattern.


140-155: LGTM: Conditional TLS proxying is well-implemented.

The conditional logic for proxying to webpack-dev-server with TLS support is properly structured. The secure: false option is appropriate for development environments with self-signed certificates.


174-188: LGTM: TLS server creation with proper error handling.

The TLS server creation includes proper error handling and informative error messages, which addresses the previous review feedback about missing error handling.

app/controllers/AuthenticationController.scala (10)

6-52: LGTM: Clean imports and dependencies

The new imports are well-organized and appropriate for the WebAuthn functionality. The addition of TemporaryStore, WebAuthn4J library components, and utility classes supports the implementation correctly.


53-189: Well-documented WebAuthn data structures

The case classes accurately represent the WebAuthn API objects with proper documentation references. The structure follows the Web API specifications closely and includes appropriate JSON serialization support.


211-213: Proper dependency injection for WebAuthn components

The addition of WebAuthn-specific dependencies (webAuthnCredentialDAO, temporaryAssertionStore, temporaryRegistrationStore) is correctly implemented and supports the new functionality.


222-242: Good security practices in WebAuthn configuration

The secure random generator, origin handling, and timeout configuration follow security best practices. The HTTPS check and proper algorithm support are correctly implemented.


319-337: Excellent refactoring of authentication logic

The extraction of common authentication logic into authenticateInner improves code reusability and maintainability. The implementation correctly handles user retrieval, deactivation checks, and logging.


586-608: Secure WebAuthn authentication start implementation

The authentication start flow properly implements security measures including feature flag checks, HTTPS requirements, secure cookie settings with SameSite.Strict, and proper challenge generation using SecureRandom.


610-649: Robust WebAuthn authentication finalization

The authentication finalization properly implements security checks, signature count validation, and credential verification. The use of temporaryAssertionStore.pop() correctly handles challenge cleanup.


651-694: Secure WebAuthn registration start flow

The registration start properly excludes existing credentials, generates secure challenges, and sets appropriate cookie security attributes. The user encoding matches the authentication expectations.


696-735: Comprehensive WebAuthn registration finalization

The registration finalization correctly verifies the attestation, creates credential records, and persists to the database. The error handling using ?~> operator aligns with codebase conventions.


737-754: Appropriate WebAuthn key management endpoints

The key listing and removal endpoints are correctly implemented with proper security checks and error handling. The simplified response formats are appropriate for the UI needs.

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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b839a2b and 7070012.

📒 Files selected for processing (2)
  • frontend/javascripts/admin/account/account_password_view.tsx (4 hunks)
  • frontend/javascripts/admin/auth/passkeys_view.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/javascripts/admin/auth/passkeys_view.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
frontend/javascripts/admin/account/account_password_view.tsx (2)
frontend/javascripts/admin/account/helpers/settings_title.tsx (1)
  • SettingsTitle (5-21)
frontend/javascripts/admin/account/helpers/settings_card.tsx (1)
  • SettingsCard (11-31)
🪛 GitHub Check: frontend-tests
frontend/javascripts/admin/account/account_password_view.tsx

[failure] 15-15:
An import path can only end with a '.tsx' extension when 'allowImportingTsExtensions' is enabled.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
🔇 Additional comments (3)
frontend/javascripts/admin/account/account_password_view.tsx (3)

4-4: LGTM: Feature flag import added correctly.

The import follows the existing pattern and enables feature flag functionality for the passkeys feature.


160-160: LGTM: Feature flag extraction is correctly implemented.

The destructuring of the passkeysEnabled flag follows React best practices and enables conditional rendering based on the feature flag.


183-192: LGTM: Conditional passkeys section follows existing patterns.

The implementation correctly:

  • Uses feature flag to conditionally render the passkeys section
  • Follows the same structure as the password section with SettingsTitle and SettingsCard
  • Maintains consistent styling and layout with the existing component
  • Properly integrates the PasskeysView component

This provides a clean, consistent user experience for the new passkeys functionality.

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

♻️ Duplicate comments (2)
app/models/user/WebAuthnCredentials.scala (2)

138-138: Remove debug print statement.

Debug print statements should not be present in production code as they can clutter logs and potentially expose sensitive information.

- _ = println(attestationStatement)

147-152: Fix race condition in signature counter update.

The current implementation unconditionally updates the signature count, which can lead to race conditions if multiple WebAuthn authentications occur concurrently. This could cause the counter to regress, potentially allowing replay attacks.

 def updateSignCount(c: WebAuthnCredential): Fox[Unit] = {
   val signatureCount = c.credentialRecord.getCounter
   for {
-    _ <- run(q"""UPDATE $existingCollectionName SET signatureCount = $signatureCount WHERE _id = ${c._id}""".asUpdate)
+    rowsUpdated <- run(q"""UPDATE $existingCollectionName SET signatureCount = $signatureCount 
+                          WHERE _id = ${c._id} AND signatureCount < $signatureCount""".asUpdate)
+    _ <- if (rowsUpdated == 1) Fox.successful(()) else Fox.failure("Failed to update signature count - potential replay attack or concurrent update")
   } yield ()
 }

This ensures the counter only increases and fails if a concurrent update occurs, requiring the caller to reload and retry.

🧹 Nitpick comments (2)
conf/evolutions/136-add-webauthn-credentials.sql (1)

18-18: Consider removing redundant unique constraint.

The unique constraint on (_id, credentialId) appears redundant since _id is already the primary key. If the intent is to ensure credentialId uniqueness within the table, consider using UNIQUE (credentialId) instead.

- UNIQUE (_id, credentialId)
+ UNIQUE (credentialId)

However, if there's a specific business requirement for this composite constraint, please add a comment explaining the rationale.

app/controllers/AuthenticationController.scala (1)

662-663: Reconsider error status code for credential fetching.

Using INTERNAL_SERVER_ERROR for credential fetching might be too harsh. This could be a legitimate case where the query succeeds but returns no results.

-      credentials <- webAuthnCredentialDAO
-        .findAllForUser(request.identity._multiUser) ?~> "Failed to fetch Passkeys" ~> INTERNAL_SERVER_ERROR
+      credentials <- webAuthnCredentialDAO
+        .findAllForUser(request.identity._multiUser) ?~> "Failed to fetch Passkeys" ~> BAD_REQUEST
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9d6956c and f59e16f.

📒 Files selected for processing (5)
  • app/controllers/AuthenticationController.scala (7 hunks)
  • app/models/user/WebAuthnCredentials.scala (1 hunks)
  • conf/evolutions/136-add-webauthn-credentials.sql (1 hunks)
  • tools/postgres/schema.sql (4 hunks)
  • util/src/main/scala/com/scalableminds/util/tools/Fox.scala (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tools/postgres/schema.sql
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:29-35
Timestamp: 2025-07-09T07:30:57.149Z
Learning: In the webknossos project, error handling and user notifications are handled at the API function level (e.g., `removeWebAuthnKey`, `fetchPasskeys`) rather than in the UI components, so additional try-catch blocks in the UI layer are unnecessary.
util/src/main/scala/com/scalableminds/util/tools/Fox.scala (3)
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/MeshFileService.scala:170-173
Timestamp: 2025-04-23T08:51:57.756Z
Learning: In the webknossos codebase, classes extending `FoxImplicits` have access to an implicit conversion from `Option[A]` to `Fox[A]`, where `None` is converted to an empty Fox that fails gracefully in for-comprehensions.
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/NeuroglancerPrecomputedMeshFileService.scala:161-166
Timestamp: 2025-04-28T14:18:04.368Z
Learning: In Scala for-comprehensions with the Fox error handling monad, `Fox.fromBool()` expressions should use the `<-` binding operator instead of the `=` assignment operator to properly propagate error conditions. Using `=` will cause validation failures to be silently ignored.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8352
File: app/models/organization/CreditTransactionService.scala:0-0
Timestamp: 2025-01-27T12:06:42.865Z
Learning: In Scala's for-comprehension with Fox (Future-like type), the `<-` operator ensures sequential execution. If any step fails, the entire chain short-circuits and returns early, preventing subsequent operations from executing. This makes it safe to perform validation checks before database operations.
conf/evolutions/136-add-webauthn-credentials.sql (2)
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8352
File: app/models/organization/CreditTransaction.scala:80-88
Timestamp: 2025-01-27T11:53:16.873Z
Learning: Credit balance consistency in WEBKNOSSOS is maintained through a PostgreSQL BEFORE trigger that calculates the total balance using SUM(credit_change) and prevents negative balances. The implementation includes DEFERRABLE foreign key constraints for flexibility, while the trigger ensures atomic balance checks within transactions.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8352
File: app/models/organization/CreditTransaction.scala:80-88
Timestamp: 2025-01-27T11:53:16.873Z
Learning: The credit balance consistency in WEBKNOSSOS is maintained through a PostgreSQL BEFORE trigger that checks for negative balances. The trigger runs within the same transaction as credit updates, ensuring atomicity and preventing race conditions.
app/models/user/WebAuthnCredentials.scala (1)
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.
app/controllers/AuthenticationController.scala (11)
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:29-35
Timestamp: 2025-07-09T07:30:57.149Z
Learning: In the webknossos project, error handling and user notifications are handled at the API function level (e.g., `removeWebAuthnKey`, `fetchPasskeys`) rather than in the UI components, so additional try-catch blocks in the UI layer are unnecessary.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module has comprehensive built-in error handling. The triggerRequest method defaults to showErrorToast: true, automatically catches errors with .catch(), and displays user notifications via Toast.error() or Toast.messages() in the handleError method. Functions using Request methods (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module's sendJSONReceiveJSON function already includes comprehensive error handling that displays Toast.error() notifications for HTTP errors. Functions using this module (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/NeuroglancerPrecomputedMeshFileService.scala:161-166
Timestamp: 2025-04-28T14:18:04.368Z
Learning: In Scala for-comprehensions with the Fox error handling monad, `Fox.fromBool()` expressions should use the `<-` binding operator instead of the `=` assignment operator to properly propagate error conditions. Using `=` will cause validation failures to be silently ignored.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8352
File: app/models/organization/CreditTransactionService.scala:0-0
Timestamp: 2025-01-27T12:06:42.865Z
Learning: In Scala's for-comprehension with Fox (Future-like type), the `<-` operator ensures sequential execution. If any step fails, the entire chain short-circuits and returns early, preventing subsequent operations from executing. This makes it safe to perform validation checks before database operations.
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/MeshFileService.scala:170-173
Timestamp: 2025-04-23T08:51:57.756Z
Learning: In the webknossos codebase, classes extending `FoxImplicits` have access to an implicit conversion from `Option[A]` to `Fox[A]`, where `None` is converted to an empty Fox that fails gracefully in for-comprehensions.
Learnt from: frcroth
PR: scalableminds/webknossos#8657
File: app/models/dataset/DatasetService.scala:380-392
Timestamp: 2025-06-16T09:04:40.603Z
Learning: Trailing commas are supported in Scala starting from version 2.12.2 as part of SIP-27. They are valid in function calls, function definitions, import statements, and other contexts. This means trailing commas after the last parameter or argument do not cause compilation errors in Scala 2.12.2+.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8393
File: app/controllers/AuthenticationController.scala:566-573
Timestamp: 2025-03-21T16:49:17.956Z
Learning: In the webknossos project, prefer using the ?~> operator for error handling with message keys stored in conf/messages rather than extensive custom error handling.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module's receiveJSON and sendJSONReceiveJSON methods already include comprehensive error handling that displays user notifications for HTTP errors. Functions using these methods (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module's receiveJSON and sendJSONReceiveJSON methods already include comprehensive error handling that displays user notifications for HTTP errors. Functions using these methods (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
🧬 Code Graph Analysis (1)
util/src/main/scala/com/scalableminds/util/tools/Fox.scala (1)
util/src/main/scala/com/scalableminds/util/tools/Box.scala (1)
  • Full (661-699)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push
  • GitHub Check: frontend-tests
🔇 Additional comments (3)
app/controllers/AuthenticationController.scala (3)

319-338: LGTM: Well-structured authentication refactoring.

Good refactoring to extract the core authentication logic into a reusable authenticateInner method. This allows both traditional and WebAuthn authentication to use the same core logic while maintaining consistency.


586-608: LGTM: Secure authentication start implementation.

The implementation correctly:

  • Validates feature flags and HTTPS requirement
  • Generates cryptographically secure random challenges
  • Sets proper security attributes on cookies (httpOnly, secure, sameSite)
  • Uses consistent timeout handling

610-650: LGTM: Comprehensive authentication finalization.

The implementation properly:

  • Validates the authentication response using WebAuthn4J
  • Implements signature counter validation for replay protection
  • Uses atomic pop operation for challenge retrieval
  • Follows the codebase error handling patterns with ?~> and ??~>

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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f59e16f and a983204.

📒 Files selected for processing (2)
  • app/controllers/AuthenticationController.scala (7 hunks)
  • util/src/main/scala/com/scalableminds/util/tools/Fox.scala (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • util/src/main/scala/com/scalableminds/util/tools/Fox.scala
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:29-35
Timestamp: 2025-07-09T07:30:57.149Z
Learning: In the webknossos project, error handling and user notifications are handled at the API function level (e.g., `removeWebAuthnKey`, `fetchPasskeys`) rather than in the UI components, so additional try-catch blocks in the UI layer are unnecessary.
app/controllers/AuthenticationController.scala (11)
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:29-35
Timestamp: 2025-07-09T07:30:57.149Z
Learning: In the webknossos project, error handling and user notifications are handled at the API function level (e.g., `removeWebAuthnKey`, `fetchPasskeys`) rather than in the UI components, so additional try-catch blocks in the UI layer are unnecessary.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module has comprehensive built-in error handling. The triggerRequest method defaults to showErrorToast: true, automatically catches errors with .catch(), and displays user notifications via Toast.error() or Toast.messages() in the handleError method. Functions using Request methods (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module's sendJSONReceiveJSON function already includes comprehensive error handling that displays Toast.error() notifications for HTTP errors. Functions using this module (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/NeuroglancerPrecomputedMeshFileService.scala:161-166
Timestamp: 2025-04-28T14:18:04.368Z
Learning: In Scala for-comprehensions with the Fox error handling monad, `Fox.fromBool()` expressions should use the `<-` binding operator instead of the `=` assignment operator to properly propagate error conditions. Using `=` will cause validation failures to be silently ignored.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8352
File: app/models/organization/CreditTransactionService.scala:0-0
Timestamp: 2025-01-27T12:06:42.865Z
Learning: In Scala's for-comprehension with Fox (Future-like type), the `<-` operator ensures sequential execution. If any step fails, the entire chain short-circuits and returns early, preventing subsequent operations from executing. This makes it safe to perform validation checks before database operations.
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/MeshFileService.scala:170-173
Timestamp: 2025-04-23T08:51:57.756Z
Learning: In the webknossos codebase, classes extending `FoxImplicits` have access to an implicit conversion from `Option[A]` to `Fox[A]`, where `None` is converted to an empty Fox that fails gracefully in for-comprehensions.
Learnt from: frcroth
PR: scalableminds/webknossos#8657
File: app/models/dataset/DatasetService.scala:380-392
Timestamp: 2025-06-16T09:04:40.603Z
Learning: Trailing commas are supported in Scala starting from version 2.12.2 as part of SIP-27. They are valid in function calls, function definitions, import statements, and other contexts. This means trailing commas after the last parameter or argument do not cause compilation errors in Scala 2.12.2+.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8393
File: app/controllers/AuthenticationController.scala:566-573
Timestamp: 2025-03-21T16:49:17.956Z
Learning: In the webknossos project, prefer using the ?~> operator for error handling with message keys stored in conf/messages rather than extensive custom error handling.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module's receiveJSON and sendJSONReceiveJSON methods already include comprehensive error handling that displays user notifications for HTTP errors. Functions using these methods (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module's receiveJSON and sendJSONReceiveJSON methods already include comprehensive error handling that displays user notifications for HTTP errors. Functions using these methods (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
🧬 Code Graph Analysis (1)
app/controllers/AuthenticationController.scala (10)
webknossos-datastore/app/com/scalableminds/webknossos/datastore/storage/TemporaryStore.scala (4)
  • TemporaryStore (9-83)
  • insert (55-60)
  • get (13-18)
  • pop (74-79)
app/utils/WkConf.scala (5)
  • User (45-60)
  • User (75-77)
  • User (85-91)
  • EmailVerification (52-58)
  • Features (128-147)
app/models/user/User.scala (4)
  • User (23-25)
  • User (27-61)
  • updateLastActivity (490-497)
  • insertOne (472-490)
app/models/user/UserService.scala (3)
  • retrieve (269-272)
  • insert (92-136)
  • emailFor (179-184)
util/src/main/scala/com/scalableminds/util/tools/Fox.scala (11)
  • flatMap (277-287)
  • flatMap (372-372)
  • Fox (30-223)
  • Fox (225-298)
  • runIf (169-178)
  • successful (53-56)
  • fromBool (32-36)
  • toFox (14-14)
  • fromFuture (42-53)
  • map (274-277)
  • map (370-370)
app/models/user/EmailVerificationService.scala (1)
  • assertEmailVerifiedOrResendVerificationMail (66-76)
app/models/user/MultiUser.scala (3)
  • updateLastLoggedInIdentity (106-114)
  • findOneById (139-146)
  • insertOne (64-77)
util/src/main/scala/com/scalableminds/util/objectid/ObjectId.scala (5)
  • toString (12-12)
  • ObjectId (11-13)
  • ObjectId (15-55)
  • fromString (17-19)
  • generate (16-16)
util/src/main/scala/com/scalableminds/util/mvc/ExtendedController.scala (1)
  • validateJson (204-209)
app/models/user/WebAuthnCredentials.scala (6)
  • findByCredentialId (118-128)
  • updateSignCount (147-152)
  • findAllForUser (109-118)
  • WebAuthnCredential (25-49)
  • insertOne (128-145)
  • removeById (154-160)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: frontend-tests
  • GitHub Check: backend-tests
  • GitHub Check: build-smoketest-push
🔇 Additional comments (8)
app/controllers/AuthenticationController.scala (8)

53-189: Well-structured WebAuthn data models

The WebAuthn case classes are well-designed with proper documentation references to MDN and clear field explanations. The JSON serialization is correctly implemented.


222-242: Good security configuration setup

The WebAuthn configuration is properly implemented with:

  • SecureRandom initialized once at class level
  • Origin derived from configuration
  • HTTPS requirement checking
  • Appropriate algorithm support and timeout settings

319-338: Smart refactoring of authentication logic

The extraction of authenticateInner method is a good refactoring that allows reuse between regular authentication and WebAuthn authentication flows while maintaining consistent behavior.


586-608: Proper authentication start implementation

The WebAuthn authentication start method correctly:

  • Validates feature flags and HTTPS requirements
  • Generates secure random challenges
  • Sets appropriate cookie security attributes including SameSite.Strict
  • Uses consistent timeout handling

638-645: Appropriate signature counter validation

The signature counter validation correctly handles both cases where the counter is unused (0) and where it should be strictly increasing, following WebAuthn security requirements.


652-695: Well-implemented registration start

The registration start method properly:

  • Validates permissions and requirements
  • Uses privacy-preserving user handle (multiUser ID instead of email)
  • Correctly excludes existing credentials
  • Implements proper challenge generation and storage

697-730: Solid registration finalization

The registration finalization correctly:

  • Validates the registration response
  • Creates proper credential records
  • Handles timeouts and errors appropriately
  • Stores credentials securely

742-749: Key removal has proper access control

The key removal method correctly filters by multiUser in the DAO, ensuring users can only remove their own keys. The error handling with ?~> follows the codebase conventions.

Copy link
Contributor

@MichaelBuessemeyer MichaelBuessemeyer left a comment

Choose a reason for hiding this comment

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

Neat, thanks for keeping digging throught this 💪 🙏 🧑‍💼

I found a few left over suggestions regarding your latest changes. These are only minor things like concistent naming & extracting error messages.

I think after this, this PR should be ready to go after. At least code-wise. I have never done any testing

@@ -105,7 +105,7 @@ object Dependencies {
// SQL Queries postgres specifics. not imported.
"org.postgresql" % "postgresql" % "42.7.5",
/// WebAuthn Dependencies
"com.webauthn4j" % "webauthn4j-core" % "0.29.2.RELEASE" exclude("com.fasterxml.jackson.core", "jackson-databind"),
"com.webauthn4j" % "webauthn4j-core" % "0.29.3.RELEASE" exclude("com.fasterxml.jackson.core", "jackson-databind"),
)

val dependencyOverrides: Seq[ModuleID] = Seq(
Copy link
Contributor

Choose a reason for hiding this comment

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

@fm3 / @robert-oleynik I am not familiar with what dependencyOverrides does, but if I understand the comment correctly, this overwrite is no longer needed as we do no longer have netlift common as a dependency. Thus, can you please delete this and test whether everything still works?

val dependencyOverrides: Seq[ModuleID] = Seq( 
   // liftweb-commons (used by us for Box/tryo) depends on older scala-xml, but we do not use its xml-related features
    "com.fasterxml.jackson.core" % "jackson-databind" % "2.14.3"	    "com.fasterxml.jackson.core" % "jackson-databind" % "2.14.3"
  )	  )

Copy link
Contributor Author

@robert-oleynik robert-oleynik Jul 15, 2025

Choose a reason for hiding this comment

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

It is necessary, due to a conflict with the Play framework:

backend: [error] com.fasterxml.jackson.databind.JsonMappingException: Scala module 2.14.3 requires Jackson Databind version >= 2.14.0 and < 2.15.0 - Found jackson-databind version 2.19.0
backend: [error]        at com.fasterxml.jackson.module.scala.JacksonModule.setupModule(JacksonModule.scala:61)
backend: [error]        at com.fasterxml.jackson.module.scala.JacksonModule.setupModule$(JacksonModule.scala:46)
backend: [error]        at com.fasterxml.jackson.module.scala.DefaultScalaModule.setupModule(DefaultScalaModule.scala:17
...

I will update the comment.

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: 0

♻️ Duplicate comments (1)
app/controllers/AuthenticationController.scala (1)

610-653: Critical bug: User handle must be base64 decoded before ObjectId conversion.

The user handle is base64 encoded during registration (line 662) but being treated as plain string during authentication. This will cause authentication to always fail.

-        multiUserId <- ObjectId.fromString(new String(authData.getUserHandle)) ??~> Messages(
-          "auth.passkeys.unauthorized") ~> UNAUTHORIZED
+        multiUserId <- ObjectId.fromString(new String(Base64.decodeBase64(authData.getUserHandle), StandardCharsets.UTF_8)) ??~> Messages(
+          "auth.passkeys.unauthorized") ~> UNAUTHORIZED
🧹 Nitpick comments (1)
app/controllers/AuthenticationController.scala (1)

746-754: Consider adding explicit validation for key removal.

While the DAO's removeById method filters by _multiUser (preventing cross-user deletions), it may silently succeed when no matching key is found. Consider adding explicit validation to return a proper 404 response.

  def webauthnRemoveKey(id: ObjectId): Action[AnyContent] = sil.SecuredAction.async { implicit request =>
    {
      for {
        _ <- Fox.fromBool(conf.Features.passkeysEnabled) ?~> Messages("auth.passkeys.disabled")
        _ <- Fox.fromBool(usesHttps) ?~> Messages("auth.passkeys.requiresHttps")
+       // Verify the key exists and belongs to the user
+       _ <- webAuthnCredentialDAO.findOneById(id, request.identity._multiUser) ?~> "Passkey not found" ~> NOT_FOUND
        _ <- webAuthnCredentialDAO.removeById(id, request.identity._multiUser) ?~> "Passkey not found" ~> NOT_FOUND
      } yield Ok(Json.obj())
    }
  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5f7b577 and 766ea06.

📒 Files selected for processing (2)
  • app/controllers/AuthenticationController.scala (7 hunks)
  • project/Dependencies.scala (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • project/Dependencies.scala
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:29-35
Timestamp: 2025-07-09T07:30:57.149Z
Learning: In the webknossos project, error handling and user notifications are handled at the API function level (e.g., `removeWebAuthnKey`, `fetchPasskeys`) rather than in the UI components, so additional try-catch blocks in the UI layer are unnecessary.
app/controllers/AuthenticationController.scala (11)
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:29-35
Timestamp: 2025-07-09T07:30:57.149Z
Learning: In the webknossos project, error handling and user notifications are handled at the API function level (e.g., `removeWebAuthnKey`, `fetchPasskeys`) rather than in the UI components, so additional try-catch blocks in the UI layer are unnecessary.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module has comprehensive built-in error handling. The triggerRequest method defaults to showErrorToast: true, automatically catches errors with .catch(), and displays user notifications via Toast.error() or Toast.messages() in the handleError method. Functions using Request methods (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module's sendJSONReceiveJSON function already includes comprehensive error handling that displays Toast.error() notifications for HTTP errors. Functions using this module (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/NeuroglancerPrecomputedMeshFileService.scala:161-166
Timestamp: 2025-04-28T14:18:04.368Z
Learning: In Scala for-comprehensions with the Fox error handling monad, `Fox.fromBool()` expressions should use the `<-` binding operator instead of the `=` assignment operator to properly propagate error conditions. Using `=` will cause validation failures to be silently ignored.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8221
File: app/controllers/JobController.scala:226-232
Timestamp: 2025-01-13T09:06:15.202Z
Learning: In the JobController of webknossos, numeric parameters should use `Option[Double]` instead of `Option[String]` for better type safety. Additionally, when adding new job parameters that are conditionally required (like evaluation settings), proper validation should be added in the `for` comprehension block before creating the `commandArgs`.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8352
File: app/models/organization/CreditTransactionService.scala:0-0
Timestamp: 2025-01-27T12:06:42.865Z
Learning: In Scala's for-comprehension with Fox (Future-like type), the `<-` operator ensures sequential execution. If any step fails, the entire chain short-circuits and returns early, preventing subsequent operations from executing. This makes it safe to perform validation checks before database operations.
Learnt from: frcroth
PR: scalableminds/webknossos#8236
File: webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/mesh/MeshFileService.scala:170-173
Timestamp: 2025-04-23T08:51:57.756Z
Learning: In the webknossos codebase, classes extending `FoxImplicits` have access to an implicit conversion from `Option[A]` to `Fox[A]`, where `None` is converted to an empty Fox that fails gracefully in for-comprehensions.
Learnt from: frcroth
PR: scalableminds/webknossos#8657
File: app/models/dataset/DatasetService.scala:380-392
Timestamp: 2025-06-16T09:04:40.603Z
Learning: Trailing commas are supported in Scala starting from version 2.12.2 as part of SIP-27. They are valid in function calls, function definitions, import statements, and other contexts. This means trailing commas after the last parameter or argument do not cause compilation errors in Scala 2.12.2+.
Learnt from: MichaelBuessemeyer
PR: scalableminds/webknossos#8393
File: app/controllers/AuthenticationController.scala:566-573
Timestamp: 2025-03-21T16:49:17.956Z
Learning: In the webknossos project, prefer using the ?~> operator for error handling with message keys stored in conf/messages rather than extensive custom error handling.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module's receiveJSON and sendJSONReceiveJSON methods already include comprehensive error handling that displays user notifications for HTTP errors. Functions using these methods (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
Learnt from: robert-oleynik
PR: scalableminds/webknossos#8393
File: frontend/javascripts/admin/auth/passkeys_view.tsx:47-51
Timestamp: 2025-07-09T07:33:45.669Z
Learning: In the webknossos project, the Request module's receiveJSON and sendJSONReceiveJSON methods already include comprehensive error handling that displays user notifications for HTTP errors. Functions using these methods (like doWebAuthnRegistration and listWebAuthnKeys) do not need additional error handling for user notifications.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build-smoketest-push
  • GitHub Check: backend-tests
  • GitHub Check: frontend-tests
🔇 Additional comments (8)
app/controllers/AuthenticationController.scala (8)

6-51: WebAuthn imports and dependencies look comprehensive.

The imports include all necessary components for WebAuthn implementation using the webauthn4j library, which is a good choice for server-side WebAuthn support.


53-189: Well-structured WebAuthn case classes with proper documentation.

The case classes accurately model the WebAuthn API specification with clear documentation referencing the MDN Web API docs. Good practice omitting unnecessary fields with explanatory comments.


211-213: Constructor properly updated with WebAuthn dependencies.

The new dependencies are correctly added and properly typed. The temporary stores for assertion and registration challenges are appropriately separated.


222-241: WebAuthn configuration and initialization looks secure.

Good practices include:

  • SecureRandom initialized at class level to avoid per-request overhead
  • Proper origin configuration from application config
  • HTTPS requirement enforced
  • Standard cryptographic algorithms (EdDSA, ES256, RS256) supported

586-608: WebAuthn authentication start method is well-implemented.

Good security practices:

  • Proper feature flag and HTTPS validation
  • Secure random challenge generation
  • Cookie configured with SameSite.Strict for CSRF protection
  • Consistent timeout handling

655-698: WebAuthn registration start method is correctly implemented.

The method properly:

  • Validates prerequisites (feature flags, HTTPS)
  • Creates user object with base64 encoded ID (WebAuthn spec requirement)
  • Excludes existing credentials to prevent re-registration
  • Generates secure challenge and stores it temporarily

700-733: Registration finalization method is correctly implemented.

The method properly validates the registration response, creates the credential record, and stores it in the database with appropriate error handling.


735-744: Key listing method is straightforward and correct.

The method properly retrieves keys for the user and returns simplified descriptors containing only the necessary information (ID and name).

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.

3 participants