Skip to content

okta/okta-mobile-swift

Repository files navigation

License Support API Reference

Okta Mobile SDK for Swift

The Okta Mobile SDK replaces our legacy mobile SDKs (such as okta-oidc-ios) and enables:

  • Streamlined development
  • Increased maintainability of the Okta portion of your code
  • Easier feature expansion
  • Support for use cases that were previously difficult or impractical to implement.

This new SDK is built as a platform, enabling you to choose the components you need for your app.

Table of Contents

Release status

This library uses semantic versioning and follows Okta's Library Version Policy.

Version Status
1.8.2 ✔️ Stable

The latest release can always be found on the releases page.

Need help?

If you run into problems using the SDK, you can:

SDK Architecture

This SDK consists of several different libraries, each with detailed documentation.

  graph TD;
    AuthFoundation-->OktaOAuth2;
    OktaOAuth2-->WebAuthenticationUI;
Loading
  • AuthFoundation -- Common classes for managing credentials and used as a foundation for other libraries.
  • OktaDirectAuth -- Direct Authentication capabilities for advanced browserless authentication (EA).
  • OktaOAuth2 -- OAuth2 authentication capabilities for advanced use-cases.
  • WebAuthenticationUI -- Authenticate users using web-based OIDC flows.

This SDK enables you to build or support a myriad of different authentication flows and approaches.

Development Roadmap

This SDK is being actively developed, with plans for future expansion. At this time, we are seeking feedback from the developer community to evaluate:

  • The overall SDK and its components
  • The APIs and overall developer experience
  • Use-cases or features that may be missed or do not align with your application’s needs
  • Suggestions for future development
  • Any other comments or feedback on this new direction.

Key Features

Several key features and capabilities are introduced with this library, with some notable improvements listed below.

Feature
Simple OIDC web-based sign in
Credential management (secure storage, retrieval, etc)
Multi-token handling (store and use tokens for multiple users, scopes, etc)
Authorization Code Flow
Native SSO / Token Exchange Flow
Device Authorization Grant Flow
Resource Owner Flow
Simplified JWT parsing and handling
Streamlined authorization of URLSession requests using credential tokens
Many extension points for customizability, monitoring, and tracking

Getting Started

To get started, you will 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". Use Okta's administrator console to create the application by following the wizard and using default properties.
  • Xcode 13.x, targeting one of the supported platforms and target versions (see the Support Policy below).

For examples of how this SDK can be utilized, please refer to the sample applications included within this repository.

Install

Swift Package Manager

Add the following to the dependencies attribute defined in your Package.swift file. You can select the version using the majorVersion and minor parameters. For example:

dependencies: [
    .Package(url: "https://github.com/okta/okta-mobile-swift.git", majorVersion: <majorVersion>, minor: <minor>)
]

CocoaPods

Simply add the following line to your Podfile:

pod 'OktaWebAuthenticationUI'

Then install it into your project:

pod install --repo-update

If you are interested in only consuming the OktaOAuth2 library, instead use the following:

pod 'OktaOAuth2'

If you are participating in the Early Access preview of the Okta Direct Authentication API, use the following:

pod 'OktaDirectAuth'

Usage Guide

Web Authentication using OIDC

The simplest way to integrate authentication in your app is with OIDC through a web browser, using the Authorization Code Flow grant.

Configure your OIDC Settings

Before authenticating your user, you need to create your client configuration using the settings defined in your application in the Okta Developer Console. The simplest approach is to use a Okta.plist configuration file to specify these settings. 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</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>

Alternatively, you can supply those values to the constructor the WebAuthentication we're about to discuss in the next section.

Create a Web Authentication session

Once you've configured your application settings within your Okta.plist file, a shared configuration is automatically available through the WebAuthentication.shared singleton property. With that in place, you can use the convenience WebAuthentication.signIn(from:) method to prompt the user to sign in.

import WebAuthenticationUI

func signIn() async {
    let token = try await WebAuthentication.signIn(from: view.window)
    let credential = try Credential.store(token)
}

The signIn(from:) function returns a token and, by using the Credential class, you can save the token and use it within your application.

