fix: security hardening — Keychain, no hardcoded creds, safe URLs
- Add KeychainService for encrypted token storage (auth, refresh, health JWT, API key) - Remove hardcoded email/password from HealthAPIService, store in Keychain - Move all tokens from UserDefaults to Keychain - API key sent via X-API-Key header instead of URL query parameter - Replace force unwrap URL(string:)! with guard let + throws - Fix force unwrap Calendar.date() in HealthKitService - Mark HealthKitService @MainActor for thread-safe @Published - Use withTaskGroup for parallel habit log fetching in TrackerView - Check notification permission before scheduling reminders - Add input validation (title max 200 chars) - Add privacy policy and terms links in Settings - Update CLAUDE.md with security section Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,8 +25,12 @@ class APIService {
|
||||
let baseURL = "https://api.digital-home.site"
|
||||
weak var authManager: AuthManager?
|
||||
|
||||
private func makeRequest(_ path: String, method: String = "GET", token: String? = nil, body: Data? = nil) -> URLRequest {
|
||||
var req = URLRequest(url: URL(string: "\(baseURL)\(path)")!)
|
||||
private func makeRequest(_ path: String, method: String = "GET", token: String? = nil, body: Data? = nil) throws -> URLRequest {
|
||||
let encoded = path.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? path
|
||||
guard let url = URL(string: "\(baseURL)\(encoded)") else {
|
||||
throw APIError.networkError("Неверный URL: \(path)")
|
||||
}
|
||||
var req = URLRequest(url: url)
|
||||
req.httpMethod = method
|
||||
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
req.timeoutInterval = 15
|
||||
@@ -36,7 +40,7 @@ class APIService {
|
||||
}
|
||||
|
||||
private func fetch<T: Decodable>(_ path: String, method: String = "GET", token: String? = nil, body: Data? = nil) async throws -> T {
|
||||
let req = makeRequest(path, method: method, token: token, body: body)
|
||||
let req = try makeRequest(path, method: method, token: token, body: body)
|
||||
let (data, response) = try await URLSession.shared.data(for: req)
|
||||
guard let http = response as? HTTPURLResponse else { throw APIError.networkError("Нет ответа") }
|
||||
if http.statusCode == 401, let auth = authManager, !auth.refreshToken.isEmpty, !path.contains("/auth/refresh") {
|
||||
|
||||
Reference in New Issue
Block a user