// // OAuthManager.swift // FantasyWatch // // Created by Claude Code // import Foundation actor OAuthManager { private let tokenStorage: OAuthTokenStorage private let clientID: String private let clientSecret: String private var refreshTask: Task? private var currentUserID: String? init( tokenStorage: OAuthTokenStorage, clientID: String, clientSecret: String ) { self.tokenStorage = tokenStorage self.clientID = clientID self.clientSecret = clientSecret } func setCurrentUser(_ userID: String) { self.currentUserID = userID } func validToken() async throws -> String { guard let userID = currentUserID else { throw OAuthError.authorizationFailed } if await tokenStorage.isTokenValid(for: userID) { guard let token = await tokenStorage.getAccessToken(for: userID) else { throw OAuthError.tokenExpired } return token } let refreshedTokenPair = try await refreshToken() return refreshedTokenPair.accessToken } func refreshToken() async throws -> TokenPair { if let existingTask = refreshTask { return try await existingTask.value } guard let userID = currentUserID else { throw OAuthError.authorizationFailed } guard let refreshToken = await tokenStorage.getRefreshToken(for: userID) else { throw OAuthError.noRefreshToken } let task = Task { let url = URL(string: "https://api.login.yahoo.com/oauth2/get_token")! var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") let bodyParams = [ "grant_type": "refresh_token", "refresh_token": refreshToken, "client_id": clientID, "client_secret": clientSecret ] let bodyString = bodyParams .map { "\($0.key)=\($0.value)" } .joined(separator: "&") request.httpBody = bodyString.data(using: .utf8) let (data, response) = try await URLSession.shared.data(for: request) guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { throw OAuthError.invalidResponse } let tokenResponse = try JSONDecoder().decode(OAuthTokenResponse.self, from: data) let tokenPair = tokenResponse.toTokenPair() await tokenStorage.saveTokenPair(tokenPair, for: userID) return tokenPair } refreshTask = task defer { refreshTask = nil } return try await task.value } func saveTokenPair(_ tokenPair: TokenPair) async { guard let userID = currentUserID else { return } await tokenStorage.saveTokenPair(tokenPair, for: userID) } func clearTokens() async { guard let userID = currentUserID else { return } await tokenStorage.clearTokens(for: userID) } }