Skip to content

Instantly share code, notes, and snippets.

@jarodl
Last active April 26, 2025 00:14
Show Gist options
  • Save jarodl/5c89d7f928991a8f7b18cd986b17337a to your computer and use it in GitHub Desktop.
Save jarodl/5c89d7f928991a8f7b18cd986b17337a to your computer and use it in GitHub Desktop.
import AuthenticationServices
import Foundation
/// A response type that encapsulates the result of an authorization request
public enum ASAuthorizationResponse {
case registration(ASAuthorizationPlatformPublicKeyCredentialRegistration)
case assertion(ASAuthorizationPlatformPublicKeyCredentialAssertion)
}
/// A wrapper class around ASAuthorizationController that provides async/await functionality
public final class AsyncAuthorizationController {
private let controller: ASAuthorizationController
private var delegate: AsyncAuthorizationDelegate?
public init(controller: ASAuthorizationController) {
self.controller = controller
}
/// Performs authorization requests asynchronously
/// - Returns: An ASAuthorizationResponse containing the result of the authorization
/// - Throws: ASAuthorizationError if the authorization fails
public func performRequests() async throws -> ASAuthorizationResponse {
try await withCheckedThrowingContinuation { continuation in
Task { @MainActor in
let delegate = AsyncAuthorizationDelegate { result in
continuation.resume(with: result)
}
self.delegate = delegate
self.controller.delegate = delegate
self.controller.performRequests()
}
}
}
}
@MainActor
private final class AsyncAuthorizationDelegate: NSObject, ASAuthorizationControllerDelegate {
private let completion: (Result<ASAuthorizationResponse, Error>) -> Void
init(completion: @escaping (Result<ASAuthorizationResponse, Error>) -> Void) {
self.completion = completion
super.init()
}
func authorizationController(
controller: ASAuthorizationController,
didCompleteWithAuthorization authorization: ASAuthorization
) {
if let registration = authorization.credential as? ASAuthorizationPlatformPublicKeyCredentialRegistration {
completion(.success(.registration(registration)))
} else if let assertion = authorization.credential as? ASAuthorizationPlatformPublicKeyCredentialAssertion {
completion(.success(.assertion(assertion)))
} else {
completion(.failure(ASAuthorizationError(.failed)))
}
}
func authorizationController(
controller: ASAuthorizationController,
didCompleteWithError error: Error
) {
completion(.failure(error))
}
}
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.presentationContextProvider = presentationContextProvider
let asyncController = AsyncAuthorizationController(controller: controller)
do {
let authResponse = try await asyncController.performRequests()
switch authResponse {
case .registration(let registration):
...
case .assertion:
...
}
} catch {
throw PasskeyError.registrationFailed("Registration failed")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment