How to Run the iOS Code Sample

Background

Previously we provided an Overview of our iOS App’s Behaviour and next we will describe how to run and test the code sample. For details on getting started with AppAuth libraries, and tracing OAuth requests, see these earlier pages:

Prerequisite 1: Install Xcode

Use an up to date version of Xcode on your macOS system, so that you have access to the latest Swift and Swift UI features.

Prerequisite 2: Install SwiftLint

This is used to make code quality checks and runs as a build step, so it is needed to compile our code sample. Download and run the PKG file for the latest version from the SwiftLint Releases Page.

Step 1: Download the Code

The project is available here, and can be downloaded / cloned to your local PC with the following command:

  • git clone https://github.com/gary-archer/oauth.mobilesample.ios

Step 2: Open the Project in Xcode

In Xcode the project layout looks as follows, and our Swift code is under the Source folder. I used techniques from this link to control the folder layout.

You should be able to run the app on an emulator without any special Signing & Capabilities requirements, as shown here:

Step 3: Understand Dependencies

We are using the following external dependencies:

Library Usage
AppAuth Does the OAuth based authentication work for the app
Swift Keychain Wrapper Simplifies secure storage of OAuth tokens on devices

These references were added via the Swift Package Manager and are built as libraries:

I then linked with the built libraries from the following page:

Step 4: Run the App on a Simulator

You can now run our demo app via the Run icon in the Xcode toolbar. You may then get an initial prompt to Secure your Device, after which you will be able to login with the following test credential:

  • User: guestuser@mycompany.com
  • Password: GuestPassword1

Next save the password when prompted, then navigate between fragments by clicking an item in the Companies view or using the Home button:

Step 5: Understand Configuration Settings

When our app runs it uses the API and OAuth settings from an embedded JSON configuration file:

{
  "app": {
    "apiBaseUrl":             "https://api.authsamples.com/investments"
  },
  "oauth": {
    "authority":              "https://cognito-idp.eu-west-2.amazonaws.com/eu-west-2_CuhLeqiE9",
    "userInfoEndpoint":       "https://login.authsamples.com/oauth2/userInfo",
    "clientId":               "2vshs4gidsbpnjmsprhh607ege",
    "webBaseUrl":             "https://authsamples.com",
    "loginRedirectPath":      "/apps/basicmobileapp/postlogin.html",
    "postLogoutRedirectPath": "/apps/basicmobileapp/postlogout.html",
    "deepLinkBaseUrl":        "https://mobile.authsamples.com",
    "loginActivatePath":      "/basicmobileapp/oauth/callback",
    "postLogoutActivatePath": "/basicmobileapp/oauth/logoutcallback",
    "scope":                  "openid profile email https://api.authsamples.com/investments",
    "customLogoutEndpoint":   "https://login.authsamples.com/logout"
  }
}

Step 6: Configure HTTPS Debugging

We will look at some OAuth messages in the following sections. To view these messages yourself you will need a working HTTPS Debugging Setup, or you can just view the below screenshots if you prefer.

Step 7: Understand Login Redirects

By using AppAuth libraries the standard Authorization Code Flow (PKCE) message is sent.

Our AWS Cognito Authorization Server accepts the request and issues an authorization code because the Client ID, Redirect URI and Scopes of the request match those configured in a Cognito OAuth Client:

Step 8: Understand Redirect Response Handling

The result of successful authorization is the following message, and note that this is sent to a web domain rather than directly to our iOS app:

Two interstitial web pages are used with our iOS sample, hosted at the following URLs:

If we do a View Source for one of the above URLs from a desktop browser, we can see that they just forward query parameters from the login response using a deep linking URL:

For this to work we need to register the https://mobile.authsamples.com base URL as an Associated Domain in the basicmobileapp.entitlements file:

Note that if the interstitial page is left for a couple of minutes before the user clicks Return to the App, the authorization code could time out, leading to a user error. The user can always retry and recover though.

Step 9: Understand Login Completion

Once the authorization code is received by the app, a background Authorization Code Grant message is sent to Cognito’s token endpoint, which return OAuth tokens in the response:

The token data is then saved to the iOS keychain, which ensures that the data is kept private to this particular mobile app.

Step 10: Test Reactivating the App During Login

It is worth performing certain tests while the ASWebAuthenticationSession window is active, to ensure that the app does not throw exceptions or recreate views unnecessarily:

The first of these is to switch away from the app and then reactivate it from its shortcut. Verify that this does not cause any application problems:

Step 11: Test Changing Orientation During Login

Similarly I would recommend changing the screen orientation half way through login and then completing the sign in.

Step 12: Test Restarting the App after Login

Restarting the app after a login will just load OAuth tokens from secure storage, and a new login will not be required. We will look at the coding details behind secure iOS Token Storage shortly.

Step 13: Test Deep Linking

While the app is running we can test deep linking on a simulator via a command such as the following. If required our app performs a login or token renewal before moving to the deep link destination:

xcrun simctl openurl booted https://mobile.authsamples.com/basicmobileapp/deeplink/company/2

Step 14: Test Access Token Expiry

We can use the Expire Access Token and Reload Data buttons to cause an invalid token to be sent to the API, resulting in a 401 response:

After every API call the UI checks for 401 responses, and handles them by getting a new access token. The API request is then retried once with the new token, so that the user session is silently extended. Note that a mobile app is a public client and the refresh token is not protected with a client credential:

Step 15: Test Refresh Token Expiry

We can use Expire Refresh Token followed by Reload Data to simulate the end of a user session, which might occur if a user left the app running overnight:

On the next request for data the attempt to renew the access token will fail, and the result of the refresh token grant message will be an Invalid Grant response:

This will trigger a login redirect, and the user may be prompted to sign in again, but will experience no errors.

Step 16: Test Logout

AppAuth libraries for iOS have built in support for End Session processing. AWS Cognito logouts require a vendor specific format though. Cognito requires the client_id and logout_url parameters, and the others are added by AppAuth libraries. The session cookie is successfully removed though, and we have a working logout solution.

When logout completes we are returned to the below post logout view within our app. In a real world app you could then test logging in as another user with different settings or permissions.

Step 17: Test Failure Scenarios

Our mobile app runs multiple fragments which could fail concurrently, so we implement the same Error HyperLink Behaviour as for our earlier recent React SPA. The following examples cause errors that the UI must handle:

Scenario Instructions
UI Error Load data normally, then disable the network and click reload, to cause a connectivity exception
API Error Long press the Reload button, which then sends a custom HTTP header to the API to rehearse an API 500 exception

Our error display looks as follows after concurrent view failures. The user can click a hyperlink to see details, or press the Home button to retry.

The summary view uses an iOS Modal Dialog / Sheet to display a view with error details, which would help to enable fast problem resolution:

Where Are We?

We have shown how to run this blog’s iOS code sample,  and test its technical behaviour. Next we will drill into the infrastructure needed to enable the use of OAuth claimed HTTPS schemes.

Next Steps