Skip to content

Conversation

@david-livefront
Copy link
Collaborator

@david-livefront david-livefront commented Dec 17, 2025

🎟️ Tracking

PM-26577

📔 Objective

This PR adds support to app links. It is currently on-hold while we wait for final requirements and verification on the API spec.

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

@david-livefront david-livefront added the hold do not merge yet label Dec 17, 2025
@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Claude finished @david-livefront's task in 2m 54s —— View job


I'll analyze this and get back to you.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 17, 2025

Logo
Checkmarx One – Scan Summary & Detailsc2e7a0ce-efcc-4adc-b494-fe53bab0b4b7

Great job! No new security vulnerabilities introduced in this pull request

@david-livefront david-livefront force-pushed the PM-26577-app-links-suppoort branch from 35b8f13 to fb60c24 Compare December 17, 2025 18:07
android:launchMode="singleTop"
android:noHistory="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter android:autoVerify="true">
Copy link
Contributor

Choose a reason for hiding this comment

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

App Link Verification Status

The PR adds android:autoVerify="true" for app links on bitwarden.com, bitwarden.eu, and bitwarden.pw domains. For secure operation, Digital Asset Links (assetlinks.json) files must be deployed and verified.

Questions:

  1. Have assetlinks.json files been deployed to all three domains?
  2. Has app link verification been tested on Android 6+ devices?
  3. What's the fallback behavior if verification fails?

Security Impact: Without proper verification, malicious apps could potentially intercept authentication callbacks by claiming the same intent filters.

