Implementation of Authentication package.
To use Authentication in a SwiftPM project:
- Add the following line to the dependencies in your
Package.swift
file:
.package(url: "https://github.com/govuk-one-login/mobile-ios-authentication", branch: "main"),
- Add
Login
as a dependency for your target:
.target(name: "MyTarget", dependencies: [
.product(name: "Authentication", package: "di-mobile-ios-authentication")
]),
- Add
import Authentication
andimport UserDetails
in your source code.
The Authentication package authenticates a users details and enables them to log into their account securely. This is done by providing them with a login session and token.
The package also integrates openID AppAuth and conforms to its standards, and also uses NetworkClient
from the Networking package.
A struct which conforms to Codable
. It requires the following to be implemented:
public struct UserInfo: Codable {
let sub: String
let phoneNumberVerified: Bool
let phoneNumber: String?
let emailVerified: Bool
public let email: String
}
UserService
implements the UserServicing
protocol in order to make a request with an authentication token. The request will fetch the UserInfo
object.
Handles creating the config
found in LoginSession
. It requires the following to be initialised:
let authorizationEndpoint: URL
let tokenEndpoint: URL
let responseType: ResponseType
let scopes: [Scope]
let clientID: String
let prefersEphemeralWebSession: Bool
let redirectURI: String
let vectorsOfTrust: String
let locale: UILocale
The struct also contains three enums to handle the language, the response and the scopes required for sending the OIDAuthorizationRequest
within the AppAuthSession.
A class to handle the login flow with the given auth provider and conforms to the LoginSession
protocol. It uses the UIWindow
to know where to display the login modal.
performLoginFlow
takes configuration, which comes from LoginSessionConfiguration
, as a parameter and contains the login information to make the request. Any errors received in the authorization flow through the openID package are thrown. Otherwise, the token response is returned.
finalise
takes a URL to redirect to as a parameter and returns a token response. If userAgent
was not assigned inside the present
method an error is thrown.
To use the Authentication package, first, make sure your module or app has a dependency on Authentication and UserDetails and import both into the relevant file(s). Next, initialise an instance of AppAuthSession (which conforms to LoginSession) and LoginSessionConfiguration, then call present
on your session, with the configuration as a parameter.
import Authentication
import UserDetails
...
let session: LoginSession
init(session: LoginSession) {
self.session = session
}
...
let configuration = LoginSessionConfiguration(authorizationEndpoint: url,
responseType: .code,
scopes: [.openid, .email, .phone, .offline_access],
clientID: "someClientID",
prefersEphemeralWebSession: true,
redirectURI: "someRedirectURI",
vectorsOfTrust: "someVectorOfTrust",
locale: .en)
session.performLoginFlow(configuration: configuration)
Your code should include a redirect URL handler method in either the SceneDelegate or AppDelegate. Therefore, once performLoginFlow
has been called, the user logs in and selects a redirect link on the login modal. The device will redirect into the app and the url is passed into the call to finalise
.
// SceneDelegate.swift
...
if let webURL = userActivity.webpageURL {
viewController?.handleCallback(redirectURL: webURL)
}
Note: Depending on how your app has been configured, the redirect URL handler should be placed within the corresponding delegate method in each file. For example, the impletentation for SceneDelegate, AppDelegate and SwiftUI is as follows:
//SceneDelegate.swift
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
//handle redirect URL
}
//AppDelegate.swift
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
// handle redirect URL
}
//SwiftUI
struct PlaygroundApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
// handle redirect URL
}
}
}
}
To get a token, call the finalise method on the session. The token can then be used to get an authenticatedClient, which in turn is used to create an instance of UserService.
fetchUserInfo
can then be called on the UserService object to receive the required data.
do {
let tokens = try await session.finalise(redirectURL: url)
let authenticatedClient = NetworkClient(authenticationProvider: tokens)
let service = UserService(client: authenticatedClient)
let userInfo = try await service.fetchUserInfo()
} catch {
// handle errors
}