Authentication using Device Code-Flow Grant

For headless devices, or devices that are difficult to use a keyboard (e.g. AppleTV), your application can use OktaOAuth2 directly with the DeviceAuthorizationFlow class. This will enable you to present a easy to remember code to your user, which they can use on a different device to authorize your application.

Using this is simple:

  1. Create an instance of DeviceAuthorizationFlow
let flow = DeviceAuthorizationFlow(
    issuer: URL(string: "https://example.okta.com")!,
    clientId: "abc123client",
    scopes: "openid offline_access email profile")
  1. Start an authentication session to receive the code and authorize URL to present to your user.
let context = try await flow.start()
let code = context.userCode
let uri = context.verificationUri
  1. Wait for the user to authorize the application from another device.
let token = try await flow.resume(with: context)

Authentication using Native SSO flow

When using the device_sso scope, your application can receive a "device secret", which can be used in combination with your user's ID token to exchange new credentials. To use this within your application, you would use the TokenExchangeFlow to exchange those sets of tokens.

let flow = TokenExchangeFlow(
    issuer: URL(string: "https://example.okta.com")!,
    clientId: "abc123client",
    scopes: "openid offline_access email profile",
    audience: .default)

let token = try await flow.start(with: [
    .actor(type: .deviceSecret, value: "DeviceToken"),
    .subject(type: .idToken, value: "IDToken")
])

Authentication with Username/Password

For simple authentication use-cases, you can use the ResourceOwnerFlow class to authenticate with a plain username and password.

NOTE: The ResourceOwnerFlow class has been marked as deprecated, since its functionality is being replaced with the more comprehensive OktaDirectAuth library.

let flow = ResourceOwnerFlow(issuer: URL(string: "https://example.okta.com")!,
                             clientId: "abc123client",
                             scopes: "openid offline_access email profile")
let token = try await flow.start(username: "jane.doe", password: "secretPassword")

Authentication using Direct Authentication (EA)

For simple authentication use-cases, you can use the ResourceOwnerFlow class to authenticate with a plain username and password.

NOTE: The Okta Direct Authentication API is currently marked as Early Access (EA) and is not generally available yet.

let flow = DirectAuthenticationFlow(issuer: URL(string: "https://example.okta.com")!,
                                    clientId: "abc123client",
                                    scopes: "openid offline_access email profile")
switch try await flow.start("[email protected]", with: .password("secretPassword")) {
    case .success(let token):
        // Store the token
    case .mfaRequired(_):
        // Continue authentication
}

For more information, see the OktaDirectAuth API documentation.

Storing and using tokens

Once your user has authenticated and you have a Token object, your application can store and use those credentials. The most direct approach is to use the Credential.store(_:tags:security:) function.

let credential = try Credential.store(token)

As a convenience, the SDK provides a default static property on the Credential class. This provides a simple way to identify if a user is currently authenticated, and to quickly access that user's credentials. When storing a new credential, if one isn't already stored, it will automatically be assigned as the default.

if let credential = Credential.default {
    // The user is signed in. Start by refreshing it.
    try await credential.refreshIfNeeded()
}

Finding credentials by their unique identifier

When a token is stored, it is assigned a unique ID, which can be used to differentiate between tokens and to retrieve a token at a later date.

let tokenId = token.id

// Later, retrieve the token
if let credential = try Credential.with(id: tokenId) {
    // Use the credential
}

Assigning and finding credentials using custom tags

For more complex applications, you may need to manage multiple credentials (e.g. multi-user sign-in, different tokens for app extensions, granular scopes for different portions of your application, etc). To make it easier to differentiate between credentials, you can assign tags to them which can later be used to identify them.

try Credential.store(token, tags: ["customTag": "someValue"])

The credential can later be retrieved based on these tags.

if let credential = try Credential.find(where: { $0.tags["customTag"] == "someValue" }).first {
    // Use the credential
}

A credential's tags are available through its tags property, and can be changed after the fact.

if !credential.tags.contains("someCustomTag") {
    credential.tags["someCustomTag"] = "someValue"
}

// Or use the following method to intercept exceptions
try credential.setTags(["customTag": "someValue"])

Finding credentials using ID token claims

This SDK simplifies access to JWT tokens and their claims. In fact, a Token's idToken property is automatically exposed as an instance of JWT. Using this, you can enumerate and retrieve credentials based on the claims associated with their tokens.

if let credential = try Credential.find(where: { $0.email == "[email protected]" }).first {
    // Use the credential
}

Rate Limit Handling

The Okta API will return 429 responses if too many requests are made within a given time. Please see Rate Limiting at Okta for a complete list of which endpoints are rate limited. This SDK automatically retries requests on 429 errors. The default configuration is as follows:

Configuration Option Description
maximumCount The number of times to retry. The default value is 3.

Customizing Rate Limit

To customize how rate limit is handled, conform to the APIClientDelegate protocol, implement the shouldRetry(request:rateLimit:) method, and add your class as a delegate for the appropriate client. When any request sent through that client receives an HTTP 429 error response, it will allow you to customize the rate limit behavior.

import AuthFoundation

func login() {
    // Configure your authentication flow, before running the following command
    flow.client.add(delegate: self)
}

extension OAuth2Client {
    public func api(client: APIClient, shouldRetry request: URLRequest) -> APIRetry {
        return .doNotRetry
    }
}

For more information, refer to the API documentation for the APIRetry enumeration.

Migration from legacy SDKs

This collection of SDKs intend to replace the following SDKs:

If your application currently uses OktaOidc, facilities are in place to migrate your existing users to the new SDK. For more information, see the SDKVersion.Migration class for details.

Running the Samples

Several applications are available to demonstrate different workflows of this SDK. For more information, please see the sample applications.

Support Policy

This policy defines the extent of the support for Xcode, Swift, and platform (iOS, macOS, tvOS, and watchOS) versions.

Xcode

The only supported versions of Xcode are those that can be currently used to submit apps to the App Store. Once a Xcode version becomes unsupported, dropping support for it will not be considered a breaking change, and will be done in a minor release.

Swift

The minimum supported Swift 5 minor version is the one released with the oldest-supported Xcode version. Once a Swift 5 minor becomes unsupported, dropping support for it will not be considered a breaking change, and will be done in a minor release.

Platforms

Only the last 4 major platform versions are officially supported, unless there are platform limitations that limit our ability to support older versions.

Platform Supported Best-Effort
iOS 13.0 12.0
tvOS 13.0 12.0
watchOS 7.0 7.0
visionOS 1.0 1.0
macCatalyst 13.0 13.0
macOS 12.0 10.15

Once a platform version becomes unsupported, dropping support for it will not be considered a breaking change and will be done in a minor release. For example, iOS 13 will cease to be supported when iOS 18 gets released, and might be dropped in a minor release.

In the case of macOS, the yearly named releases are considered a major platform version for this Policy, regardless of the actual version numbers.

Note: Older OS versions are supported in a best-effort manner. Unless there are API limitations that prevent the SDK from working effectively on older OS versions, the minimum requirements will not be changed.

Additionally, Linux compatibility is considered best-effort and is not officially supported.

Legacy SDK support

After the okta-mobile-swift SDK becomes generally available, we intend all new feature development to proceed within this new library. We plan to support okta-oidc-ios (and our other legacy SDKs that okta-mobile-swift replaces) with critical bug and security fixes for the foreseeable future.

Development

Protecting Test Configuration

This repository contains two files within Samples/Shared which are used to expose test credentials to automated tests as well as the sample applications.

To protect against accidental changes being introduced to these files, it is recommended that you use the following command after cloning this repository:

git config core.hooksPath ./.githooks

This will run checks before committing changes to ensure these files are not altered.

Running Tests

Tests can be run on macOS from the command-line using:

swift test

Alternatively, if you wish to run tests within Linux, you can utilize Docker from a macOS environment to run Linux tests:

docker run --rm --privileged --interactive --tty \
    --volume "$(pwd):/src" --workdir "/src" swift:latest \
    swift test

Known issues

Contributing

We are happy to accept contributions and PRs! Please see the contribution guide to understand how to structure a contribution.