v1.5.0 — getProfile() + ProfileInfo
Apps können den 2FA-Status des eingeloggten Users lesen, damit AccountView entscheidet ob "Zwei-Faktor aktivieren" oder "Zwei-Faktor deaktivieren" angezeigt wird. ProfileInfo (public struct) — id, email, name, emailVerified, twoFactorEnabled. AuthClient.getProfile() -> ProfileInfo — lädt das Profil vom Server (GET /api/v1/auth/profile → Better Auths /api/auth/get-session). Nutzt Session-Token als Bearer (Wire-Konvention). 4 neue Tests, 70/70 grün. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0a79083b58
commit
fe607c15d2
397 changed files with 2876 additions and 0 deletions
|
|
@ -635,3 +635,101 @@ private struct TotpEnableResponse: Decodable {
|
|||
let totpURI: String?
|
||||
let backupCodes: [String]?
|
||||
}
|
||||
|
||||
// MARK: - Profile
|
||||
|
||||
/// Subset des Server-User-Profiles, das die Apps für UI-Entscheidungen
|
||||
/// brauchen. Wird von ``AuthClient/getProfile()`` geliefert.
|
||||
///
|
||||
/// Server-Quelle: `GET /api/v1/auth/profile` (→ Better Auths
|
||||
/// `/api/auth/get-session`). Returnt `{user: {…}, session: {…}}`.
|
||||
/// Wir nehmen nur die UI-relevanten Felder mit.
|
||||
public struct ProfileInfo: Sendable, Equatable {
|
||||
public let id: String
|
||||
public let email: String
|
||||
public let name: String?
|
||||
public let emailVerified: Bool
|
||||
/// `true` wenn der User TOTP-2FA aktiviert hat. Apps zeigen
|
||||
/// dann den Disable-/Regenerate-Pfad statt des Enroll-Wizards.
|
||||
public let twoFactorEnabled: Bool
|
||||
|
||||
public init(
|
||||
id: String,
|
||||
email: String,
|
||||
name: String?,
|
||||
emailVerified: Bool,
|
||||
twoFactorEnabled: Bool
|
||||
) {
|
||||
self.id = id
|
||||
self.email = email
|
||||
self.name = name
|
||||
self.emailVerified = emailVerified
|
||||
self.twoFactorEnabled = twoFactorEnabled
|
||||
}
|
||||
}
|
||||
|
||||
public extension AuthClient {
|
||||
/// Lädt das aktuelle Profil des eingeloggten Users vom Server.
|
||||
///
|
||||
/// - Important: Nutzt den Session-Token als Bearer (Wire-Konvention
|
||||
/// für mana-auth-Endpoints, siehe Doc-Header dieser Datei).
|
||||
///
|
||||
/// - Throws: ``AuthError/notSignedIn`` ohne Session,
|
||||
/// ``AuthError/unauthorized`` wenn Server den Token rejected,
|
||||
/// Netzwerk-Cases.
|
||||
func getProfile() async throws -> ProfileInfo {
|
||||
let token = try currentSessionToken()
|
||||
let url = config.authBaseURL.appending(path: "/api/v1/auth/profile")
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "GET"
|
||||
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
||||
|
||||
let (data, http): (Data, HTTPURLResponse)
|
||||
do {
|
||||
let (d, r) = try await session.data(for: request)
|
||||
guard let h = r as? HTTPURLResponse else {
|
||||
throw AuthError.networkFailure("Keine HTTP-Antwort")
|
||||
}
|
||||
data = d
|
||||
http = h
|
||||
} catch let error as URLError {
|
||||
throw AuthError.networkFailure(error.localizedDescription)
|
||||
}
|
||||
|
||||
guard http.statusCode == 200 else {
|
||||
throw AuthError.classify(
|
||||
status: http.statusCode,
|
||||
data: data,
|
||||
retryAfterHeader: http.retryAfterSeconds
|
||||
)
|
||||
}
|
||||
|
||||
let envelope = try JSONDecoder().decode(ProfileEnvelope.self, from: data)
|
||||
guard let user = envelope.user else {
|
||||
throw AuthError.decoding("user-Feld fehlt in profile-Antwort")
|
||||
}
|
||||
|
||||
return ProfileInfo(
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
emailVerified: user.emailVerified ?? false,
|
||||
twoFactorEnabled: user.twoFactorEnabled ?? false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wire-Format-Hülle für `GET /api/v1/auth/profile`. Better-Auth-
|
||||
/// `/api/auth/get-session` returnt `{user: {...}, session: {...}}`;
|
||||
/// wir lesen `user` und ignorieren `session`.
|
||||
private struct ProfileEnvelope: Decodable {
|
||||
let user: ProfileUser?
|
||||
}
|
||||
|
||||
private struct ProfileUser: Decodable {
|
||||
let id: String
|
||||
let email: String
|
||||
let name: String?
|
||||
let emailVerified: Bool?
|
||||
let twoFactorEnabled: Bool?
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue