/images/resources/code-examples/code-examples-swift-haapi.jpg

Swift iOS App using HAAPI

On this page

This tutorial shows how to run a code example that implements mobile OpenID Connect in a Swift App using the Hypermedia Authentication API (HAAPI). See also the API Driven Demo Client tutorial for an introductory guide that runs a simple web client.

Overview

The code sample provides a basic iOS implementation to demonstrate how to integrate a HAAPI-driven authentication flow in an app. It uses the HAAPI UI SDK, a high-level SDK that handles the whole flow, requiring just minimal configuration and customization. The app initially presents a view to start the authentication process:

Unauthenticated View

The authentication workflow is then managed as a series of fast API requests that return JSON responses. The mobile app processes each API response to perform various actions including the rendering of forms:

Authentication Selector

Once authentication completes, the app presents its Authenticated View, to simulate a mobile app that works with access tokens to call APIs. The app can then easily perform other OAuth operations such as refreshing tokens and ending the authenticated session:

Authenticated View

Get the Code

First, ensure that you are running an up-to-date version of Xcode, then clone the GitHub repository and open its folder to view the Swift classes used.

Quick Start

The easiest way to run the code example is to point it to a deployed and preconfigured instance of the Curity Identity Server, running in Docker. This is done via a script included with the example that is explained in the Mobile Setup how-to:

bash
1
./start-idsvr.sh

The result is a working instance of the Curity Identity Server, listening on localhost: https://localhost:8443, ready for the mobile app to connect to. The Admin UI is also available and can be accessed using the following details:

PropertyValue
URLhttps://localhost:6749/admin
Useradmin
PasswordPassword1

The deployed system also includes a user account of demouser / Password1 that can be used to sign in.

Configuration

The code example uses the following client settings, including the haapi and code capabilities. For further details on HAAPI specific settings refer to the iOS HAAPI Client Setup tutorial.

xml
123456789101112131415161718192021222324252627282930313233343536373839404142
<config xmlns="http://tail-f.com/ns/config/1.0">
<profiles xmlns="https://curity.se/ns/conf/base">
<profile>
<id>token-service</id>
<type xmlns:as="https://curity.se/ns/conf/profile/oauth">as:oauth-service</type>
<settings>
<authorization-server xmlns="https://curity.se/ns/conf/profile/oauth">
<client-store>
<config-backed>
<client>
<id>haapi-ios-dev-client</id>
<client-name>haapi-ios-dev-client</client-name>
<no-authentication>true</no-authentication>
<redirect-uris>haapi:start</redirect-uris>
<proof-key>
<require-proof-key>false</require-proof-key>
</proof-key>
<refresh-token-ttl>3600</refresh-token-ttl>
<scope>openid</scope>
<scope>profile</scope>
<capabilities>
<code>
</code>
<haapi>
</haapi>
</capabilities>
<validate-port-on-loopback-interfaces>true</validate-port-on-loopback-interfaces>
<attestation>
<disable-attestation-validation>true</disable-attestation-validation>
<ios>
<app-id>io.curity.cat.ios.client</app-id>
<ios-policy>ios-haapi-dev</ios-policy>
</ios>
</attestation>
</client>
</config-backed>
</client-store>
</authorization-server>
</settings>
</profile>
</profiles>
</config>

Updating the App Configuration

The demo app's configuration settings are specified in the Configuration module. If running the quick start, use the default values. If you deployed the Curity Identity Server with ngrok, or have your own configuration, then update the settings.

swift
123456789
struct Configuration {
static let clientId = "haapi-ios-dev-client"
static let redirectUri = "haapi:start"
static let scopes = ["openid", "profile"]
static let baseURL = URL(string: "https://localhost:8443")!
static let tokenEndpointURL = URL(string: "/oauth/v2/oauth-token", relativeTo: baseURL)!
static let authorizationEndpointURL = URL(string: "/oauth/v2/oauth-authorize", relativeTo: baseURL)!
static let userInfoEndpointURL = URL(string: "/oauth/v2/oauth-userinfo", relativeTo: baseURL)!
}

Run the App

Once the above configuration is complete you are ready to run the app and use whichever authenticators and users you have configured in the Curity Identity Server. If using the quickstart option, the username-password authenticator is already configured, and the following username and password can be used to log in: demouser / Password1.

Use Many Authentication Methods

The code example also supports a more advanced deployment, that supports accessing the instance of the Curity Identity Server remotely, e.g., when you want to test the mobile app from a physical device. Edit the start-idsvr.sh and stop-idsvr.sh files to use ngrok (set USE_NGROK=true). This will result in exposing the containerized instance of the Curity Identity Server under a random domain similar to https://87ce095409f9.ngrok.io.

