/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.

Note

Curity Identity Server is used in this example, but other OAuth servers can also be used.

Overview

The code sample consists of an initial Unauthenticated View used to trigger the sign-in, but before a login attempt is allowed, the app must cryptographically prove its identity using Client Attestation:

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:

Login Form

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.

Deploying the Curity Identity Server with a Remote URL

It will sometimes be necessary to access the instance of the Curity Identity Server remotely. E.g., when you want to test the mobile app from a physical device, that is not connected to the same network where you run the server. You can use the ngrok tool to quickly expose local applications on the Internet. You can 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. Should you use this option you will have to update the apps' configuration (see Updating the App Configuration).

If you want more control over exposing the Curity Identity Server with ngrok, then have a look at the Exposing with ngrok tutorial.

Configuration

The code example uses the following client settings, including the haapi and code capabilities. For best reliability, configure use-legacy-dpop=false. This prevents potential clock skew problems between devices and the Curity Identity Server. Further details on HAAPI specific settings are explained in the iOS HAAPI SDK tutorial.

xml
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
<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>
<user-authentication>
<allowed-authenticators>Username-Password</allowed-authenticators>
</user-authentication>
<capabilities>
<code>
</code>
<haapi>
<use-legacy-dpop>false</use-legacy-dpop>
</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

In XCode, the demo app's configuration settings are specified in the Demo/Settings/Profile module. If using the quick start, you can just use the default values.

If you deployed the Curity Identity Server with ngrok, or have your own configuration, then you need to provide the details to the app configuration.

This can be achieved in two ways.

  1. Update the relevant fields in the Constants enum to return proper defaults:
swift
1234567891011
private enum Constants {
static let defaultName = "Default"
static let scopes = ["openid", "profile"]
static let defaultAuthorizationEndpointURI = "https://idsvr.example.com/authorize"
static let defaultTokenEndpointURI = "https://idsvr.example.com/token"
static let defaultUserinfoEndpointURI = "https://idsvr.example.com/userinfo"
static let defaultClientId = "haapi-ios-dev-client"
static let defaultBaseURLString = "https://idsvr.example.com"
static let defaultMetaBaseURLString = "https://idsvr.example.com/issuer"
}
  1. In the app itself, you can use the Profile editor to change current settings and create profiles to quickly jump between different environments. To open the settings editor, click on the settings icon while on the app's home page. You can edit an existing profile, or create a new one, then set it as active. As a convenience, you can provide the issuer URI and tap "Fetch the latest configuration". The app will query the server's metadata endpoint to fetch relevant configuration.

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. In the following sections, we will explain the key technical behavior implemented in the code example.

HAAPI Integration

Integrating the Hypermedia Authentication API is done in three layers summarized below. Curity will be providing libraries for the view layer in the near future so that integration requires very little code:

LayerImplemented ByDescription
DriverCurity LibraryProcessing of low-level messages used for client attestation to prove the app's identity.
ControllerCurity LibraryProcessing HAAPI responses, including form submitting, following redirects, and polling the server.
ViewYour AppOverriding the defaults and customizing presentation to take full control over the login user experience.

Client Attestation Overview

When authentication is first triggered, the app sends a number of low-level messages to prove it is the genuine app, based on the digital identity with which the app is signed and delivered to the App Store.

This must be done before the app is allowed to attempt to authenticate the user. The main request to prove the client's identity is a client assertion request with the following parameters:

Form ParameterExample Value
client_idhaapi-ios-dev-client
scopeurn:se:curity:scopes:haapi
grant_typeclient_credentials
client_assertion_typeurn:se:curity:attestation:client
client_assertioneyJraWQi...paTubjbwReZ0A

The server verifies these details and returns a JWT access token in a JSON response that must be sent with subsequent requests in the authentication workflow, along with a separate proof of possession token. See the HAAPI Whitepaper for further details on security.

Response FieldExample Value
access_tokeneyJraWQi...JUHpZbxbstkK41RJhA7vw
scopeurn:se:curity:scopes:haapi
token_typeDPoP

HAAPI Messages

Once the client is attested it can start authenticating the user, which is done using standard OpenID Connect parameters, but with a custom accept header to indicate that the client wants to receive hypermedia responses. The HAAPI models iOS SDK exposes a HaapiManager class which is used to communicate with the Curity Identity Server. The manager exposes these relevant methods:

  • start
  • submit
  • followLink

Each returns a HaapiResult object, which encloses a HaapiRepresentation, a ProblemRepresentation, or an Error. HaapiRepresentation is a sealed interface that might represent:

  • Forms to be rendered
  • Redirects to be followed
  • Instructions to open the system browser or an external app
  • Polling responses
  • Error Responses

Response models can be processed in a generic way within the mobile app to deal with finer-grained work such as rendering the set of fields within a form model. Have a look at the Views package group of the sample app to check how such rendering could be implemented.

Eventually, the manager will return an OAuthAuthorizationResponseStep, which signals that the authentication has finished, and the app can now obtain access, refresh, and ID tokens from the Curity Identity Server. This can be easily done using the OAuthTokenManager object. The token manager exposes two methods: fetchAccessToken and refreshAccessToken.

HAAPI Extensible Authentication

HAAPI supports many authentication methods in addition to passwords, so you are encouraged to try additional authentication options such as these:

Authentication FlowCharacteristics
Google Sign InWith this type of personal password, HAAPI will open the system browser to sign the user in
CIBA FlowWith this authentication flow, HAAPI polls while a client waits for a remote user to sign in
WebAuthnThis authenticator demonstrates passwordless and user friendly logins

For further detail about the requests and responses used by the Hypermedia Authentication API, see the HAAPI Data Model, which provides full details on schemas for all areas, including authentication, consent, polling, and error responses.

Using and Refreshing Access Tokens

Once the 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 refreshAccessToken method of the token manager is used to get a new token.

Logout

Logout can be implemented in a simple manner, by just removing tokens from the app and redirecting the user back to the Unauthenticated View. Subsequent sign-in behavior can then be controlled via the following OpenID Connect fields. This can also be useful when testing, in order to sign in as multiple users on the same device:

OpenID Connect Request ParameterUsage
promptSet prompt=login to force the user to re-authenticate immediately
max-ageSet max-age=N to specify the maximum elapsed time in seconds before which the user must re-authenticate

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 Identity Server continues to dictate security
  • The authentication workflow can be updated 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