Add Single Sign-On (SSO) to your iOS application with WSO2 Identity Server in easy steps

Dileesha Rajapakse
14 min readSep 25, 2018

--

Have you ever heard of ‘Password Fatigue’? You may have not but we are pretty sure it is something you all have experienced at some point in your online life. It can be frustrating to remember multiple passwords and more frustrating to reset them every time you forget. See the frustration on Grandad Harold’s face? So how can you avoid all this stress and maintain your inner harmony? Yes, you’ve guessed it! Single Sign-On to the rescue.

Source: Independent.co.uk

Single Sign-On allows users to access multiple services with a single set of credentials. As an application developer, you might already know how much users love to see that ‘Sign in with Facebook’ or ‘Sign in with Google’ button on your sign up page. Enabling SSO will save them a lot of time spent on filling out those boring sign up forms and most importantly, it will make their lives easier. So if you really want to make users happy, continue reading. Still not convinced? Follow the links at the bottom of this page, educate yourself, and get rid of that heartless attitude.

Let’s see how you can add SSO support to your iOS application. Before we begin, let’s go through this quick checklist to make sure you have all the necessary tools and knowledge to do this.

  • You need a Mac or at least, a Hackintosh
  • Xcode 8+installed
  • Fundamental knowledge in iOS application development and some experience in the SWIFT (3+) programming language
  • Fundamental knowledge in OAuth 2.0, OpenIDConnect and PKCE — Proof Key Code Exchange mechanism (optional) concepts
  • To run the WSO2 Identity Server: Java 7+, 2GB RAM minimum and 1GB minimum disk space

All checked? We are good to go. Buckle up! Things are going to get really technical from this point onwards.

We are going to use the WSO2 Identity Server as the identity provider for this particular tutorial.

Why WSO2 Identity Server?

The WSO2 Identity Server is a key part of the WSO2’s advance integration agile platform. It is a uniquely flexible and robust Identity and Access Management product which is optimized for identity federation and Single Sign-On with extensive support for adaptive and secure authentication. It is highly scalable and most importantly, it is open source! So the opportunities are limitless.

Source: wso2.com

In this tutorial, we are going to let users sign in into a sample iOS application with WSO2 Identity Server credentials.

1. Install and configure the WSO2 Identity Server

Download the latest version of the WSO2 Identity Server from the official website. Refer to the installation guide to set up and run the server in your machine. There are few key points you should be concerned about when setting up the WSO2 IS.

  • Make sure you have installed a valid SSL certificate in the server. This step is important since iOS applications by default are restricted from communicating with sources without valid certificates. However, if you are running the server locally and planning to test the application on the iOS emulator, you can use a self signed certificate and install it on both the server and the emulator. To do this, follow these steps.
  1. Install the self-signed certificate on the server as instructed here.
  2. Import the self-signed certificate to the iOS emulator using this tool.
  3. If you are running the WSO2 IS locally, you can add a local DNS entry resolving to a custom domain name and generate self-signed certificates to that particular domain. If you’re doing this, make sure to change the host in the WSO2 IS accordingly. To add a local DNS entry to your hosts file, simply follow this link.
  • Log in to the server dashboard and create a ‘Service Provider’ as instructed here.
  • Create an ‘OAuth Inbound Authenticator’ as instructed here.
  • When creating the OAuth inbound provisioning application, make sure to enable the ‘Allow authentication without client credentials’ option as shown in the image below. This option will allow public clients such as native mobile applications bypass the authentication phase. The reason for doing so is explained here and also the OAuth 2.0 for Native Apps specification provides a detailed description.
  • Mark the ‘PKCE Mandatory’ option as checked. Making PKCE mandatory is solely up to the user. However, as the best practice, we encourage to stick to the PKCE protocol when developing native mobile applications.
  • Give ‘wso2issample://oauth’ as the Callback Url. This is the sample application’s URI and will be explained later in this document.
  • Click ‘Add’ and copy the generated Client ID. We will be using it in the application.

We are done setting up and configuring the WSO2 Identity Server, let’s move to the next step.

2. Setting up the sample iOS application

Do we have to create an iOS application from scratch? Absolutely not! The thoughtful developers at WSO2 has developed a sample iOS application which you can use as boilerplate code. You can clone or download the sample application repository for WSO2 Identity Server from here.

Before opening the application in Xcode, you need to install the popular Swift and Objective-C project dependency management system, CocoaPods on your Mac. Once done, go to the following directory and double click the ‘WSO2-IS-SampleApp.xcworkspace’ workspace file and the project will open in Xcode. Make sure NOT to open the WSO2-IS-SampleApp.xcodeproj project file instead as it will not resolve the CocoaPods dependencies properly.

<repository_home>/oidc-client-app-samples/ios-client-app-sample

