-
Notifications
You must be signed in to change notification settings - Fork 99
[PM-24195] fix: Pass userId to refreshToken and setTokens #2374
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
LRNcardozoWDF
wants to merge
24
commits into
main
Choose a base branch
from
cmcg/pm-24195-log-error-response-model
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
8b1762b
pm-24195 Log this error as general error
LRNcardozoWDF 08eb750
pm-32276 Add new funcs and tests with explicit userId
LRNcardozoWDF e927d7a
pm-24195 Log error in account token provider
LRNcardozoWDF 0c069d7
Merge branch 'main' into cmcg/pm-24195-log-error-response-model
LRNcardozoWDF 4b4893d
Merge branch 'main' into cmcg/pm-24195-log-error-response-model
LRNcardozoWDF 0b57b8a
[PM-24195] Force log to crashlytics
LRNcardozoWDF f0b166f
Merge branch 'main' into cmcg/pm-24195-log-error-response-model
LRNcardozoWDF 2ef74b9
Merge branch 'main' into cmcg/pm-32276-verify-user-id-before-refresh-β¦
LRNcardozoWDF 0c24943
Merge remote-tracking branch 'origin/main' into cmcg/pm-24195-log-errβ¦
LRNcardozoWDF 87c4115
[PM-24195] Fix tests
LRNcardozoWDF 2db926d
Merge branch 'main' into cmcg/pm-24195-log-error-response-model
LRNcardozoWDF c758640
[PM-24195] Fix pr comment
LRNcardozoWDF 0c40b7e
Merge branch 'main' into cmcg/pm-24195-log-error-response-model
LRNcardozoWDF c29a11a
Merge branch 'main' into cmcg/pm-24195-log-error-response-model
LRNcardozoWDF 6ea36da
Merge branch 'main' into cmcg/pm-24195-log-error-response-model
LRNcardozoWDF 1f2667a
Merge branch 'main' of https://github.com/bitwarden/ios into cmcg/pm-β¦
LRNcardozoWDF a62f0ed
Merge branch 'main' into cmcg/pm-24195-log-error-response-model
LRNcardozoWDF 764ca8f
Merge remote-tracking branch 'origin/main' into cmcg/pm-32276-verify-β¦
LRNcardozoWDF 0e254f8
[PM-24195] Fix pr comments
LRNcardozoWDF 766613a
Merge branch 'main' into cmcg/pm-32276-verify-user-id-before-refresh-β¦
LRNcardozoWDF d667f2c
Merge branch 'main' into cmcg/pm-24195-log-error-response-model
LRNcardozoWDF 4ac7416
Merge branch 'cmcg/pm-32276-verify-user-id-before-refresh-token' intoβ¦
LRNcardozoWDF a7d3bcf
[PM-24195] Fix pr comment and remove unnecessary code
LRNcardozoWDF 70f5629
Merge branch 'main' into cmcg/pm-24195-log-error-response-model
LRNcardozoWDF File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,9 @@ actor DefaultAccountTokenProvider: AccountTokenProvider { | |
| /// The delegate to use for specific operations on the token provider. | ||
| private weak var accountTokenProviderDelegate: AccountTokenProviderDelegate? | ||
|
|
||
| /// The service used to report non-fatal errors. | ||
| private let errorReporter: ErrorReporter | ||
|
|
||
| /// The `HTTPService` used to make the API call to refresh the access token. | ||
| private let httpService: HTTPService | ||
|
|
||
|
|
@@ -42,15 +45,18 @@ actor DefaultAccountTokenProvider: AccountTokenProvider { | |
| /// - httpService: The service used to make the API call to refresh the access token. | ||
| /// - timeProvider: The service used to get the present time. | ||
| /// - tokenService: The service used to get the current tokens from. | ||
| /// - errorReporter: The service used to report non-fatal errors. | ||
| /// | ||
| init( | ||
| httpService: HTTPService, | ||
| timeProvider: TimeProvider = CurrentTime(), | ||
| tokenService: TokenService, | ||
| errorReporter: ErrorReporter, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π€ Should these be alphabetized? |
||
| ) { | ||
| self.httpService = httpService | ||
| self.timeProvider = timeProvider | ||
| self.tokenService = tokenService | ||
| self.errorReporter = errorReporter | ||
| } | ||
|
|
||
| // MARK: Methods | ||
|
|
@@ -81,16 +87,29 @@ actor DefaultAccountTokenProvider: AccountTokenProvider { | |
| defer { self.refreshTask = nil } | ||
|
|
||
| do { | ||
| let refreshToken = try await tokenService.getRefreshToken() | ||
| let expectedUserId = try await tokenService.getActiveAccountId() | ||
|
|
||
| let refreshToken = try await tokenService.getRefreshToken(userId: expectedUserId) | ||
| let response = try await httpService.send( | ||
| IdentityTokenRefreshRequest(refreshToken: refreshToken), | ||
| ) | ||
| let expirationDate = timeProvider.presentTime.addingTimeInterval(TimeInterval(response.expiresIn)) | ||
|
|
||
| let userIdAfter = try await tokenService.getActiveAccountId() | ||
| guard expectedUserId == userIdAfter else { | ||
| let error = AccountTokenProviderError( | ||
| userIdBefore: expectedUserId, | ||
| userIdAfter: userIdAfter, | ||
| ) | ||
| errorReporter.log(error: error) | ||
| throw error | ||
| } | ||
|
|
||
| try await tokenService.setTokens( | ||
| accessToken: response.accessToken, | ||
| refreshToken: response.refreshToken, | ||
| expirationDate: expirationDate, | ||
| userId: expectedUserId, | ||
| ) | ||
|
|
||
| return response.accessToken | ||
|
|
||
24 changes: 24 additions & 0 deletions
24
BitwardenShared/Core/Platform/Services/API/AccountTokenProviderError.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import Foundation | ||
|
|
||
| // MARK: - AccountTokenProviderError | ||
|
|
||
| /// Error logged when the active account changes during a token refresh operation. | ||
| /// | ||
| struct AccountTokenProviderError: Error, CustomStringConvertible { | ||
| // MARK: Properties | ||
|
|
||
| /// The active user ID before the token refresh operation. | ||
| let userIdBefore: String | ||
|
|
||
| /// The active user ID after the token refresh operation. | ||
| let userIdAfter: String | ||
|
|
||
| // MARK: CustomStringConvertible | ||
|
|
||
| var description: String { | ||
| """ | ||
| Token refresh race condition detected: Active account changed from '\(userIdBefore)' to '\(userIdAfter)' \ | ||
| during token refresh operation. Tokens were not stored. | ||
| """ | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,15 +10,35 @@ class MockTokenService: TokenService { | |
| var getIsExternalResult: Result<Bool, Error> = .success(false) | ||
| var refreshToken: String? = "REFRESH_TOKEN" | ||
|
|
||
| // Track which userId was used in explicit userId methods | ||
| var getAccessTokenCalledWithUserId: String? | ||
| var getRefreshTokenCalledWithUserId: String? | ||
| var setTokensCalledWithUserId: String? | ||
| var activeAccountId: String = "1" | ||
| var accessTokenByUserId: [String: String] = [:] | ||
| var refreshTokenByUserId: [String: String] = [:] | ||
|
Comment on lines
+13
to
+19
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. βοΈ Can you alphabetize these with the other properties? |
||
|
|
||
| func getAccessToken() async throws -> String { | ||
| guard let accessToken else { throw StateServiceError.noActiveAccount } | ||
| return accessToken | ||
| } | ||
|
|
||
| func getAccessToken(userId: String) async throws -> String { | ||
| getAccessTokenCalledWithUserId = userId | ||
| return accessTokenByUserId[userId] ?? accessToken ?? "ACCESS_TOKEN" | ||
| } | ||
|
|
||
| func getAccessTokenExpirationDate() async throws -> Date? { | ||
| try accessTokenExpirationDateResult.get() | ||
| } | ||
|
|
||
| func getActiveAccountId() async throws -> String { | ||
| if activeAccountId.isEmpty { | ||
| throw StateServiceError.noActiveAccount | ||
| } | ||
| return activeAccountId | ||
| } | ||
|
|
||
| func getIsExternal() async throws -> Bool { | ||
| try getIsExternalResult.get() | ||
| } | ||
|
|
@@ -28,9 +48,24 @@ class MockTokenService: TokenService { | |
| return refreshToken | ||
| } | ||
|
|
||
| func getRefreshToken(userId: String) async throws -> String { | ||
| getRefreshTokenCalledWithUserId = userId | ||
| return refreshTokenByUserId[userId] ?? refreshToken ?? "REFRESH_TOKEN" | ||
| } | ||
|
|
||
| func setTokens(accessToken: String, refreshToken: String, expirationDate: Date) async { | ||
| self.accessToken = accessToken | ||
| self.refreshToken = refreshToken | ||
| self.expirationDate = expirationDate | ||
| } | ||
|
|
||
| func setTokens(accessToken: String, refreshToken: String, expirationDate: Date, userId: String) async { | ||
| setTokensCalledWithUserId = userId | ||
| accessTokenByUserId[userId] = accessToken | ||
| refreshTokenByUserId[userId] = refreshToken | ||
| self.expirationDate = expirationDate | ||
| // Also update legacy properties for backward compatibility with existing tests | ||
| self.accessToken = accessToken | ||
| self.refreshToken = refreshToken | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
βοΈ Does it make sense to keep these next to
getRefreshTokenResultandgetRefreshTokenResult. That's the pattern we've used elsewhere and keeps them alphabetized.