Add device access token authentication

This commit is contained in:
diyaa
2026-06-09 12:05:15 +02:00
parent 2945257ea7
commit 45c270c187
49 changed files with 1345 additions and 233 deletions
@@ -345,12 +345,7 @@ final class MacLibraryViewModel {
appVersion: environment.appVersion
)
let response = try await makeAPIClient(for: environment).registerDevice(payload)
try await keychainService.save(response.deviceId, forKey: Self.deviceIdKey)
try await keychainService.save(response.bootstrapToken, forKey: Self.bootstrapTokenKey)
registeredDeviceId = response.deviceId
deviceRegistrationStatus = "Registered successfully at \(response.serverTime)."
try await persistRegisteredDevice(response)
} catch {
deviceRegistrationStatus = "Registration failed: \(error.localizedDescription)"
}
@@ -365,7 +360,7 @@ final class MacLibraryViewModel {
do {
let environment = try currentEnvironment()
let deviceId = try await currentDeviceId()
let deviceId = try await currentOrRegisterDeviceId(for: environment)
let response = try await makeAPIClient(for: environment).sendHeartbeat(
DeviceHeartbeatPayload(
deviceId: deviceId,
@@ -421,7 +416,7 @@ final class MacLibraryViewModel {
do {
let environment = try currentEnvironment()
let deviceId = try await currentDeviceId()
let deviceId = try await currentOrRegisterDeviceId(for: environment)
let fileURL = URL(fileURLWithPath: initialTrack.localFilePath)
try await withStoredFolderAccess {
@@ -589,13 +584,18 @@ final class MacLibraryViewModel {
private func restoreDeviceIdentity() async {
do {
let deviceId = try await keychainService.loadValue(forKey: Self.deviceIdKey)
let deviceAccessToken = try await keychainService.loadValue(
forKey: Self.deviceAccessTokenKey
)
let bootstrapToken = try await keychainService.loadValue(forKey: Self.bootstrapTokenKey)
registeredDeviceId = deviceId
if let deviceId {
if let bootstrapToken, !bootstrapToken.isEmpty {
if let deviceAccessToken, !deviceAccessToken.isEmpty {
deviceRegistrationStatus = "Registered locally."
} else if let bootstrapToken, !bootstrapToken.isEmpty {
deviceRegistrationStatus = "Device ID restored (\(deviceId)). Re-register to get a device access token."
} else {
deviceRegistrationStatus = "Device ID restored (\(deviceId)). Bootstrap token is missing."
}
@@ -621,23 +621,62 @@ final class MacLibraryViewModel {
)
}
private func currentDeviceId() async throws -> String {
private func currentOrRegisterDeviceId(
for environment: ServerEnvironment
) async throws -> String {
if let registeredDeviceId, !registeredDeviceId.isEmpty {
return registeredDeviceId
if let savedDeviceAccessToken = try await keychainService.loadValue(
forKey: Self.deviceAccessTokenKey
), !savedDeviceAccessToken.isEmpty {
return registeredDeviceId
}
}
if let savedDeviceId = try await keychainService.loadValue(forKey: Self.deviceIdKey),
let savedDeviceAccessToken = try await keychainService.loadValue(
forKey: Self.deviceAccessTokenKey
),
!savedDeviceAccessToken.isEmpty,
!savedDeviceId.isEmpty
{
registeredDeviceId = savedDeviceId
return savedDeviceId
}
throw BackendConnectionError.missingDeviceIdentity
let payload = DeviceRegistrationPayload(
platform: .macos,
deviceName: Self.currentDeviceName,
appVersion: environment.appVersion
)
let response = try await makeAPIClient(for: environment).registerDevice(payload)
try await persistRegisteredDevice(response)
return response.deviceId
}
private func makeAPIClient(for environment: ServerEnvironment) -> URLSessionVelodyAPIClient {
URLSessionVelodyAPIClient(environment: environment)
URLSessionVelodyAPIClient(
environment: environment,
deviceAccessTokenProvider: { [self] in
try await self.keychainService.loadValue(forKey: Self.deviceAccessTokenKey)
}
)
}
private func persistRegisteredDevice(
_ response: DeviceRegistrationResponse
) async throws {
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
)
registeredDeviceId = response.deviceId
deviceRegistrationStatus = "Registered successfully at \(response.serverTime)."
}
private func currentTrack(for trackID: String) -> LibraryTrack? {
@@ -847,6 +886,7 @@ final class MacLibraryViewModel {
private static let serverURLDefaultsKey = "velody.server-environment.base-url"
private static let deviceIdKey = "velody.device-id"
private static let deviceAccessTokenKey = "velody.device-access-token"
private static let bootstrapTokenKey = "velody.bootstrap-token"
private static let playbackSessionDefaultsKey = "velody.playback.session"