smithery/rektoooooo

ios-authentication

iOS authentication expert for user sign-in. Use when working with Sign in with Apple, biometric authentication (Face ID, Touch ID), Keychain credentials, or password autofill.

Installation

$ npx skills add smithery/rektoooooo --skill ios-authentication

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/rektoooooo · top by installs.

npx skills add smithery/rektoooooo

Browse all from smithery/rektoooooo

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 13,202 B
  • docs SUMMARY.md 201 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

iOS Authentication

Expert guidance for implementing authentication in iOS apps.

Sign in with Apple

Setup

  1. Add "Sign in with Apple" capability in Xcode
  2. Configure in App Store Connect

Sign in with Apple Button

import AuthenticationServices

struct SignInView: View {
    @Environment(\.colorScheme) var colorScheme

    var body: some View {
        SignInWithAppleButton(.signIn) { request in
            request.requestedScopes = [.fullName, .email]
        } onCompletion: { result in
            handleSignIn(result)
        }
        .signInWithAppleButtonStyle(colorScheme == .dark ? .white : .black)
        .frame(height: 50)
        .padding()
    }

    private func handleSignIn(_ result: Result<ASAuthorization, Error>) {
        switch result {
        case .success(let authorization):
            if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
                let userID = appleIDCredential.user
                let email = appleIDCredential.email
                let fullName = appleIDCredential.fullName

                // First sign-in: email and name available
                // Subsequent sign-ins: only userID

                // Store userID securely
                saveUserID(userID)

                // Send identity token to your server
                if let identityToken = appleIDCredential.identityToken,
                   let tokenString = String(data: identityToken, encoding: .utf8) {
                    authenticateWithServer(token: tokenString)
                }
            }

        case .failure(let error):
            print("Sign in failed: \(error)")
        }
    }
}

Check Credential State

func checkAppleIDCredentialState(userID: String) async {
    let provider = ASAuthorizationAppleIDProvider()

    do {
        let state = try await provider.credentialState(forUserID: userID)

        switch state {
        case .authorized:
            // User is still authorized
            break
        case .revoked:
            // User revoked authorization - sign out
            signOut()
        case .notFound:
            // Credential not found - show sign in
            break
        case .transferred:
            // User transferred to different team
            break
        @unknown default:
            break
        }
    } catch {
        print("Failed to check credential state: \(error)")
    }
}

Listen for Revocation

class AuthManager: ObservableObject {
    init() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleCredentialRevoked),
            name: ASAuthorizationAppleIDProvider.credentialRevokedNotification,
            object: nil
        )
    }

    @objc private func handleCredentialRevoked() {
        signOut()
    }
}

Biometric Authentication

Face ID / Touch ID

import LocalAuthentication

class BiometricAuthManager: ObservableObject {
    @Published var isAuthenticated = false
    @Published var biometricType: BiometricType = .none

    enum BiometricType {
        case none, touchID, faceID, opticID
    }

    init() {
        biometricType = getBiometricType()
    }

    func getBiometricType() -> BiometricType {
        let context = LAContext()
        var error: NSError?

        guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            return .none
        }

        switch context.biometryType {
        case .touchID:
            return .touchID
        case .faceID:
            return .faceID
        case .opticID:
            return .opticID
        case .none:
            return .none
        @unknown default:
            return .none
        }
    }

    func authenticate() async -> Bool {
        let context = LAContext()
        var error: NSError?

        guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            return false
        }

        do {
            let success = try await context.evaluatePolicy(
                .deviceOwnerAuthenticationWithBiometrics,
                localizedReason: "Authenticate to access your account"
            )
            await MainActor.run {
                isAuthenticated = success
            }
            return success
        } catch {
            print("Authentication failed: \(error)")
            return false
        }
    }

    // Fallback to passcode
    func authenticateWithPasscode() async -> Bool {
        let context = LAContext()

        do {
            let success = try await context.evaluatePolicy(
                .deviceOwnerAuthentication,
                localizedReason: "Authenticate to access your account"
            )
            return success
        } catch {
            return false
        }
    }
}

Info.plist

<key>NSFaceIDUsageDescription</key>
<string>We use Face ID to securely authenticate you.</string>

SwiftUI Integration

struct SecureView: View {
    @StateObject private var authManager = BiometricAuthManager()
    @State private var showContent = false

    var body: some View {
        Group {
            if showContent {
                ContentView()
            } else {
                VStack(spacing: 20) {
                    Image(systemName: authManager.biometricType == .faceID ? "faceid" : "touchid")
                        .font(.system(size: 64))

                    Button("Unlock") {
                        Task {
                            showContent = await authManager.authenticate()
                        }
                    }
                    .buttonStyle(.borderedProminent)
                }
            }
        }
        .task {
            showContent = await authManager.authenticate()
        }
    }
}

