Replace manual backend config with auto JWT auth
Some checks failed
CI / build-and-deploy (push) Failing after 6s
Some checks failed
CI / build-and-deploy (push) Failing after 6s
Device auto-registers on first launch using hardcoded backend URL and anon key (MarksAuth.swift). 90-day JWT stored in UserDefaults, refreshed transparently. No settings fields needed — ClaudeService is always active and non-optional throughout the app. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
52
Marks/Services/MarksAuth.swift
Normal file
52
Marks/Services/MarksAuth.swift
Normal file
@@ -0,0 +1,52 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
// Hardcoded backend config — no user-facing settings needed
|
||||
private let backendBaseURL = "https://magicive-api-production.up.railway.app"
|
||||
private let marksAnonKey = "marks-anon-2025"
|
||||
|
||||
private let tokenKey = "marksJWT"
|
||||
private let tokenExpiryKey = "marksJWTExpiry"
|
||||
|
||||
enum MarksAuth {
|
||||
static var baseURL: String { backendBaseURL }
|
||||
|
||||
/// Returns a valid JWT, registering if missing or within 1 hour of expiry.
|
||||
static func validToken() async throws -> String {
|
||||
if let token = cached(), !token.isEmpty { return token }
|
||||
return try await register()
|
||||
}
|
||||
|
||||
private static func cached() -> String? {
|
||||
guard
|
||||
let token = UserDefaults.standard.string(forKey: tokenKey),
|
||||
let expiry = UserDefaults.standard.object(forKey: tokenExpiryKey) as? Date,
|
||||
expiry > Date().addingTimeInterval(3600)
|
||||
else { return nil }
|
||||
return token
|
||||
}
|
||||
|
||||
private static func register() async throws -> String {
|
||||
guard let url = URL(string: "\(backendBaseURL)/api/auth/register") else {
|
||||
throw URLError(.badURL)
|
||||
}
|
||||
let deviceId = UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
|
||||
var req = URLRequest(url: url)
|
||||
req.httpMethod = "POST"
|
||||
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
req.setValue(marksAnonKey, forHTTPHeaderField: "X-API-Key")
|
||||
req.httpBody = try JSONSerialization.data(withJSONObject: ["device_id": deviceId])
|
||||
let (data, response) = try await URLSession.shared.data(for: req)
|
||||
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
|
||||
guard (200...299).contains(status) else {
|
||||
throw APIError.badStatus(status)
|
||||
}
|
||||
struct Resp: Decodable { let access_token: String; let expires_in: Int }
|
||||
let resp = try JSONDecoder().decode(Resp.self, from: data)
|
||||
UserDefaults.standard.set(resp.access_token, forKey: tokenKey)
|
||||
let expiry = Date().addingTimeInterval(TimeInterval(resp.expires_in))
|
||||
UserDefaults.standard.set(expiry, forKey: tokenExpiryKey)
|
||||
print("[Auth] Registered device \(deviceId.prefix(8))…")
|
||||
return resp.access_token
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user