Swiftpack.co - Package - sejr/swift-authentication

Swift Authentication

Swift

This Swift package provides a simple protocol-based interface for defining authentication mechanisms for a service.

At its foundation is the AuthenticationService protocol, copied here for convenience:

/// Functionality that enables a service to _authenticate_ a user.
///
/// Authentication is defined as verifying the identity of the user. This specifically does not include
/// functionality for determining which actions the user has _authorization_ for.
public protocol AuthenticationService {
    /// Information that can be used to verify the identity of a user.
    ///
    /// This information will generally have some security implications; you should ensure it is transmitted
    /// and stored (if necessary) in a secure fashion, using encryption and/or hashing. Those mechanisms are
    /// outside the scope of the Swift Authentication package.
    associatedtype Credential
    
    /// The result of a successful authentication request.
    associatedtype AuthenticationSuccess
    
    /// The result of a failed authentication request.
    associatedtype AuthenticationFailure: Error
    
    /// Authenticates a user based on some provided `Credential`.
    func authenticate(
        with credential: Credential,
        completion: @escaping (Result<AuthenticationSuccess, AuthenticationFailure>) -> Void
    )
}

Beyond this protocol, you are largely on your own. The goal of this project is to provide some common credential types, such as the UsernamePasswordCredential for basic username and password authentication.

Getting Started

Installation

Swift Package Manager

Update your Package.swift file like so:

let package = Package(
    // name, platforms, products, etc.
    dependencies: [
        .package(url: "https://github.com/sejr/swift-authentication", from: "0.1.0"),
        // other dependencies
    ],
    targets: [
        .target(name: "<target>", dependencies: [
            "Authentication",
            // other dependencies
        ]),
        // other targets
    ]
)

Defining an authentication service

import Authentication

/// An example service.
///
/// Notice that this definition has nothing pertaining to authentication; we encapsulate all of that
/// information inside an extension.
class ExampleAuthenticationService {
    /// General notion of user identity within this example service.
    struct User {
        /// The unique ID for the user.
        var id: String
    }
    
    /// General error for this example service.
    enum ServiceError: Error {
        /// Invalid username and password combination.
        case invalidCredential
        
        /// Something else went wrong.
        case unknown
    }
}

extension ExampleAuthenticationService: AuthenticationService {
    typealias Credential = UsernamePasswordCredential
    typealias AuthenticationSuccess = User
    typealias AuthenticationFailure = ServiceError
    
    public struct ServiceCredential: UsernamePasswordCredential {
        var username: String
        var password: String
    }
    
    func authenticate(with credential: UsernamePasswordCredential, completion: @escaping (Result<ExampleAuthenticationService.User, ExampleAuthenticationService.ServiceError>) -> Void) {
        let validUsername = credential.username == "testing"
        let validPassword = credential.password == "testing"
        
        if (validUsername && validPassword) {
            completion(.success(ExampleAuthenticationService.User(id: credential.username)))
        } else {
            completion(.failure(.invalidCredential))
        }
    }
}

Building a credential

This step is largely dependent on how you have defined your credential. Because our ExampleAuthenticationService has defined a ServiceCredential type that conforms to UsernamePasswordCredential, it can be used like so:

let credential = ExampleAuthenticationService.ServiceCredential(
    username: "testing",
    password: "testing"
)

Authenticating the credential against the service

The AuthenticationService provides a single function, authenticate, which takes the credential type you've defined, and returns the result you've defined. It's pretty straight-forward, right?

service.authenticate(with: credential) { (result) in
    // Check result of authentication attempt
    switch result {
    case .success(let user):
        print(user.id)
    case .failure(let err):
        print(err)
    }
}

Github

link
Stars: 1

Dependencies

Used By

Total: 0

Releases

v0.2.0 AuthenticationService Update -

Updated AuthenticationService protocol

The protocol function AuthenticationService.authorize(with:) now takes two arguments:

  1. The credential, as before
  2. A completion to be executed when the authentication attempt has completed

v0.1.0 Initial Release -

This is the initial release of Swift Authentication. It is under active-ish development and is highly unlikely to be production ready.

Features

  • Define safe and consistent authentication mechanisms with the AuthenticationService protocol.
  • Support based username and password authentication with the UsernamePasswordCredential protocol.