Keychain Credentials

Password Autofill Setup

// In your text fields
TextField("Email", text: $email)
    .textContentType(.username)
    .keyboardType(.emailAddress)

SecureField("Password", text: $password)
    .textContentType(.password)

Save Credentials to Keychain

import Security

class CredentialManager {
    static let shared = CredentialManager()

    func saveCredentials(username: String, password: String, server: String) throws {
        let passwordData = password.data(using: .utf8)!

        let query: [String: Any] = [
            kSecClass as String: kSecClassInternetPassword,
            kSecAttrAccount as String: username,
            kSecAttrServer as String: server,
            kSecValueData as String: passwordData
        ]

        // Delete existing
        SecItemDelete(query as CFDictionary)

        // Add new
        let status = SecItemAdd(query as CFDictionary, nil)
        guard status == errSecSuccess else {
            throw KeychainError.saveFailed(status)
        }
    }

    func getCredentials(server: String) throws -> (username: String, password: String)? {
        let query: [String: Any] = [
            kSecClass as String: kSecClassInternetPassword,
            kSecAttrServer as String: server,
            kSecReturnAttributes as String: true,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]

        var result: CFTypeRef?
        let status = SecItemCopyMatching(query as CFDictionary, &result)

        guard status == errSecSuccess,
              let item = result as? [String: Any],
              let username = item[kSecAttrAccount as String] as? String,
              let passwordData = item[kSecValueData as String] as? Data,
              let password = String(data: passwordData, encoding: .utf8) else {
            return nil
        }

        return (username, password)
    }

    func deleteCredentials(server: String) throws {
        let query: [String: Any] = [
            kSecClass as String: kSecClassInternetPassword,
            kSecAttrServer as String: server
        ]

        let status = SecItemDelete(query as CFDictionary)
        guard status == errSecSuccess || status == errSecItemNotFound else {
            throw KeychainError.deleteFailed(status)
        }
    }
}

enum KeychainError: Error {
    case saveFailed(OSStatus)
    case deleteFailed(OSStatus)
}

Passkeys (iOS 16+)

Passkey Registration

import AuthenticationServices

class PasskeyManager: NSObject, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {

    func registerPasskey(username: String, challenge: Data, userID: Data) {
        let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: "example.com")

        let request = provider.createCredentialRegistrationRequest(
            challenge: challenge,
            name: username,
            userID: userID
        )

        let controller = ASAuthorizationController(authorizationRequests: [request])
        controller.delegate = self
        controller.presentationContextProvider = self
        controller.performRequests()
    }

    func signInWithPasskey(challenge: Data) {
        let provider = ASAuthorizationPlatformPublicKeyCredentialProvider(relyingPartyIdentifier: "example.com")

        let request = provider.createCredentialAssertionRequest(challenge: challenge)

        let controller = ASAuthorizationController(authorizationRequests: [request])
        controller.delegate = self
        controller.presentationContextProvider = self
        controller.performRequests()
    }

    // MARK: - Delegate

    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        if let credential = authorization.credential as? ASAuthorizationPlatformPublicKeyCredentialRegistration {
            // Registration successful
            let credentialID = credential.credentialID
            let attestationObject = credential.rawAttestationObject
            // Send to server
        } else if let credential = authorization.credential as? ASAuthorizationPlatformPublicKeyCredentialAssertion {
            // Sign in successful
            let signature = credential.signature
            let authenticatorData = credential.rawAuthenticatorData
            // Verify on server
        }
    }

    func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
        print("Authorization failed: \(error)")
    }

    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        // Return your app's window
        UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
            .first { $0.isKeyWindow }!
    }
}

OAuth / Social Login

Generic OAuth Flow

import AuthenticationServices

class OAuthManager: NSObject, ASWebAuthenticationPresentationContextProviding {

    func signIn(provider: OAuthProvider) async throws -> String {
        let authURL = provider.authorizationURL
        let callbackScheme = "myapp"

        return try await withCheckedThrowingContinuation { continuation in
            let session = ASWebAuthenticationSession(
                url: authURL,
                callbackURLScheme: callbackScheme
            ) { callbackURL, error in
                if let error = error {
                    continuation.resume(throwing: error)
                    return
                }

                guard let url = callbackURL,
                      let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
                      let code = components.queryItems?.first(where: { $0.name == "code" })?.value else {
                    continuation.resume(throwing: OAuthError.noAuthCode)
                    return
                }

                continuation.resume(returning: code)
            }

            session.presentationContextProvider = self
            session.prefersEphemeralWebBrowserSession = true
            session.start()
        }
    }

    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
            .first { $0.isKeyWindow }!
    }
}

enum OAuthError: Error {
    case noAuthCode
}

Apple Documentation