Unlock Hub vaults in write mode via the Hub-iOS-License JWT#466
Unlock Hub vaults in write mode via the Hub-iOS-License JWT#466tobihagemann wants to merge 1 commit into
Conversation
WalkthroughThis PR introduces iOS license token verification for Hub authentication. It adds a new Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@CryptomatorCommon/Sources/CryptomatorCommonCore/Hub/HubLicenseVerifier.swift`:
- Around line 150-156: The mock's verify(token:) exits early when
verifyTokenThrowableError is set, so increment verifyTokenCallsCount and set
verifyTokenReceivedToken before throwing; update the method in
HubLicenseVerifier.verify(token:) to first increment verifyTokenCallsCount and
assign verifyTokenReceivedToken = token, then if verifyTokenThrowableError is
non-nil throw it, otherwise proceed to call verifyTokenClosure or return
verifyTokenReturnValue (keeping references to verifyTokenClosure and
verifyTokenReturnValue as currently used).
In
`@CryptomatorCommon/Tests/CryptomatorCommonCoreTests/Hub/HubAuthenticationViewModelTests.swift`:
- Around line 234-240: The test is setting hubLicenseVerifier only inside
withDependencies but HubAuthenticationViewModel (which captures
`@Dependency`(\.hubLicenseVerifier) at init) is created earlier in setUpWithError,
so resolveSubscriptionState(from:) may call the default verifier; fix by
instantiating a new HubAuthenticationViewModel inside the same withDependencies
block (or provide a factory helper that constructs HubAuthenticationViewModel
using current dependencies) immediately after overriding $0.hubLicenseVerifier
and before calling continueToAccessCheck(); apply the same pattern for the other
occurrences mentioned (lines around 266-272, 298-304, 329-335).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d0097921-37ef-4d43-911b-147594a4ee17
📒 Files selected for processing (5)
CryptomatorCommon/Sources/CryptomatorCommonCore/Hub/HubAuthenticationViewModel.swiftCryptomatorCommon/Sources/CryptomatorCommonCore/Hub/HubLicenseVerifier.swiftCryptomatorCommon/Tests/CryptomatorCommonCoreTests/Hub/HubAuthenticationViewModelTests.swiftCryptomatorCommon/Tests/CryptomatorCommonCoreTests/Hub/HubLicenseVerifierTests.swiftSharedResources/en.lproj/Localizable.strings
Hub now returns a
Hub-iOS-Licenseheader on the vault access-token response: a JWT signed by the License Server (ES512). It replacesHub-Subscription-Statefor deciding whether a vault covered by a paid Hub license unlocks in write mode for a freemium user. The old header can't go away yet (older Hub versions are still in production), so it keeps working but is deprecated, and the new header takes precedence when present.This adds JWS verification, which the codebase didn't do before (only JWE). A stateless
HubLicenseVerifierchecks the ES512/P-521 signature against the License Server's embedded public key and enforcesexpmanually (60s leeway). It's wired intoHubAuthenticationViewModel.receivedExistingKeybehind a@Dependencyseam so tests can inject a test key. Precedence: whenHub-iOS-Licenseis present the legacy header is ignored entirely; a bad signature surfaces an error and blocks the unlock rather than silently falling back. The result feeds the existingHubSubscriptionStatepersistence unchanged, so there's no DB migration andPermissionProvideris untouched.The timeless
.active/.inactivesnapshot model is kept on purpose:expgates once at unlock, matching howHub-Subscription-Statealready behaves.Flow
flowchart TD A[access-token response] --> B{Hub-iOS-License present?} B -- no --> C[Legacy Hub-Subscription-State path] B -- yes --> D[Verify ES512 signature] D -- bad signature or malformed --> E[Surface error and block unlock] D -- valid --> F{Not expired?} F -- yes --> G[Write mode .active] F -- no --> H[Read-only .inactive]