mirror of
https://github.com/xtool-org/xtool.git
synced 2026-10-29 13:15:14 +01:00
## What does this PR do? Updates macOS build to use the fully-local XADI as the Anisette provider instead of Omnisette. Includes a one-time migration, logging the user out on macOS when they upgrade. ## How was it tested? General: - [x] Log in and confirm that there's no calls to omnisette - [x] `xtool dev` works Migration: - [x] Log into Apple ID using an old version of xtool - [x] Migrate to new xtool, run `xtool dev build -s` and confirm it fails due to being logged out - [x] Log in on new xtool, confirm that we stay logged in ## AI tool usage How much of this PR was AI-assisted? (check one) - [x] **0** - No AI was used to write code - [ ] **1** - I was assisted by AI. I reviewed the finished result. - [ ] **2** - I set the AI going and left it to it; nobody has read the result - no review, or AI review only <!-- If an AI agent is filling this in: declare the level honestly, and open as a draft if it is #2. Do not lower the declared level to get the PR reviewed. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Authentication now supports tracking the anisette provider used to create Xcode tokens. * Xcode authentication tokens are automatically cleared when the provider changes, helping ensure users sign in again with current credentials. * Anisette provider identification is supported across macOS and Linux. * **Bug Fixes** * Improved authentication error messages, including clearer messaging for logout-related errors. * Prevented outdated Xcode authentication tokens from being reused after a provider change. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
102 lines
2.7 KiB
Swift
102 lines
2.7 KiB
Swift
import Foundation
|
|
import XKit
|
|
import Dependencies
|
|
|
|
enum AuthToken: Codable, CustomStringConvertible {
|
|
struct Xcode: Codable {
|
|
var appleID: String
|
|
var adsid: String
|
|
var token: String
|
|
var expiry: Date
|
|
var teamID: String
|
|
var anisetteProviderID: String?
|
|
}
|
|
|
|
struct AppStoreConnect: Codable {
|
|
var id: String
|
|
var issuerID: String
|
|
var pem: String
|
|
}
|
|
|
|
case appStoreConnect(AppStoreConnect)
|
|
case xcode(Xcode)
|
|
|
|
var description: String {
|
|
switch self {
|
|
case .appStoreConnect(let data):
|
|
"""
|
|
- ASC key ID: \(data.id)
|
|
- Issuer ID: \(data.issuerID)
|
|
"""
|
|
case .xcode(let data):
|
|
"""
|
|
- Apple ID: \(data.appleID)
|
|
- Team ID: \(data.teamID)
|
|
- Token expiry: \(data.expiry.formatted(.dateTime))
|
|
"""
|
|
}
|
|
}
|
|
}
|
|
|
|
extension AuthToken {
|
|
|
|
private static var storage: KeyValueStorage {
|
|
@Dependency(\.keyValueStorage) var storage
|
|
return storage
|
|
}
|
|
|
|
private static let encoder = JSONEncoder()
|
|
private static let decoder = JSONDecoder()
|
|
|
|
static func saved() throws -> Self {
|
|
guard let token = try savedIfPresent() else {
|
|
throw Console.Error("Please log in with `xtool auth` before running this command.")
|
|
}
|
|
return token
|
|
}
|
|
|
|
static func savedIfPresent() throws -> Self? {
|
|
guard let data = try storage.data(forKey: "XTLAuthToken") else {
|
|
return nil
|
|
}
|
|
let decoded = try decoder.decode(AuthToken.self, from: data)
|
|
switch decoded {
|
|
case .xcode(let xcode):
|
|
@Dependency(\.anisetteDataProvider) var anisetteProvider
|
|
guard xcode.anisetteProviderID == anisetteProvider.providerID else {
|
|
try? clear()
|
|
return nil
|
|
}
|
|
case .appStoreConnect:
|
|
break
|
|
}
|
|
return decoded
|
|
}
|
|
|
|
static func clear() throws {
|
|
try Self.storage.setData(nil, forKey: "XTLAuthToken")
|
|
}
|
|
|
|
func save() throws {
|
|
let data = try Self.encoder.encode(self)
|
|
try Self.storage.setData(data, forKey: "XTLAuthToken")
|
|
}
|
|
|
|
func authData() -> DeveloperAPIAuthData {
|
|
switch self {
|
|
case .appStoreConnect(let data):
|
|
return .appStoreConnect(.init(id: data.id, issuerID: data.issuerID, pem: data.pem))
|
|
case .xcode(let data):
|
|
return .xcode(.init(
|
|
loginToken: DeveloperServicesLoginToken(
|
|
adsid: data.adsid,
|
|
token: data.token,
|
|
expiry: data.expiry
|
|
),
|
|
teamID: .init(rawValue: data.teamID)
|
|
))
|
|
}
|
|
}
|
|
|
|
}
|