Sample Application Structure

This is the file structure of the sample iOS application provided by the WSO2 Identity Server.

File structure of the sample iOS application

Let’s have a look at the key elements of this particular application.

  • AppDelegate.swift — The default delegation class which observes for changes of the application state
  • Main.storyboard — The main storyboard of the application
  • Config.plist — This configuration file contains Identity Provider details along with the OAuth client ID
  • Controllers directory — View Controllers for the Login view and profile view
  • Model directory — Model data objects
  • Management (mgt) directory — Management classes
  • Utility (utils) directory — Utility classes
  • Resources directory — Resource files utilized in the application
  • Service directory — This directory contains the service class which handles the OAuth communication. This will be further discussed in later sections

Dependencies and Third-Party Libraries

As mentioned before, we are using CocoaPods as the dependency manager for this project. Dependencies are defined in the ‘Pods’ file and you can simply install them by navigating to the project root folder and running the following command. However, you do not need to do this step as they have already included the dependencies along with the source code.

pod install

In this application, we are using AppAuth for iOS as a third party library for OAuth 2.0 communication between the application and the WSO2 Identity Server. The AppAuth library has been added as a dependency in the pod file and note that we are using a development branch of the AppAuth for iOS library (dev-logout branch) due to several issues in the master branch related to the logout functionality. However, you do not need to worry about this much as the libraries are already included in this project. You will find the following line in the Pods file.

pod 'AppAuth', :git => 'https://github.com/openid/AppAuth-iOS.git', :branch => 'dev-logout'

The Cocoa framework will download the source code from the specified ‘dev-logout’ branch and add it to the project.

However, if you do not need to log out from the identity server and prefer to log the user out from the application and invalidate the session, you can simply use the regular AppAuth for iOS library by replacing this line with the following and remove the logging out related implementation from the code.

pod 'AppAuth'

Configuring Application Settings

In the root folder of the project, you will find the ‘Info.plist’ file which contains all the configuration details about the application. Right click on it and click Open As Source Code. Note the following configuration.

The ‘CFBundleURLTypes’ property is used to define the URL scheme of the application. The URL scheme is the mechanism iOS applications communicate with each other. In this application, we are using a custom URL scheme called ‘wso2issample’ and now you may understand why we used ‘wso2issample://oauth’ as the callback URL when creating the OAuth service provider in the WSO2 Identity Server. Once the authentication is done, users will be redirected to the application through this URL. You can register any URL scheme you prefer by simply defining it as above.

If you are running the WSO2 Identity Server locally, you must have the following configuration to successfully test the application. Note the following property in the same file.

Here we have enabled the application to allow connecting to sources with untrusted certificates. However, doing this is not recommended in a production environment so make sure you remove this property before publishing your application for review in the app store.

Configuring Endpoints and OAuth Client Settings

The config.plist file contains the configuration information of the endpoints of the identity server. Open the file as source code and you will find several OAuth related properties defined inside this file.

The first few properties are the endpoints of the WSO2 Identity Server OAuth 2.0 API and you must replace the hostname (wso2is.local) with your configured hostname. Make sure you use the same URL scheme created earlier in the RedirectURL property here. Also, you need to put the generated client ID inside the ‘ClientID’ property. We do not need a client secret as we are using the PKCE based authentication feature provided by the WSO2 Identity Server.

Once you are done with all these steps, you can run the application in the emulator or in an actual iOS device.

Application Flow

Let’s have a look at the application flow. The application has two main user interfaces: the login screen and the profile screen.

Once you click the ‘LOGIN’ button, you will be redirected to the sign in page of the WSO2 Identity Server. Once you enter your credentials and click ‘Sign in’ you will be requested to choose which personal data to be shared with the application. Once you give consent, you will be redirected to the application’s profile page. This is a sample profile page which shows the username and a ‘Sign out’ button. Once the user clicks the ‘Sign out’ button, all user data will be cleared from the application memory and the user will be logged out locally. Then the application will redirect the user to the logout page of the WSO2 Identity Server where user can logout from the server as well.

Deep Dive into Source Code

Let’s get into the implementation details of the application. This will help you to understand the internals and customize the application in any way you want.

We have used AppAuth for three main operations in this application; to log the user in, to retrieve user information and ultimately to log the user out. However, for simplicity and ease of development, we have abstracted these three operations into a single service class called ‘WSO2OIDCAuthService’. It wraps the implementation logic for aforementioned operations and provides a simplified API. Let’s have a look.

Login Functionality

Let’s have a look at the following function.

The parameters are as follows,

  • configFilePath — The relative path of the configuration file
  • callingViewController — The view controller which calls this function
  • completion — The completion callback function

The completion callback function of this method will return a UserInfo object which contains all the user information.

