Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Add Authentication to Your iOS Apps With Centralized Login, Part 3

DZone's Guide to

Add Authentication to Your iOS Apps With Centralized Login, Part 3

We wrap up our series on adding a centralized login functionality to your iOS application by checking out an authentication SDK.

· Security Zone ·
Free Resource

DON’T STRESS! Assess your OSS. Get your free code scanner from FlexeraFlexNet Code Aware scans Java, NuGet, and NPM packages.

Welcome back! If you missed them, you can find Part 1 and Part 2 here. 

A Simpler Approach: Using the Auth0 SDK for iOS

Although the steps shown before are somewhat simple, there is an even easier way to do them! Auth0 provides a Swift library that can be used to interact with these endpoints in a simpler way. Let's take a look.

We will start from the same basic clean template as before: a single page iOS application with a label and a button that reads Login. When this button is pressed, the following code is executed:

@IBAction func buttonClicked(_ sender: Any) {
  if profile != nil {
      profile = nil
      updateUI()
  } else {
      Auth0
          .webAuth(clientId: clientId, domain: domain)
          .scope("openid token profile")
          .start { result in

              switch result {

              case .success(let credentials):
                  Auth0
                      .authentication(clientId: clientId, domain: domain)
                      .userInfo(withAccessToken: credentials.accessToken!)
                      .start { result in

                          switch result {

                          case .success(let profile):
                              self.profile = profile

                          case .failure(let error):
                              print("Failed with \(error)")
                              self.profile = nil
                          }

                          self.updateUI()
                  }

              case .failure(let error):
                  self.profile = nil
                  print("Failed with \(error)")
              }

              self.updateUI()
      }
  }
}

To use this library simply call Auth0.webAuth and pass the right clientId and domain parameters. You can then proceed to set the right scope or other settings by chaining calls. Finally, call start to start the authorization process. This will display the authorization URL in Safari. Once this is completed, the callback passed to start will be called with the access token and other credentials. That's right! All the PKCE stuff and the call to /oauth/token is handled automagically by the library, no need to do that manually anymore!

Once the credentials are in your possession you can get the user's information by calling:

Auth0.authentication(clientId: clientId, domain: domain)
     .userInfo(withAccessToken: credentials.accessToken!)
     .start { result in //(...) }

The access token passed to this call is the one returned by the webAuth operation from before. Piece of cake!

The Auth0.swift library internally uses the same method to communicate between Safari and the application: URL schemes. This means that the right callback URL must be set in the client settings for your app in the Auth0 dashboard. The format of this URL, however, is much more specific:

{YOUR_BUNDLE_IDENTIFIER}://${YOUR_AUTH0_DOMAIN}/ios/{YOUR_BUNDLE_IDENTIFIER}/callback

In our example this looks like:

Auth0.Centralized-Login-Test-2://speyrott.auth0.com/ios/Auth0.Centralized-Login-Test-2/callback

On iOS versions older than 11, the Auth0.swift library still requires the URL scheme to be configured manually as we did for our "manual endpoints" example in the Info.plist file:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>None</string>
    <key>CFBundleURLName</key>
    <string>auth0</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    </array>
  </dict>
</array>

The right handler in the AppDelegate must also be set:

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
  return Auth0.resumeAuth(url, options: options)
}

In any case, this is still much easier than hitting the endpoints manually.

Get the full code!

A Note About Credentials

The credentials returned by the call to /oauth/token or Auth0.webAuth (i.e. the access token and the refresh token) must be stored safely. In other words, do not put these elements in insecure storage like NSUserDefaults. iOS provides a keychain that can be used for these purposes. The safety of your app depends on the safety of the access and refresh tokens! Consider these as sensitive as user passwords.

Customization of the Centralized Login Page

As we said before, a big part of the user experience is the login view of our mobile app. This is, usually, one of the first things the users see. Therefore, we must make sure it fits with the rest of the application. The centralized login feature allows this too!

We've seen in our example that using Safari to open the URL for the /authorize endpoint results in a login page that gets displayed. This login page is hosted by the authorization server. In contrast, in legacy native mobile applications, the login page or view would be an internal part of the application and completely managed by it.

