Add device access token authentication
This commit is contained in:
@@ -469,7 +469,12 @@ final class iPhoneLibraryViewModel {
|
||||
baseURL: ServerEnvironment.defaultLocalBaseURL,
|
||||
appVersion: "0.1.0"
|
||||
)
|
||||
let apiClient = URLSessionVelodyAPIClient(environment: environment)
|
||||
let apiClient = URLSessionVelodyAPIClient(
|
||||
environment: environment,
|
||||
deviceAccessTokenProvider: {
|
||||
try await keychainService.loadValue(forKey: Self.deviceAccessTokenKey)
|
||||
}
|
||||
)
|
||||
let store = Self.makeRemoteLibraryStore()
|
||||
let downloadStateStore = Self.makeRemoteTrackDownloadStateStore()
|
||||
let audioFileStore = Self.makeOfflineAudioFileStore()
|
||||
@@ -657,7 +662,10 @@ final class iPhoneLibraryViewModel {
|
||||
private func currentOrRegisterDeviceID() async throws -> String {
|
||||
if let existingDeviceID = try await keychainService.loadValue(
|
||||
forKey: Self.deviceIDKey
|
||||
), !existingDeviceID.isEmpty {
|
||||
), !existingDeviceID.isEmpty,
|
||||
let existingDeviceAccessToken = try await keychainService.loadValue(
|
||||
forKey: Self.deviceAccessTokenKey
|
||||
), !existingDeviceAccessToken.isEmpty {
|
||||
return existingDeviceID
|
||||
}
|
||||
|
||||
@@ -670,6 +678,10 @@ final class iPhoneLibraryViewModel {
|
||||
)
|
||||
|
||||
try await keychainService.save(response.deviceId, forKey: Self.deviceIDKey)
|
||||
try await keychainService.save(
|
||||
response.deviceAccessToken,
|
||||
forKey: Self.deviceAccessTokenKey
|
||||
)
|
||||
try await keychainService.save(
|
||||
response.bootstrapToken,
|
||||
forKey: Self.bootstrapTokenKey
|
||||
@@ -914,6 +926,7 @@ final class iPhoneLibraryViewModel {
|
||||
#endif
|
||||
|
||||
private static let deviceIDKey = "velody.iphone.device-id"
|
||||
private static let deviceAccessTokenKey = "velody.iphone.device-access-token"
|
||||
private static let bootstrapTokenKey = "velody.iphone.bootstrap-token"
|
||||
private static let remoteLibraryEmptyMessage = LibrarySectionMessage(
|
||||
title: "No music synced yet",
|
||||
|
||||
@@ -454,6 +454,71 @@ final class iPhoneLibraryViewModelPolishTests: XCTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
final class iPhoneLibraryViewModelDeviceAuthTests: XCTestCase {
|
||||
func testRefreshSyncStoresDeviceAccessTokenWhenRegistrationRuns() async throws {
|
||||
let keychain = MemoryKeychainService()
|
||||
let counter = RegisterCallCounter()
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [
|
||||
makeRemoteTrack(
|
||||
trackId: "remote-auth-store",
|
||||
assetId: "asset-auth-store",
|
||||
title: "Store Token"
|
||||
),
|
||||
],
|
||||
apiClient: TestRegisterAPIClient(counter: counter),
|
||||
keychainService: keychain
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
await viewModel.refreshSync()
|
||||
|
||||
let storedDeviceID = try await keychain.loadValue(forKey: "velody.iphone.device-id")
|
||||
let storedDeviceAccessToken = try await keychain.loadValue(
|
||||
forKey: "velody.iphone.device-access-token"
|
||||
)
|
||||
|
||||
XCTAssertEqual(await counter.count, 1)
|
||||
XCTAssertFalse((storedDeviceID ?? "").isEmpty)
|
||||
XCTAssertFalse((storedDeviceAccessToken ?? "").isEmpty)
|
||||
}
|
||||
|
||||
func testExistingDeviceWithoutAccessTokenReregistersCleanly() async throws {
|
||||
let keychain = MemoryKeychainService()
|
||||
let legacyDeviceID = "legacy-device-id"
|
||||
try await keychain.save(legacyDeviceID, forKey: "velody.iphone.device-id")
|
||||
try await keychain.save(
|
||||
"legacy-bootstrap-token",
|
||||
forKey: "velody.iphone.bootstrap-token"
|
||||
)
|
||||
let counter = RegisterCallCounter()
|
||||
let viewModel = makeViewModel(
|
||||
remoteTracks: [
|
||||
makeRemoteTrack(
|
||||
trackId: "remote-auth-reregister",
|
||||
assetId: "asset-auth-reregister",
|
||||
title: "Re-register Token"
|
||||
),
|
||||
],
|
||||
apiClient: TestRegisterAPIClient(counter: counter),
|
||||
keychainService: keychain
|
||||
)
|
||||
|
||||
await viewModel.loadIfNeeded()
|
||||
await viewModel.refreshSync()
|
||||
|
||||
let storedDeviceID = try await keychain.loadValue(forKey: "velody.iphone.device-id")
|
||||
let storedDeviceAccessToken = try await keychain.loadValue(
|
||||
forKey: "velody.iphone.device-access-token"
|
||||
)
|
||||
|
||||
XCTAssertEqual(await counter.count, 1)
|
||||
XCTAssertNotEqual(storedDeviceID, legacyDeviceID)
|
||||
XCTAssertFalse((storedDeviceAccessToken ?? "").isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
private actor RegisterCallCounter {
|
||||
private(set) var count = 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user