init
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// AuthService.swift
|
||||
// furumi_macos
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct AuthService {
|
||||
|
||||
private let urlSession = URLSession.shared
|
||||
|
||||
private let decoder: JSONDecoder = {
|
||||
let d = JSONDecoder()
|
||||
d.keyDecodingStrategy = .convertFromSnakeCase
|
||||
return d
|
||||
}()
|
||||
|
||||
private var deviceName: String {
|
||||
ProcessInfo.processInfo.hostName.components(separatedBy: ".").first ?? "macOS"
|
||||
}
|
||||
|
||||
// MARK: - URL builders
|
||||
|
||||
func ssoStartURL(baseUrl: String) -> URL? {
|
||||
guard var components = URLComponents(string: "\(baseUrl)/auth/mobile/oidc/start") else { return nil }
|
||||
components.queryItems = [URLQueryItem(name: "redirect_uri", value: "furumi://auth/callback")]
|
||||
return components.url
|
||||
}
|
||||
|
||||
// MARK: - API
|
||||
|
||||
func login(baseUrl: String, username: String, password: String) async throws -> LoginResponse {
|
||||
struct Body: Encodable {
|
||||
let username: String
|
||||
let password: String
|
||||
let device_name: String
|
||||
}
|
||||
return try await post(
|
||||
urlString: "\(baseUrl)/api/auth/password",
|
||||
body: Body(username: username, password: password, device_name: deviceName)
|
||||
)
|
||||
}
|
||||
|
||||
func ssoExchange(baseUrl: String, code: String) async throws -> LoginResponse {
|
||||
struct Body: Encodable {
|
||||
let code: String
|
||||
let device_name: String
|
||||
}
|
||||
return try await post(
|
||||
urlString: "\(baseUrl)/api/auth/sso/exchange",
|
||||
body: Body(code: code, device_name: deviceName)
|
||||
)
|
||||
}
|
||||
|
||||
func refresh(baseUrl: String, refreshToken: String) async throws -> TokenResponse {
|
||||
struct Body: Encodable { let refresh_token: String }
|
||||
return try await post(
|
||||
urlString: "\(baseUrl)/api/auth/refresh",
|
||||
body: Body(refresh_token: refreshToken)
|
||||
)
|
||||
}
|
||||
|
||||
func logout(baseUrl: String, authorizationHeader: String, refreshToken: String) async throws {
|
||||
struct Body: Encodable { let refresh_token: String }
|
||||
let _: LogoutResponse = try await post(
|
||||
urlString: "\(baseUrl)/api/auth/logout",
|
||||
body: Body(refresh_token: refreshToken),
|
||||
authHeader: authorizationHeader
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func post<B: Encodable, R: Decodable>(
|
||||
urlString: String,
|
||||
body: B,
|
||||
authHeader: String? = nil
|
||||
) async throws -> R {
|
||||
guard let url = URL(string: urlString) else {
|
||||
throw AuthError.invalidServerUrl("Invalid URL: \(urlString)")
|
||||
}
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
if let authHeader { request.setValue(authHeader, forHTTPHeaderField: "Authorization") }
|
||||
request.httpBody = try JSONEncoder().encode(body)
|
||||
|
||||
let (data, response) = try await urlSession.data(for: request)
|
||||
|
||||
guard let http = response as? HTTPURLResponse else {
|
||||
throw AuthError.invalidResponse
|
||||
}
|
||||
|
||||
guard (200..<300).contains(http.statusCode) else {
|
||||
let msg = (try? decoder.decode(ErrorResponse.self, from: data))?.error ?? ""
|
||||
throw AuthError.serverError(http.statusCode, msg)
|
||||
}
|
||||
|
||||
do {
|
||||
return try decoder.decode(R.self, from: data)
|
||||
} catch {
|
||||
throw AuthError.invalidResponse
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user