The advanced deployment automates a setup to enable passwordless authentication using passkeys. This requires some additional configuration, including the use of associated domains. For further details on the advanced deployment, see the Configure Native Passkeys for Mobile Logins tutorial. Many other authentication methods are also possible, as summarized in the Advanced Authentication Flows page.

Passkeys Login

HAAPI Integration

Integration with the HAAPI UI SDK is described in detail in the iOS integration guide and can be summarized in these steps that the app implements:

  1. In the application's package manager, the SDK is defined as a dependency.
  2. The DemoAppDelegate class creates global objects and creates a configuration object that is then used by the SDK itself.
  3. The UnauthenticatedView implements the HaapiFlowResult protocol, to start the login and receive tokens afterwards.
  4. When authentication is complete, an AuthenticatedView demonstrates how to use tokens, and a real app would use them to call APIs.

OAuth Lifecycle Events

Once a HAAPI mobile client obtains an access token it will use it to call APIs in the standard way, by sending them as bearer tokens in the HTTP Authorization header. When the access token expires, the OAuthLifecycle.refreshToken helper method is used to get a new token. Logout can be implemented in a simple manner, by just removing tokens from the app and redirecting the user back to the unauthenticated view.

Customize the Look and Feel

The UI SDK allows for a simple change of the styles used by the view components. Have a look at the application's asset catalog and the Resources/CustomTheme.plist file, to see the techniques used in the demo app to change the default theme. Note that the custom theme is applied to the HaapiUIKitApplication object in the DemoAppDelegate class. Have a look at the customization tutorial to learn more about changing the look and feel of an authentication flow.

Model SDK Code Example

HAAPI SDKs

This section is only relevant if you are using the older model SDK and want to run the model SDK code example. All new HAAPI secured mobile apps should instead integrate the UI SDK.

The UI SDK is implemented using the lower level iOS Model SDK, which uses raw API responses. The iOS HAAPI Model SDK Code Example demonstrates an alternative approach, where the model SDK is used directly, but considerably more plumbing code is needed.

To use the code example, execute the ./start-idsvr.sh script and then run the app from Xcode. First locate the OAuth configuration settings in the Classes/Settings/Profiles.swift module and update them if required:

swift
1234567891011
private enum Constants {
static let defaultName = "Default"
static let scopes = ["openid", "profile"]
static let baseUrl = Bundle.main.object(forInfoDictionaryKey: "CURITY_BASE_URL") as? String ?? "localhost:8443"
static let defaultAuthorizationEndpointURI = "\(defaultBaseURLString)/oauth/v2/oauth-authorize"
static let defaultTokenEndpointURI = "\(defaultBaseURLString)/oauth/v2/oauth-token"
static let defaultUserinfoEndpointURI = "\(defaultBaseURLString)/oauth/v2/oauth-userinfo"
static let defaultClientId = "haapi-ios-dev-client"
static let defaultBaseURLString = "https://\(baseUrl)"
static let defaultMetaBaseURLString = "\(defaultBaseURLString)/oauth/v2/oauth-anonymous"
}

Study the code example's main classes to understand the logic. The DemoApplication class creates a HaapiAccessor factory class, which instantiates a HaapiManager. A HaapiController provides the user interface for login screens. A FlowViewModel starts the flow and processes each API response until authentication completes. The start and end of the authentication flow are shown in the following simplified code snippet:

swift
12345678910111213141516
func start() {
haapiManager?.start(completionHandler: {haapiResult in
self.processHaapiResult(haapiResult)
completionHandler()
})
}
private func processHaapiResult(haapiResult: HaapiResult) {
switch haapiResult {
case ...
case let oAuthStep as OAuthAuthorizationResponseStep:
let code = oAuthStep.oauthAuthorizationResponseProperties.code
self.fetchAccessToken(code: code)
}
}

Conclusion

The Hypermedia Authentication API provides a number of benefits to mobile apps, in terms of both financial grade security and login usability:

  • The app gets an improved login user experience.
  • The Curity Identity Server and the HAAPI SDK deal with the security, so the application only requires simple code.
  • The authentication workflow can be changed without needing to redeploy the app.
  • Only genuine mobile clients can attempt user authentication.
  • Man In the Browser (MITB) risks are eliminated for most authentication methods.

Join our Newsletter

Get the latest on identity management, API Security and authentication straight to your inbox.

Start Free Trial

Try the Curity Identity Server for Free. Get up and running in 10 minutes.

Start Free Trial