Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CocoaHeads.xcworkspace/xcshareddata/swiftpm/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 41 additions & 0 deletions CocoaHeadsCore/Sources/CocoaHeadsCore/Auth/AttestDTOs.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// AttestDTOs.swift
//
//
// Created by Mauricio Cardozo on 8/7/26.
//

import Foundation

/// Response of `POST /attest/challenge`. The challenge is single-use and
/// short-lived; the client embeds it in the attestation or assertion it
/// produces next.
public struct AttestChallengeResponse: Codable, Equatable, Sendable {
public init(challenge: String, expiresIn: Int) {
self.challenge = challenge
self.expiresIn = expiresIn
}

/// Base64-encoded random challenge bytes.
public let challenge: String
/// Seconds until the challenge expires.
public let expiresIn: Int
}

/// Body of `POST /attest/key` — registers an App Attest key with the server.
public struct AttestKeyRegistrationRequest: Codable, Equatable, Sendable {
public init(keyId: String, attestation: String, challenge: String) {
self.keyId = keyId
self.attestation = attestation
self.challenge = challenge
}

/// The App Attest key identifier (base64) from `DCAppAttestService.generateKey`.
public let keyId: String
/// Base64-encoded CBOR attestation object from
/// `DCAppAttestService.attestKey(_:clientDataHash:)`, where `clientDataHash`
/// is SHA-256 of the raw challenge bytes.
public let attestation: String
/// The base64 challenge previously issued by `POST /attest/challenge`.
public let challenge: String
}
93 changes: 93 additions & 0 deletions CocoaHeadsCore/Sources/CocoaHeadsCore/Auth/AuthDTOs.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//
// AuthDTOs.swift
//
//
// Created by Mauricio Cardozo on 8/7/26.
//

import Foundation

/// Body of `POST /auth/apple`. Sent by the iOS client after a successful
/// `ASAuthorizationController` run.
public struct AppleSignInRequest: Codable, Equatable, Sendable {
public init(
identityToken: String,
authorizationCode: String,
fullName: String? = nil,
email: String? = nil
) {
self.identityToken = identityToken
self.authorizationCode = authorizationCode
self.fullName = fullName
self.email = email
}

/// The Apple identity token (a JWT) from `ASAuthorizationAppleIDCredential`.
public let identityToken: String
/// The single-use authorization code, exchanged server-side for Apple tokens.
public let authorizationCode: String
/// Only present on the user's first authorization.
public let fullName: String?
/// Only present on the user's first authorization. May be a Hide-My-Email relay.
public let email: String?
}

/// Token pair returned by `POST /auth/apple` and `POST /auth/refresh`.
public struct TokenResponse: Codable, Equatable, Sendable {
public init(
accessToken: String,
refreshToken: String,
expiresIn: Int,
user: UserDTO
) {
self.accessToken = accessToken
self.refreshToken = refreshToken
self.expiresIn = expiresIn
self.user = user
}

/// Backend-signed JWT. Send as `Authorization: Bearer <token>`.
public let accessToken: String
/// Opaque single-use refresh token. Store in the Keychain.
public let refreshToken: String
/// Access-token lifetime in seconds.
public let expiresIn: Int
public let user: UserDTO
}

/// Body of `POST /auth/refresh` and `POST /auth/logout`.
public struct RefreshRequest: Codable, Equatable, Sendable {
public init(refreshToken: String) {
self.refreshToken = refreshToken
}

public let refreshToken: String
}

/// The current user, as returned by `GET /me`.
public struct UserDTO: Codable, Equatable, Sendable {
public init(
id: UUID,
email: String? = nil,
fullName: String? = nil,
role: UserRole
) {
self.id = id
self.email = email
self.fullName = fullName
self.role = role
}

public let id: UUID
public let email: String?
public let fullName: String?
public let role: UserRole
}

/// Roles are foundation-only for now: they are embedded in access-token claims
/// but not yet enforced by any middleware.
public enum UserRole: String, Codable, Equatable, Sendable, CaseIterable {
case user
case organizer
case admin
}
Loading