sendAction(
EnterpriseSignOnAction.Internal.OnGenerateUriForSsoResult(
uri = uri.toUri(),
scheme = requireNotNull(prevalidateSsoResult.redirectUri.toUri().scheme),
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Missing Redirect URI Scheme Validation

requireNotNull(prevalidateSsoResult.redirectUri.toUri().scheme) will crash if the scheme is null, but doesn't validate the scheme value itself.

Problem: An attacker-controlled server could send a redirectUri with an unexpected scheme (e.g., "http", "javascript", "file") causing crashes or security issues.

Suggested fix:

val scheme = prevalidateSsoResult.redirectUri.toUri().scheme
require(scheme == "bitwarden" || scheme == "https") { "Invalid redirect scheme" }

return if (action == Intent.ACTION_VIEW && localData?.host == SSO_HOST) {
return if (action == Intent.ACTION_VIEW &&
// We check the host for Deeplinks, and the path for App Links.
(localData?.host == SSO_HOST || localData?.path == SSO_HOST)
Copy link
Contributor

Choose a reason for hiding this comment

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

Path-Based Intent Matching Verification

The condition localData?.host == SSO_HOST || localData?.path == SSO_HOST checks both host and path.

Questions:

  1. For app links (https://bitwarden.com/sso-callback), does Uri.path return "/sso-callback" or "sso-callback"?
  2. Has this been tested with actual app link URIs to confirm the path matching works?

Risk: If path doesn't match expected format, app links will silently fail.

?.let { token ->
PrevalidateSsoResult.Success(
token = token,
redirectUri = response
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Server-Provided redirectUri Lacks Validation

The server-provided redirectUri is accepted without validation, only checked if blank.

Problem: Server compromise could inject malicious redirect URIs (file://, javascript://, arbitrary https URLs).

Suggested fix:

private fun isValidRedirectUri(uri: String): Boolean {
    val parsed = uri.toUri()
    return when (parsed.scheme) {
        "bitwarden" -> parsed.host in listOf("sso-callback", "duo-callback", "webauthn-callback")
        "https" -> parsed.host in listOf("bitwarden.com", "bitwarden.eu", "bitwarden.pw") &&
                   parsed.path in listOf("/sso-callback", "/duo-callback", "/webauthn-callback")
        else -> false
    }
}

@david-livefront david-livefront force-pushed the PM-26577-app-links-suppoort branch from fb60c24 to 79f8019 Compare December 17, 2025 18:41
@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Code Review Summary: App Links Authentication Support

Overall Assessment: REQUEST CHANGES

Critical Issues:
Finding 1: app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt:1202 - Missing redirect URI validation
Finding 2: app/src/main/AndroidManifest.xml:143 - App link verification without documented .well-known/assetlinks.json
Finding 3: network/src/main/kotlin/com/bitwarden/network/util/TwoFactorRequiredExtensions.kt:46 - Duo redirect URI accepts server value without validation

Important Issues:
Finding 4: app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SsoUtils.kt:103 - Reconstructed redirect URI may not match original
Finding 5: app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnViewModel.kt:411 - requireNotNull on scheme could crash

See detailed findings below.

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Finding 1: Missing Redirect URI Validation

Location: app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt:1202-1205

Severity:CRITICAL

Issue: The code accepts redirect URIs from server responses without validation in prevalidateSso. This creates an open redirect vulnerability where a compromised server could redirect authentication callbacks to attacker-controlled domains.

Attack Scenario:

  1. Compromised SSO prevalidation response provides redirect_uri: "https://evil.com/callback"
  2. User completes SSO authentication
  3. Authorization code gets sent to attacker domain instead of legitimate Bitwarden app

Required Fix:
Implement allowlist validation that only allows:

  • bitwarden://sso-callback, bitwarden://duo-callback, bitwarden://webauthn-callback
  • https://bitwarden.com/[sso|duo|webauthn]-callback
  • https://bitwarden.eu/[sso|duo|webauthn]-callback
  • https://bitwarden.pw/[sso|duo|webauthn]-callback

These must match the intent filters declared in AndroidManifest.xml. Reject any other redirect URIs and fall back to default deep link.

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Finding 2: App Link Verification Configuration Missing Documentation

Location: app/src/main/AndroidManifest.xml:143-156

Severity:CRITICAL

Issue: The PR adds android:autoVerify="true" for authentication callback app links but provides no documentation about:

  1. Required .well-known/assetlinks.json configuration on bitwarden.com, bitwarden.eu, bitwarden.pw
  2. Verification testing procedures
  3. Fallback behavior if verification fails

Security Impact:

  • If Digital Asset Links verification fails, Android falls back to intent disambiguation dialog
  • Users could accidentally select malicious app claiming same intent patterns
  • Authentication callbacks could be intercepted by rogue applications

Required Fix:

  1. Document the required .well-known/assetlinks.json configuration for all three domains
  2. Verify that asset links files include correct package name and SHA-256 certificate fingerprints
  3. Add test plan section describing app link verification testing on Android 12+
  4. Consider whether autoVerify should be enabled for initial release or phased in after server-side configuration confirmed

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Finding 3: Duo Redirect URI Validation Missing

Location: network/src/main/kotlin/com/bitwarden/network/util/TwoFactorRequiredExtensions.kt:46-53

Severity:CRITICAL

Issue: The twoFactorDuoRedirectUri extension accepts server-provided redirect URI without validation, falling back to default only if null/blank. A malicious response could provide arbitrary redirect URI.

Code:

val GetTokenResponseJson.TwoFactorRequired?.twoFactorDuoRedirectUri: String
    get() = this
        ?.authMethodsData
        ?.duo
        ?.get("RedirectUri")
        ?.jsonPrimitive
        ?.contentOrNull
        ?: "bitwarden://duo-callback"  // Only used if null

Attack Vector:
Server responds with RedirectUri: "https://attacker.com/duo-callback" in two-factor auth data. Duo token gets redirected to attacker.

Required Fix:
Validate redirect URI matches allowed patterns before using. If invalid, use default and log security warning:

val GetTokenResponseJson.TwoFactorRequired?.twoFactorDuoRedirectUri: String
    get() {
        val serverUri = this?.authMethodsData?.duo
            ?.get("RedirectUri")?.jsonPrimitive?.contentOrNull
        return if (serverUri?.isValidCallbackUri() == true) {
            serverUri
        } else {
            "bitwarden://duo-callback"
        }
    }

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Finding 4: WebAuthn Redirect URI Validation Missing

Location: app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/WebAuthUtils.kt:95-98

Severity:CRITICAL

Issue: Similar to Duo, the getRedirectUri function for WebAuthn accepts any redirect URI from server without validation.

Code:

fun JsonObject.getRedirectUri(): String = this["RedirectUri"]
    ?.jsonPrimitive
    ?.contentOrNull
    ?: CALLBACK_URI  // "bitwarden://webauthn-callback"

Attack Vector:
Compromised WebAuthn challenge response includes RedirectUri: "https://phishing.com/webauthn-callback". WebAuthn token redirects to attacker.

Required Fix:
Apply same validation as Duo redirect URIs. Only accept URIs matching AndroidManifest intent filters.

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Finding 5: Redirect URI Reconstruction May Not Match Original

Location: app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SsoUtils.kt:103-110

Severity: ⚠️ IMPORTANT

Issue: The SSO callback result reconstruction removes path, query, and fragment from the original URI. This assumes all app links use base domain only, but the AndroidManifest allows path patterns.

Code:

redirectUri = this
    .buildUpon()
    .path(null)       // Removes /sso-callback path
    .query(null)
    .fragment(null)
    .build()
    .toString(),

Problem:
If app link is https://bitwarden.com/sso-callback?code=abc, the reconstructed URI becomes https://bitwarden.com, which won't match the expected callback format sent to identity server.

Required Fix:
Preserve the path component for HTTPS URIs:

redirectUri = this
    .buildUpon()
    .apply {
        // Keep path for app links, remove for deep links
        if (this@getSsoCallbackResult?.scheme != "bitwarden") {
            // For app links, keep path but remove query/fragment
            query(null)
            fragment(null)
        } else {
            // For deep links, remove all
            path(null)
            query(null)
            fragment(null)
        }
    }
    .build()
    .toString(),

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Finding 6: requireNotNull on Scheme Could Crash Application

Location: app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnViewModel.kt:411

Severity: IMPORTANT

Issue: Using requireNotNull on redirect URI scheme will crash the app if server provides malformed URI without scheme. If redirectUri lacks scheme, .toUri().scheme returns null causing crash.

Required Fix: Handle null scheme gracefully with fallback to default scheme. Apply to all three locations: EnterpriseSignOnViewModel.kt:411, TwoFactorLoginViewModel.kt:195, TwoFactorLoginViewModel.kt:234

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Finding 7: Missing Test Coverage for App Link Path Matching

Location: app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/util/SsoUtilsTest.kt

Severity: IMPORTANT

Issue: Tests only verify deep link host matching (bitwarden://sso-callback) but not new app link path matching (https://bitwarden.com/sso-callback). The implementation checks both host and path but tests do not cover app link scenarios.

Code in SsoUtils.kt line 70:
(localData?.host == SSO_HOST || localData?.path == SSO_HOST)

Missing test cases:

  1. Intent with https scheme and /sso-callback path should return Success
  2. Intent with https scheme but wrong path should return null
  3. Same for Duo and WebAuthn callback utilities

Required Fix: Add test coverage for app link path pattern matching in SsoUtilsTest, DuoUtilsTest, and create WebAuthUtilsTest if it does not exist.

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Finding 8: Intent Filter Path Pattern Too Broad

Location: app/src/main/AndroidManifest.xml:85

Severity: SUGGESTED

Issue: The pathPattern="/redirect-connector.*" uses wildcard matching which could unintentionally match paths like /redirect-connector-admin or /redirect-connector/evil/path. While not currently exploitable, overly broad patterns increase attack surface.

Current:

Recommendation: Use exact path match if possible, or limit wildcards:

Or if query parameters needed:

Verify with product team whether wildcard is required for this endpoint.

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Finding 9: Inconsistent Redirect URI Handling Between Auth Methods

Location: Multiple files

Severity: TECHNICAL DEBT

Issue: SSO uses dynamic redirect URI from server, while Duo and WebAuthn have hardcoded defaults with server override capability. This inconsistency makes security auditing harder and could lead to different vulnerability profiles.

SSO: Always uses server-provided or falls back to DEFAULT_SSO_REDIRECT_URI
Duo: Uses server RedirectUri field or "bitwarden://duo-callback"
WebAuthn: Uses RedirectUri from JSON or "bitwarden://webauthn-callback"

Recommendation: Standardize redirect URI handling across all auth methods. Create single validation function used by all three flows. Document the security model in code comments.

Example:
fun validateAuthRedirectUri(serverUri: String?, authType: String): String {
// Single source of truth for redirect URI validation
// Used by SSO, Duo, and WebAuthn
}

This would prevent security fixes from being applied inconsistently.

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Finding 10: Missing Security Documentation in PR Description

Location: PR description

Severity: SUGGESTED

Issue: The PR description states this is on-hold waiting for final requirements and API spec verification, but does not document:

  1. Security review status of app links feature
  2. Threat model analysis for redirect URI attacks
  3. Digital Asset Links verification plan
  4. Rollout strategy (feature flag, phased release)

Given this adds new authentication redirect flows with security implications, the PR should document security review findings and deployment approach.

Recommendation: Update PR description with:

  • Security review checklist
  • Link to threat model document
  • Server-side configuration requirements (.well-known/assetlinks.json)
  • Testing procedures for app link verification
  • Rollback plan if issues discovered post-release

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

QUESTION: Redirect URI Scheme Determination Strategy

Location: app/src/main/kotlin/com/x8bit/bitwarden/ui/auth/feature/enterprisesignon/EnterpriseSignOnViewModel.kt:411

The code extracts the redirect scheme from server-provided redirect URI to pass to IntentManager.startAuthTab. This determines whether to use deep links (bitwarden://) or app links (https://).

Question: What is the expected behavior when:

  1. User is on self-hosted instance - should app links be supported or only deep links?
  2. Server provides https redirect URI but Digital Asset Links verification fails - will auth flow still work via browser fallback?
  3. Different redirect URI types (deep link vs app link) are used across SSO, Duo, and WebAuthn in same session?

This affects whether validation should allow mixed schemes or enforce consistency per environment/session.

@claude
Copy link
Contributor

claude bot commented Dec 17, 2025

Final Review Assessment

Overall Assessment: REQUEST CHANGES

Critical Security Issues Identified: 4
Important Issues: 3
Technical Debt: 1
Suggestions: 2
Questions: 1

CRITICAL ISSUES (must fix before merge):

  1. Missing redirect URI validation - open redirect vulnerability in SSO, Duo, and WebAuthn flows
  2. App link verification configuration not documented - risk of intent hijacking
  3. Duo redirect URI accepts untrusted server values
  4. WebAuthn redirect URI accepts untrusted server values

IMPORTANT ISSUES (should fix before merge):
5. Redirect URI reconstruction removes path component needed for app links
6. requireNotNull on scheme will crash if server provides malformed URI
7. Missing test coverage for app link path pattern matching

TECHNICAL DEBT:
9. Inconsistent redirect URI handling across auth methods makes security maintenance harder

SUGGESTIONS:
8. Intent filter path pattern broader than necessary
10. Missing security documentation in PR description

POSITIVE ASPECTS:

  • Comprehensive test coverage updates for existing deep link functionality
  • Proper use of autoVerify flag for app link verification
  • Consistent pattern matching logic (host OR path) across all callback utilities
  • Good separation of concerns between deep links and app links
  • Backward compatible - existing deep link flows continue working

The implementation is well-structured but requires security hardening before release. The redirect URI validation is the highest priority fix.

@david-livefront david-livefront force-pushed the PM-26577-app-links-suppoort branch from 79f8019 to 0379b9b Compare December 17, 2025 20:39
@codecov
Copy link

codecov bot commented Dec 17, 2025

Codecov Report

❌ Patch coverage is 85.55556% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.50%. Comparing base (7c929c3) to head (0379b9b).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...warden/network/util/TwoFactorRequiredExtensions.kt 14.28% 0 Missing and 6 partials ⚠️
...itwarden/data/auth/repository/util/WebAuthUtils.kt 57.14% 2 Missing and 1 partial ⚠️
...twarden/data/auth/repository/AuthRepositoryImpl.kt 85.71% 0 Missing and 1 partial ⚠️
...it/bitwarden/data/auth/repository/util/SsoUtils.kt 93.33% 0 Missing and 1 partial ⚠️
...ture/enterprisesignon/EnterpriseSignOnViewModel.kt 95.23% 0 Missing and 1 partial ⚠️
.../feature/twofactorlogin/TwoFactorLoginViewModel.kt 94.44% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6277      +/-   ##
==========================================
- Coverage   85.53%   85.50%   -0.04%     
==========================================
  Files         761      762       +1     
  Lines       54522    54590      +68     
  Branches     7833     7850      +17     
==========================================
+ Hits        46636    46677      +41     
- Misses       5169     5185      +16     
- Partials     2717     2728      +11     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

hold do not merge yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants