The Okta Client SDK represents a collection of SDKs for different languages, each of which itself is a modular ecosystem of libraries that build upon one-another to enable client applications to:
- Authenticate clients with an Authorization Server (AS) using a variety of authentication flows.
- Flexibly store and manage the resulting tokens enabling a wide variety of use-cases.
- Transparently persist and manage the lifecycle of those tokens through authentication, refresh, and revocation.
- Secure applications and tokens, using best practices, by default.
This SDK emphasizes security, developer experience, and customization of the SDK's core capabilities. It is built as a platform, enabling you to choose the individual library components you need for your app.
Table of Contents
This library uses semantic versioning and follows Okta's Library Version Policy.
Version | Status |
---|---|
1.8.3 | Retiring |
2.0.0 | ✔️ Stable |
The latest release can always be found on the releases page.
If you run into problems using the SDK, you can:
- Review the API documentation for AuthFoundation, OAuth2Auth, OktaDirectAuth, OktaIdxAuth, and BrowserSignin
- Ask questions on the Okta Developer Forums
- Post issues here on GitHub (for code errors)
This SDK consists of several different libraries, each with detailed documentation.
graph BT;
AuthFoundation-->OAuth2Auth;
AuthFoundation-->OktaDirectAuth;
AuthFoundation-->OktaIdxAuth;
OAuth2Auth-->BrowserSignin;
- AuthFoundation -- Common classes for managing credentials and used as a foundation for other libraries.
- OAuth2Auth -- OAuth2 authentication capabilities for advanced use-cases.
- OktaDirectAuth -- Direct Authentication capabilities for advanced browserless authentication.
- OktaIdxAuth -- Okta's Identity Engine support using Okta's IDX API for native browserless authentication.
- BrowserSignin -- Authenticate users using web-based OIDC flows.
This SDK enables you to build or support a myriad of different authentication flows and approaches.
This SDK is being actively developed, with plans for future expansion. We are always 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.
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 |
JWT 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 |
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 16.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.
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>)
]
Simply add the following line to your Podfile
:
pod 'OktaBrowserSignin'
Then install it into your project:
pod install --repo-update
If you are interested in only consuming the OAuth2Auth library, instead use the following:
pod 'OAuth2Auth'
If you intend to use the Okta Direct Authentication API, use the following:
pod 'OktaDirectAuth'
If your application requires advanced integration with Okta's IDX API, use the following:
pod 'OktaIdxAuth'
The simplest way to integrate authentication in your app is with OIDC through a web browser, using the Authorization Code Flow grant.
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_url</key>
<string>https://{yourOktaDomain}.com</string>
<key>client_id</key>
<string>{clientId}</string>
<key>redirect_uri</key>
<string>{redirectUri}</string>
<key>logout_redirect_uri</key>
<string>{logoutRedirectUri}</string>
<key>scope</key>
<string>openid profile offline_access</string>
</dict>
</plist>
Alternatively, you can supply those values to the constructor the BrowserSignin
we're about to discuss in the next section.
Once you've configured your application settings within your Okta.plist
file, a shared configuration is automatically available through the BrowserSignin.shared
singleton property. With that in place, you can use the convenience BrowserSignin.signIn(from:)
method to prompt the user to sign in.
import BrowserSignin
func signIn() async throws {
let token = try await BrowserSignin.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.
For headless devices, or devices that are difficult to use a keyboard (e.g. AppleTV), your application can use OAuth2Auth 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:
- Create an instance of
DeviceAuthorizationFlow
let flow = DeviceAuthorizationFlow(
issuerURL: URL(string: "https://example.okta.com")!,
clientId: "abc123client",
scope: "openid offline_access email profile")
- 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
- Wait for the user to authorize the application from another device.
let token = try await flow.resume(with: context)
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(
issuerURL: URL(string: "https://example.okta.com")!,
clientId: "abc123client",
scope: "openid offline_access email profile",
audience: .default)
let token = try await flow.start(with: [
.actor(type: .deviceSecret, value: "DeviceToken"),
.subject(type: .idToken, value: "IDToken")
])
For simple authentication use-cases, you can use the ResourceOwnerFlow
class to authenticate with a plain username and password.
NOTE: The ResourceOwnerFlow class is not recommended since this flow does not support multifactor authentication. For alternatives to this flow, please see the more comprehensive OktaDirectAuth or OktaIdxAuth libraries.
let flow = ResourceOwnerFlow(issuerURL: URL(string: "https://example.okta.com")!,
clientId: "abc123client",
scope: "openid offline_access email profile")
let token = try await flow.start(username: "jane.doe", password: "secretPassword")
For simple authentication use-cases, you can use the ResourceOwnerFlow
class to authenticate with a plain username and password.
NOTE: The Okta Direct Authentication library is only available in Swift at this time.
let flow = DirectAuthenticationFlow(issuerURL: URL(string: "https://example.okta.com")!,
clientId: "abc123client",
scope: "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.
For more advanced native authentication use-cases, you can use the InteractionCodeFlow
class to authenticate using the Okta Identity Engine.
let flow = try InteractionCodeFlow(issuerURL: URL(string: "https://example.okta.com")!,
clientId: "abc123client",
scope: "openid offline_access email profile",
redirectUri: URL(string: "my.app:/callback"))
For more information, see the OktaIdxAuth API documentation.
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()
}
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
}
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"])
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
}
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 . |
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.
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.
Several applications are available to demonstrate different workflows of this SDK. For more information, please see the sample applications.
This policy defines the extent of the support for Xcode, Swift, and platform (iOS, macOS, tvOS, and watchOS) versions.
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.
The minimum supported Swift version is 5.10, which is the version shipped 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.
This library supports Swift 6, with full support for Strict Concurrency.
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 | 15.0 | 13.0 |
tvOS | 15.0 | 13.0 |
watchOS | 8.0 | 7.0 |
visionOS | 1.0 | 1.0 |
macCatalyst | 15.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 15 will cease to be supported when iOS 19 is released, and might be dropped in a minor release in the future.
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.
Linux Compatibility
Linux support is experimental. Compatibility with Linux considered is best-effort and is not officially supported. Ubuntu is included as a test target for all Continuous Integration tests, and every effort is taken to ensure its continued compatibility.
Some features are not yet supported in Linux, including but not limited to:
Feature | Comments |
---|---|
Keychain Token Storage | The UserDefaults token storage mechanism is supported, but encryption / security at rest is not implemented yet. |
Browser Authentication | The BrowserSignin library only targets Apple platforms, and is unavailable in Linux |
JWT Validation | The Linux-compatible crypto libraries have not yet been integrated into JWT validation |
PKCE | PKCE key and signature generation has not been implemented in Linux yet |
The okta-oidc-ios SDK is considered legacy, and all new feature development is made to okta-mobile-swift. The legacy SDKs only receive critical bug and security fixes, so it's advisable for developers to migrate to this new SDK.
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.
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
We are happy to accept contributions and PRs! Please see the contribution guide to understand how to structure a contribution.