This function initially authenticates the user by redirecting to the login page of the OIDC provider, in this case, the WSO2 Identity Server. Once the authentication is successful, it will send a request to the user information endpoint of the identity server to retrieve basic user information. Once the retrieval is successful, it will return a UserInfo object which can be utilized anywhere in the application.

The retrieval of user information is done through a private function defined in the same class. This method is called by the logInUser() method.

The user information endpoint, the current authorization state of the application and a completion callback function is passed as parameters. The callback function will return a ‘UserInfo’ object which contains all the user information retrieved from the server. Let’s talk about ‘OIDAuthState’ later in this section.

Logout Functionality

The parameters are as follows,

  • configFilePath — The relative path of the configuration file
  • callingViewController — The view controller which calls this function
  • targetViewControllerId — The target view controller after logging out

This function will take the calling view controller or the view controller which has the logout button and the target view controller which the user will be redirected to after logging out as parameters. It will log the user out from the application locally and then will send a logout request to the logout endpoint of the server. The user will then be redirected to the logout pages of the server.

Utilization of AppAuth

Both logInUser() and logOutUser() functions utilize some of the key functionalities of the AppAuth library. In AppAuth, OIDC configuration details are represented as an ‘OIDServiceConfiguration’ object. Look at the following line inside the logInUser() method.

We are setting the authorization and token endpoints to the configuration object. The next step would be to generate the authorization request.

In addition to passing the configuration object as a parameter, we are setting the necessary request components such as the client ID, scopes, redirect URL, response type and an array of additional parameters.

The following code segment will initiate and start the authentication flow. The constructed authorization request, the calling view controller (in this case, the login view controller) and a callback function to handle success and error states are passed as parameters.

This method will return an instance of the ‘OIDExternalUserAgentSession’ class. It contains all the necessary session data which could be utilized to manage sessions throughout the application flow.

Refreshing Tokens

Before each request which requires tokens retrieved from the server, it is necessary to check the validity beforehand to make sure the tokens are not expired. If expired, we should retrieve new tokens and replace the expired tokens. The following code segment can be used to do this using AppAuth.

The ‘authState’ in this particular code segment is an instance of the ‘OIDAuthState’ class which contains information about the latest authorization request, authorization response, token request, token response and several other useful information regarding to the current authorization state of the application.

A callback function including the access token, ID token and an error instance is passed as parameters and if expired, tokens will be refreshed and returned.

Logout Functionality

As mentioned earlier in this tutorial, the logout functionality is not yet available up to this date in the latest release of the AppAuth for iOS library. However, there is a development branch (dev-logout) which is dedicated for developing the logout functionality. Note that up to this date, there is no proper implementation to achieve a complete logout from the identity provider but however, in this application, we have implemented a basic logout flow by minimizing the issues which are not resolved yet in the AppAuth for iOS library.

The following code segments can be found inside the logOutUser() function.

Initially we need to make a configuration instance which contains the logout endpoint of the identity server.

Once the configuration object is created, the logout request can be constructed as follows.

The ‘OIDEndSessionRequest’ can only be found in the aforementioned development branch up to this date. In this code segment we are passing the the configuration object, current ID token as the ID token hint, the redirect URL, the current authorization state as mentioned earlier in the section and finally an array of additional parameters.

To execute the logout flow in an external browser similar to the login flow, and external user agent must be created. This can be done using the following code segment.

The ‘OIDExternalUserAgentIOS’ will return the native external browser view according to the iOS version of the host device. The calling view controller is passed as a parameter. For the latest iOS devices up to this date (iOS 11.0–12.0), this will return an ‘SFAuthenticationSession’ instance.

The logout request can be triggered using the following code segment.

The ‘OIDAuthorizationService’ in this particular instance will execute the logout flow. The constructed logout request, external user agent and the callback for success and error responses are passed as parameters.

Some important stuff you need to know

If you did not logout from the WSO2 Identity Server and the application is not cleared from memory, you will still be able to login without credentials as the cookies set by the server are not cleared from the application side. Also, after logging in, if you close the application, clear it from memory and try to logout, the cookie data will not be available to perform the external logout from the server’s side. This happens since there is currently no way to persist cookie data due to the same aforementioned issue.

Currently, these are issues in the AppAuth for iOS library which are not resolved yet and there is no workaround for these at the moment as Apple does not allow to access the cookie storage of the Safari View Controllers. You can track this issue from the links below.

Time to wrap up. I know this article is too long and I’m too lazy to break this into two posts. If you have any comments or suggestions, please let me know. Also, if you have any questions about anything mentioned here, please feel free to contact me via Twitter or Facebook.

--

--

Dileesha Rajapakse

Developer | Tech Enthusiast | Art | Music | Philosophy | If you like my content, you can buy me a coffee ☕ https://ko-fi.com/dilee