The Auth0 authorization server allows you to modify the login page served by it to your taste. To do this, open the dashboard and navigate to the Hosted Pages section. If you enable the Customize Login Page switch you will be presented by a text editor right below. This is the HTML page that is served when the authorization server needs a user to authenticate themselves.

By default, Auth0 uses the Auth0 Lock library. The Auth0 Lock library is a convenience library for displaying login pages and popups. This library supports all of the different login features that Auth0 provides, including social logins, username/password signup, and log in, etc. Of course, you are free to use your own, completely custom, login page. In general, Auth0 Lock is a great choice and can be customized to your taste.

For our example, we will simply change the logo and the default color scheme to a different one for our Centralized Login Test 1 application. The Centralized Login Test 2 application will remain with the default login page.

We have edited the default script in the default HTML page to look like this:

 <script>
  // Decode utf8 characters properly
  var config = JSON.parse(decodeURIComponent(escape(window.atob('@@config@@'))));
  config.extraParams = config.extraParams || {};
  var connection = config.connection;
  var prompt = config.prompt;
  var languageDictionary;
  var language;

  if (config.dict && config.dict.signin && config.dict.signin.title) {
    languageDictionary = { title: config.dict.signin.title };
  } else if (typeof config.dict === 'string') {
    language = config.dict;
  }
  var loginHint = config.extraParams.login_hint;

  var logo;
  var color;
  if(config.clientID === 'iV2lnrgSBw64uzf1x0MN3svQTYQYcBl2') {
    logo = 'https://upload.wikimedia.org/wikipedia/commons/5/54/Emojione_1F60E.svg'
    color = 'green'
  }

  var lock = new Auth0Lock(config.clientID, config.auth0Domain, {
    auth: {
      redirectUrl: config.callbackURL,
      responseType: (config.internalOptions || {}).response_type ||
        config.callbackOnLocationHash ? 'token' : 'code',
      params: config.internalOptions
    },
    assetsUrl:  config.assetsUrl,
    allowedConnections: connection ? [connection] : null,
    rememberLastLogin: !prompt,
    language: language,
    languageDictionary: languageDictionary,
    theme: {
      logo: logo,
      primaryColor: color
    },
    prefill: loginHint ? { email: loginHint, username: loginHint } : null,
    closable: false,
    // uncomment if you want small buttons for social providers
    // socialButtonStyle: 'small'
  });

  lock.show();
 </script>

Notice the if(config.clientID === 'iV2lnrgSBw64uzf1x0MN3svQTYQYcBl2') statement. When this client ID is used to log in, we simply change the logo and color scheme used by Auth0 Lock:

var logo;
var color;
if(config.clientID === 'iV2lnrgSBw64uzf1x0MN3svQTYQYcBl2') {
  logo = 'https://upload.wikimedia.org/wikipedia/commons/5/54/Emojione_1F60E.svg'
  color = 'green'
}

Here is the result:

If you are planning to support more than one social login option, or you are planning to enable more login options in the future, we strongly advise that you use Auth0 Lock. Removing Auth0 Lock implies handling each social login provider manually or with the help of a library such as Auth0.js but allows for 100% custom code.

Conclusion

Centralized logins are safer than embedded login widgets or pages. Relying on the OS's browser allows special security features to be used for your application to prevent credentials theft, and also allows password managers and single sign-on solutions to work across different applications. Furthermore, centralized login pages can be customized for a better user experience and to match the design guidelines of your application. Auth0 Lock can be used as the solution for centralized logins by having the authorization server use it during authentication, further reducing the amount of work that needs to be done for custom and 100% functional login solution. On the iOS side, the Auth0.swift library makes it a breeze to support as many social login providers and login options as required, all with just a few lines of non-invasive code! Sign up for a free Auth0 account today and start working on your iOS apps by focusing on what matters: the logic, not the login widget.

Try FlexNet Code Aware Today! A free scan tool for developers. Scan Java, NuGet, and NPM packages for open source security and license compliance issues.

Topics:
security ,ios security ,mobile security ,authentication

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}