Skip to content

nikolainobadi/NnCredentialKit

Repository files navigation

NnCredentialKit

Unit Tests Swift Platform License

NnCredentialKit is a comprehensive Swift package designed to handle user authentication workflows, including email/password sign-up, social login (Apple Sign-In, Google Sign-In), and account linking. This package streamlines the process of managing credentials, reauthentication, and account deletion.

Table of Contents

Features

  • Account Link Section: Easily integrate account linking for various providers, with customizable colors and accessibility support.
  • Credential Management: Load and manage credentials for Apple, Google, and email/password logins.
  • Reauthentication Workflow: Handle reauthentication for sensitive actions using custom alerts.
  • Social Sign-In Integration: Seamlessly integrate Apple Sign-In and Google Sign-In flows with reusable components.
  • Error Handling: Robust error handling for common authentication issues.

Installation

Xcode Projects

To integrate NnCredentialKit into your Xcode project using Swift Package Manager:

  1. In Xcode, go to File > Swift Packages > Add Package Dependency.

  2. Enter the following repository URL:

    https://github.com/nikolainobadi/NnCredentialKit
    
  3. Choose the version 3.0.0.

  4. Select the target where you want to add the package.

Swift Package

If you are using NnCredentialKit in another Swift package, add it to your Package.swift dependencies:

.package(url: "https://github.com/nikolainobadi/NnCredentialKit", from: "3.0.0")

Then, in the target you want to use NnCredentialKit, add it to the list of dependencies:

.target(
    name: "YourTargetName",
    dependencies: [
        .product(name: "NnCredentialKit", package: "NnCredentialKit"),
        .product(name: "NnCredentialKitAccessibility", package: "NnCredentialKit") // optional for access to accessibility identifiers
    ]
)

Usage

Import NnCredentialKit in your Swift file and implement the required delegates to start managing authentication flows.

Social Sign-In

GoogleSignInService and AppleSignInService help manage Google and Apple sign-ins.

let appleCredentialInfo = try await AppleSignInService().createAppleTokenInfo()
let googleCredentialInfo = try await GoogleSignInService.signIn(rootVC: viewController)

Alternatively, you can use SocialCredentialManager to handle both operations:

let socialManager = SocialCredentialManager(appleSignInScopes: [.email])
let appleCredentialInfo = try await socialManager.loadAppleCredential()
let googleCredentialInfo = try await socialManager.loadGoogleCredential()

The top-most UIViewController will be used automatically during Google Sign-In. The method to retrieve it is below:

extension UIApplication {
    func getTopViewController() -> UIViewController? {
        var topController = connectedScenes
            .filter { $0.activationState == .foregroundActive }
            .map { $0 as? UIWindowScene }
            .compactMap { $0 }
            .first?
            .windows
            .filter { $0.isKeyWindow }
            .first?
            .rootViewController
        
        // ensures any presented views will be treated as 'top-most' viewController
        while let presentedViewController = topController?.presentedViewController {
            topController = presentedViewController
        }
        
        return topController
    }
}

Account Linking

The AccountLinkSection view provides a way to display and manage linked accounts within your app. It supports Apple, Google, and email/password providers. It requires an AccountLinkSectionColorsConfig for customizing text colors and an AccountLinkDelegate to perform the credential linking.

import NnCredentialKit

struct ContentView: View {
    var body: some View {
        AccountLinkSection(
            config: .init(providerNameColor: .primary, emailColor: .secondary),
            delegate: YourAccountLinkDelegate(),
            appleSignInScopes: [.email, .fullName],
            preventUnlinkingLastProvider: true
        ) { delegate in
            Button(delegate.buttonText) {
                Task {
                    try? await delegate.linkAction()
                }
            }
        }
    }
}

Preventing Unlinking of Last Provider

By default, preventUnlinkingLastProvider is set to true, which prevents users from unlinking their only authentication method. This ensures users always have at least one way to sign in. When enabled, the link/unlink button is automatically hidden when a provider is the only one linked to the account.

Set preventUnlinkingLastProvider: false to allow users to unlink their last provider (not recommended for most use cases).

Firebase Integration Notes

When using Firebase Authentication, it's common for operations like updating sensitive user information (email, password, or account linking) to fail if the user’s sign-in session is considered too old. Firebase throws an error with the .requiresRecentLogin code in these cases.

NnCredentialKit is designed to handle this automatically by providing a secure reauthentication flow whenever a .reauthRequired result occurs. After reauthentication is successfully completed, the originally intended operation is automatically retried without any extra logic needed in your code.

This ensures smooth Firebase integration, especially for:

  • Linking additional sign-in providers
  • Deleting user accounts
  • Updating sensitive credentials

Detecting .requiresRecentLogin Errors

When performing Firebase auth operations, you can detect if reauthentication is needed using the following helper:

func handleAuthOperation(_ operation: @escaping () async throws -> Void) async -> AccountCredentialResult {
    do {
        try await operation()
        return .success
    } catch let nsError as NSError {
        if let authError = AuthErrorCode(rawValue: nsError.code), authError == .requiresRecentLogin {
            return .reauthRequired
        }
        return .failure(nsError)
    }
}

Example: Wrapping Firebase Auth Operations

You can integrate handleAuthOperation inside your AccountLinkDelegate implementation like this:

func linkProvider(with type: CredentialType) async -> AccountCredentialResult {
    return await handleAuthOperation {
        try await delegate.link(to: type)
    }
}

func unlinkProvider(_ type: AuthProviderType) async -> AccountCredentialResult {
    return await handleAuthOperation {
        try await delegate.unlink(from: type)
    }
}

In these examples:

  • delegate.link(to:) and delegate.unlink(from:) represent your Firebase linking/unlinking methods.
  • handleAuthOperation ensures any .requiresRecentLogin Firebase error is automatically escalated to .reauthRequired, allowing NnCredentialKit to seamlessly handle reauthentication.

Dependencies

NnCredentialKit depends on the following external libraries:

Contributing

Any feedback or ideas to enhance NnCredentialKit would be greatly appreciated.
Please feel free to open an issue or submit a pull request if you'd like to help improve this Swift package.

License

NnCredentialKit is available under the MIT license. See the LICENSE file for more information.

About

Swift package for managing account linking, credential handling, and social authentication in iOS apps.

Topics

Resources

License

Stars

Watchers

Forks

Languages