/okta-sdk-appauth-ios

Okta with AppAuth

Primary LanguageSwiftOtherNOASSERTION

CI Status Version License Platform

Okta AppAuth-iOS Wrapper Library

This library is a wrapper around the AppAuth-iOS* SDK for communicating with Okta as an OAuth 2.0 + OpenID Connect provider, and follows current best practice for native apps using Authorization Code Flow + PKCE.

*Okta is using a forked version of AppAuth-iOS with logout functionality. See #259 for more details on this pending addition.

You can learn more on the Okta + iOS page in our documentation.

Table of Contents

Getting Started

Installing the Okta AppAuth wrapper into your project is simple. The easiest way to include this library into your project is through CocoaPods.

You'll also need:

  • An Okta account, called an organization (sign up for a free developer organization if you need one).
  • An Okta Application, configured as a Native App. This is done from the Okta Developer Console and you can find instructions here. When following the wizard, use the default properties. They are are designed to work with our sample applications.

Using Cocoapods

Simply add the following line to your Podfile:

pod 'OktaAuth'

Then install it into your project:

pod install

Handle the redirect

In order to redirect back to your application from a web browser, you must specify a unique URI to your app. To do this, open Info.plist in your application bundle and set a URL Scheme to the scheme of the redirect URI.

For example, if your Redirect URI is com.okta.example:/callback, the URL Scheme will be com.okta.example.

Next, update your AppDelegate to include the following function to allow the redirect to occur:

// AppDelegate.swift
import OktaAuth

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
    return OktaAuth.resume(url: url, options: options)
}

Usage Guide

For an overview of this library's features and authentication flows, check out our developer docs.

You can also browse the full API reference documentation.

Configuration Reference

There are multiple ways you can configure this library to perform authentication into your application. You can create a new plist file with shared values or directly pass your configuration into the sign in method directly.

Need a refresh token? A refresh token is a special token that is used to generate additional access and ID tokens. Make sure to include the offline_access scope in your configuration to silently renew the user's session in your application!

Property list

The easiest way is to create a proptery list in your application's bundle. By default, this library checks for the existance of the file Okta.plist, however any property list file will suffice. Ensure one is created with the following fields:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>issuer</key>
    <string>https://{yourOktaDomain}.com/oauth2/default</string>
    <key>clientId</key>
    <string>{clientId}</string>
    <key>redirectUri</key>
    <string>{redirectUri}</string>
    <key>logoutRedirectUri</key>
    <string>{logoutRedirectUri}</string>
    <key>scopes</key>
    <string>openid profile offline_access</string>
  </dict>
</plist>

Configuration object

Alternatively, you can create a dictionary with the required values:

let config = [
  "issuer": "https://{yourOktaDomain}/oauth2/default",
  "clientId": "{clientID}",
  "redirectUri": "{redirectUri}",
  "logoutRedirectUri": "{logoutRedirectUri}",
  "scopes": "openid profile offline_access",
  // Custom parameters
  "login_hint": "username@email.com"
]

API Reference

signInWithBrowser

Start the authorization flow by simply calling signIn. By default, this method uses the values specified in the Okta.plist file:

OktaAuth
  .signInWithBrowser()
  .start(view: self)
  .then { tokens in
    // tokens.accessToken
    // tokens.idToken
    // tokens.refreshToken
  }
  .catch { error in
    // Error
  }

Alternatively, use a custom plist file using the withPListConfig argument:

OktaAuth
  .signInWithBrowser()
  .start(withPListConfig: "CustomPlist", view: self)
  .then { tokens in
    // tokens.accessToken
    // tokens.idToken
    // tokens.refreshToken
  }
  .catch { error in
    // Error
  }

Finally, use a dictionary instead of a plist:

let config: [String: String] = [
  // Your configuation
]

OktaAuth
  .signInWithBrowser()
  .start(withDictConfig: config, view: self)
  .then { tokens in
    // tokens.accessToken
    // tokens.idToken
    // tokens.refreshToken
  }
  .catch { error in
    // Error
  }

signOutOfOkta

You can start the sign out flow by simply calling signOutFromOkta. This method will end the user's Okta session in the browser.

Important: This method does not clear or revoke tokens minted by Okta. Use the revoke and clear methods to terminate the user's local session in your application.

// Redirects to the configured 'logoutRedirectUri' specified in Okta.plist.
OktaAuth
  .signOutFromOkta()
  .start(view: self)
  .then {
    // Additional signout logic
  }
  .catch { error in
    // Error
  }

Similar to the signIn method, signOutOfOkta can accept a custom plist or dictionary configuration:

// Use a custom plist file
OktaAuth
  .signOutFromOkta()
  .start(withPListConfig: "CustomPlist", view: self)
  .then {
    // Additional signout logic
  }
  .catch { error in
    // Error
  }

// Use a dictionary object for configuration
let config: [String: String] = [
  // Your configuation
]

OktaAuth
  .signOutFromOkta()
  .start(withDictConfig: config, view: self)
  .then {
    // Additional signout logic
  }
  .catch { error in
    // Error
  }

isAuthenticated

Returns true if there is a valid access token stored in the TokenManager. This is the best way to determine if a user has successfully authenticated into your app.

if !OktaAuth.isAuthenticated() {
  // Prompt for sign in
}

getUser

Calls the OpenID Connect UserInfo endpoint with the stored access token to return user claim information.

OktaAuth.getUser() { response, error in
  if error != nil {
    print("Error: \(error!)")
  }

  if let userinfo = response {
    // JSON response
  }
}

introspect

Calls the introspection endpoint to inspect the validity of the specified token.

OktaAuth
  .introspect()
  .validate(token: token)
  .then { isActive in
    print("Is token valid? \(isActive)")
  }
  .catch { error in
    // Error
  }

refresh

Since access tokens are traditionally short-lived, you can refresh expired tokens by exchanging a refresh token for new ones. See the configuration reference to ensure your app is configured properly for this flow.

OktaAuth
  .refresh()
  .then { newAccessToken in
    print(newAccessToken)
  }
  .catch { error in
    // Error
  }

revoke

Calls the revocation endpoint to revoke the specified token.

OktaAuth
  .revoke(token: token) { response, error in
    if error != nil {
      print("Error: \(error!)")
    }
    if let _ = response {
      print("Token was revoked")
    }
}

tokens

Tokens are securely stored in the Keychain and can be retrieved by accessing the TokenManager. You can request them at any time by calling on the token object bound to OktaAuth:

OktaAuth.tokens?.accessToken
OktaAuth.tokens?.idToken
OktaAuth.tokens?.refreshToken

clear

Removes the local authentication state by removing cached tokens in the keychain.

OktaAuth.clear()

Development

Running Tests

To perform an end-to-end test, update the Okta.plist file to match your configuration as specified in the prerequisites. Next, export the following environment variables:

export USERNAME={username}
export PASSWORD={password}
export CLIENT_ID={clientId}
export ISSUER=https://{yourOktaDomain}/oauth2/default
export REDIRECT_URI={redirectUri}
export LOGOUT_REDIRECT_URI={logoutRedirectUri}

# Run E2E end Unit tests
bash ./scripts/build-and-test.sh

Note: You may need to update the emulator device to match your Xcode version