The Okta Authentication SDK is a convenience wrapper around Okta's Authentication API.
- Is This Library Right for Me?
- Authentication State Machine
- Release status
- Need help?
- Getting started
- Usage guide
- API Reference - Status classes
- API Reference - Factor classes
- SDK extension
- Contributing
This SDK is a convenient wrapper for Okta's Authentication API. These APIs are powerful and useful if you need to achieve one of these cases:
- You have an existing application that needs to accept primary credentials (username and password) and do custom logic before communicating with Okta.
- You have significantly custom authentication workflow or UI needs, such that Okta’s hosted sign-in page or Sign-In Widget do not give you enough flexibility.
The power of this SDK comes with more responsibility and maintenance: you will have to design your authentication workflow and UIs by hand, respond to all relevant states in Okta’s authentication state machine, and keep up to date with new features and states in Okta.
Otherwise, most applications can use the Okta hosted sign-in page or the Sign-in Widget. For these cases, you should use Okta's OIDC SDK for iOS or other OIDC/OAuth 2.0 library.
Okta's Authentication API is built around a state machine. In order to use this library you will need to be familiar with the available states. You will need to implement a handler for each state you want to support.
This library uses semantic versioning and follows Okta's library version policy.
Version | Status |
---|---|
1.x | |
2.x | ✔️ Stable |
The latest release can always be found on the releases page.
If you run into problems using the SDK, you can
- Ask questions on the Okta Developer Forums
- Post issues here on GitHub (for code errors)
- Check our sample application
If you do not already have a Developer Edition Account, you can create one at https://developer.okta.com/signup/.
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-auth-swift.git", majorVersion: <majorVersion>, minor: <minor>)
]
This SDK is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod "OktaAuthSdk"
To integrate this SDK into your Xcode project using Carthage, specify it in your Cartfile:
github "okta/okta-auth-swift"
The Authentication SDK helps you build the following flows using your own UI elements:
- Primary authentication - allows you to verify username and password credentials for a user.
- Multifactor authentication (MFA) - strengthens the security of password-based authentication by requiring additional verification of another factor such as a temporary one-time password or an SMS passcode. This SDK supports user enrollment with MFA factors enabled by the administrator, as well as MFA challenges based on your Okta Sign-On Policy.
- Unlock account - unlocks a user account if it has been locked out due to excessive failed login attempts. This functionality is subject to the security policy set by the administrator.
- Recover password - allows users to securely reset their password if they've forgotten it. This functionality is subject to the security policy set by the administrator
- Restore authentication/unlock/recover transaction with the state token
To initiate a particular flow please make a call to one of the available functions in OktaAuthSdk.swift
:
// Authentication
public class func authenticate(with url: URL,
username: String,
password: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
// Unlock account
public class func unlockAccount(with url: URL,
username: String,
factorType: OktaRecoveryFactors,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
// Forgot password
public class func recoverPassword(with url: URL,
username: String,
factorType: OktaRecoveryFactors,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
// Restore authentication
public class func fetchStatus(with stateToken: String,
using url: URL,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Please note that the closure onStatusChange
returns OktaAuthStatus
instance as a parameter. An instance of the OktaAuthStatus
class represents the current status that is returned by the server. It's the developer's responsibilty to handle current status in order to proceed with the initiated flow. Check the status type by calling status.statusType
and downcast to a concrete status class. Here is an example handler function:
func handleStatus(status: OktaAuthStatus) {
switch status.statusType {
case .success:
let successState: OktaAuthStatusSuccess = status as! OktaAuthStatusSuccess
handleSuccessStatus(successStatus: successStatus)
case .passwordWarning:
let warningPasswordStatus: OktaAuthStatusPasswordWarning = status as! OktaAuthStatusPasswordWarning
handlePasswordWarning(passwordWarningStatus: warningPasswordStatus)
case .passwordExpired:
let expiredPasswordStatus: OktaAuthStatusPasswordExpired = status as! OktaAuthStatusPasswordExpired
handleChangePassword(passwordExpiredStatus: expiredPasswordStatus)
case .MFAEnroll:
let mfaEnroll: OktaAuthStatusFactorEnroll = status as! OktaAuthStatusFactorEnroll
handleEnrollment(enrollmentStatus: mfaEnrollStatus)
case .MFAEnrollActivate:
let mfaEnrollActivate: OktaAuthStatusFactorEnrollActivate = status as! OktaAuthStatusFactorEnrollActivate
handleActivateEnrollment(enrollActivateStatus: mfaEnrollActivateStatus)
case .MFARequired:
let mfaRequired: OktaAuthStatusFactorRequired = status as! OktaAuthStatusFactorRequired
handleFactorRequired(factorRequiredStatus: mfaRequiredStatus)
case .MFAChallenge:
let mfaChallenge: OktaAuthStatusFactorChallenge = status as! OktaAuthStatusFactorChallenge
handleFactorChallenge(factorChallengeStatus: mfaChallengeStatus)
case .recovery:
let recovery: OktaAuthStatusRecovery = status as! OktaAuthStatusRecovery
handleRecovery(recoveryStatus: recoveryStatus)
case .recoveryChallenge:
let recoveyChallengeStatus: OktaAuthStatusRecoveryChallenge = status as! OktaAuthStatusRecoveryChallenge
handleRecoveryChallenge(recoveryChallengeStatus: recoveyChallengeStatus)
case .passwordReset:
let passwordResetStatus: OktaAuthStatuPasswordReset = status as! OktaAuthStatuPasswordReset
handlePasswordReset(passwordResetStatus: passwordResetStatus)
case .lockedOut:
let lockedOutStatus: OktaAuthStatusLockedOut = status as! OktaAuthStatusLockedOut
handleLockedOut(lockedOutStatus: lockedOutStatus)
case .unauthenticated:
let unauthenticatedStatus: OktaAuthUnauthenticated = status as! OktaAuthUnauthenticated
handleUnauthenticated(unauthenticatedStatus: unauthenticatedStatus)
}
}
Sample app example
An authentication flow starts with a call to authenticate
method:
OktaAuthSdk.authenticate(with: URL(string: "https://{yourOktaDomain}")!,
username: "username",
password: "password",
onStatusChange: { authStatus in
handleStatus(status: authStatus)
},
onError: { error in
handleError(error)
})
Please refer to the Primary Authentication section of API documentation for more information about this API request. Sample app example
OktaAuthSdk.unlockAccount(with: URL(string: "https://{yourOktaDomain}")!,
username: "username",
factorType: .sms,
onStatusChange: { authStatus in
handleStatus(status: authStatus)
},
onError: { error in
handleError(error)
})
Please refer to the Unlock Account section of API documentation for more information about this API request. Sample app example
OktaAuthSdk.recoverPassword(with: URL(string: "https://{yourOktaDomain}")!,
username: "username",
factorType: .sms,
onStatusChange: { authStatus in
handleStatus(status: authStatus)
},
onError: { error in
handleError(error)
})
Please refer to the Forgot Password section of API documentation for more information about this API request. Sample app example
OktaAuthSdk.fetchStatus(with: "state_token",
using: URL(string: "https://{yourOktaDomain}")!,
onStatusChange: { authStatus in
handleStatus(status: authStatus)
},
onError: { error in
handleError(error)
})
Please refer to the Get Transaction State section of API documentation for more information about this API request.
Collection of status classes. Downcast the status instance to a status specific class to get access to status specific functions and properties.
Base status class that implements some common functions for the statuses. Also contains statusType
property that is used to check the actual status type.
Returns true
if current status can transition to the previous status.
open func canReturn() -> Bool
Sample app example
Moves the current transaction state back to the previous state.
open func returnToPreviousStatus(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Returns true
if current flow can be cancelled.
open func canCancel() -> Bool
Sample app example
Cancels the current transaction and revokes the state token.
open func cancel(onSuccess: (() -> Void)? = nil,
onError: ((_ error: OktaError) -> Void)? = nil)
Sample app example
This class is used to initiate authentication or recovery transactions.
open func authenticate(username: String,
password: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
open func unlockAccount(username: String,
factorType: OktaRecoveryFactors,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
open func recoverPassword(username: String,
factorType: OktaRecoveryFactors,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
The transaction has completed successfully. Add a handler function to retrieve the session token. Sample app example
The user must select and enroll an available factor for additional verification. Add a handler function to enroll specific factor.
Returns true
if specific factor can be enrolled.
open func canEnrollFactor(factor: OktaFactor) -> Bool
Returns true
if enrollment can be skipped.
open func canSkipEnrollment() -> Bool
Sample app example
Skips the current transaction state and advance to the next state.
open func skipEnrollment(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Enrolls a user with a factor assigned by their MFA Policy.
open func enrollFactor(factor: OktaFactor,
questionId: String?,
answer: String?,
credentialId: String?,
passCode: String?,
phoneNumber: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
The user must activate the factor to complete enrollment.
Returns true
if SDK can resend factor.
open func canResend(factor: OktaFactor) -> Bool
Sample app example
Activates sms, call and token:software:totp factors to complete the enrollment process.
open func activateFactor(passCode: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Tries to resend activate request.
open func resendFactor(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
The user must provide additional verification with a previously enrolled factor.
Triggers the selected factor. Sends SMS/Call OTP or push notification to Okta Verify application.
open func selectFactor(_ factor: OktaFactor,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
The user must verify the factor-specific challenge.
Returns true
if the SDK can verify challenge.
open func canVerify() -> Bool
Sample app example
Returns true
if the SDK can resend challenge for the selected factor.
open func canResend() -> Bool
Sample app example
Verifies an answer to a question factor or OTP code for SMS/Call/Totp/Token factors.
open func verifyFactor(passCode: String?,
answerToSecurityQuestion: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sends another OTP code or push notification if the user didn't receive the previous one due to timeout or error.
open func resendFactor(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
The user's password was successfully validated but is expired.
Returns true
if password can be changed.
open func canChange() -> Bool
Changes a user's password by providing the existing password and the new password.
open func changePassword(oldPassword: String,
newPassword: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
The user's password was successfully validated but is about to expire and should be changed.
Returns true
if password can be changed.
open func canChange() -> Bool
Returns true
if user may skip password change.
open func canSkip() -> Bool
Sample app example
Changes a user's password by providing the existing password and the new password.
open func changePassword(oldPassword: String,
newPassword: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Sends skip request to skip the password change state and advance to the next state.
open func skipPasswordChange(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
The user has requested a recovery token to reset their password or unlock their account.
Returns true
if recovery flow can be continued.
open func canRecover() -> Bool
Sample app example
Answers the user's recovery question
open func recoverWithAnswer(_ answer: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
The user must verify the factor-specific recovery challenge.
Returns true
if the factor can be verified.
open func canVerify() -> Bool
Sample app example
Returns true
if the factor can be resent.
open func canResend() -> Bool
Sample app example
Verifies SMS/Call OTP(passCode) sent to the user's device for primary authentication for a recovery transaction.
open func verifyFactor(passCode: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Resends SMS/Call OTP sent to the user's device for a recovery transaction.
open func resendFactor(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
The user successfully answered their recovery question and must set a new password.
Returns true
if password can be reset.
open func canReset() -> Bool
Sample app example
Resets a user's password to complete a recovery transaction.
open func resetPassword(newPassword: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
The user account is locked; self-service unlock or administrator unlock is required.
Returns true
if the user account can be unlocked.
open func canUnlock() -> Bool
Starts a new unlock recovery transaction for a given user and issues a recovery token that can be used to unlock a user's account.
open func unlock(username: String,
factorType: OktaRecoveryFactors,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Collection of factor classes. Downcast the factor instance to a factor specific class to get access to factor specific functions and properties.
Enrolls a user with the Okta SMS factor and an SMS profile. A text message with an OTP is sent to the device during enrollment.
public func enroll(phoneNumber: String?,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
Sample app example
Activates an SMS factor by verifying the OTP.
public func activate(passCode: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Sends a new OTP to the device.
public func select(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Verifies an enrolled SMS factor by verifying the OTP.
public func verify(passCode: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Enrolls a user with the Okta call factor and a Call profile. A voice call with an OTP is sent to the device during enrollment.
public func enroll(phoneNumber: String?,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
Sample app example
Activates a call factor by verifying the OTP.
public func activate(passCode: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Sends a new OTP to the device.
public func select(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Verifies an enrolled call factor by verifying the OTP.
public func verify(passCode: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Enrolls a user with the Okta verify push factor. The factor must be activated on the device by scanning the QR code or visiting the activation link sent via email or SMS. Use the published activation links to embed the QR code or distribute an activation email or SMS.
public func enroll(onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
Sample app example
Activation of push factors are asynchronous and must be polled for completion when the factorResult returns a WAITING status.
Activations have a short lifetime (minutes) and will TIMEOUT if they are not completed before the expireAt
timestamp. Restart the activation process if the activation is expired.
public func activate(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
After the push notification is sent to user's device we need to know when the user completes the activation/challenge.
public func checkFactorResult(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Keep calling checkFactorResult
function while status.factorResult
value equals .waiting
.
NOTE Don't call checkFactorResult
function too often, keep polling interval within 3-5 seconds to not cause extra load on the server
Example of polling logic:
func handlePushChallenge(factor: OktaFactorPush) {
factor.checkFactorResult(onStatusChange: { (status) in
if status.factorResult == .waiting {
DispatchQueue.main.asyncAfter(deadline:.now() + 5.0) {
self.handlePushChallenge(factor: factor)
}
} else {
self.handleStatus(status: status)
}
}, onError: { (error) in
self.handleError(error)
})
}
Sends an activation SMS when the user is unable to scan the QR code provided as part of an Okta Verify transaction. If for any reason the user can't scan the QR code, they can use the link provided in SMS to complete the transaction.
public func sendActivationLinkViaSms(with phoneNumber:String,
onSuccess: @escaping () -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sends an activation email when when the user is unable to scan the QR code provided as part of an Okta Verify transaction. If for any reason the user can't scan the QR code, they can use the link provided in email to complete the transaction.
public func sendActivationLinkViaEmail(onSuccess: @escaping () -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sends an asynchronous push notification (challenge) to the device for the user to approve or reject.
public func select(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Sends an asynchronous push notification (challenge) to the device for the user to approve or reject. The factorResult
for the transaction will have a result of WAITING, SUCCESS, REJECTED, or TIMEOUT.
public func verify(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Enrolls a user with the Okta token:software:totp factor.
public func enroll(onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
Sample app example
Activates a token:software:totp factor by verifying the OTP (passcode).
public func activate(passCode: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Selects TOTP factor from the list of required factors and verifies the OTP (passcode).
public func select(passCode: String,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
Sample app example
Verifies an OTP for a token:software:totp factor.
public func verify(passCode: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Enrolls a user with the Okta question factor. List of security questions can be downloaded by downloadSecurityQuestions
call.
NOTE: Security Question factor does not require activation and is ACTIVE after enrollment
public func enroll(questionId: String,
answer: String,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
Sample app example
Downloads security questions for the user
public func downloadSecurityQuestions(onDownloadComplete: @escaping ([SecurityQuestion]) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
Sample app example
Selects a question factor from the list of required factors and verifies the answer.
public func select(answerToSecurityQuestion: String,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
Sample app example
Verifies an answer to a question factor.
public func verify(answerToSecurityQuestion: String,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
Sample app example
Enrolls a user with a RSA SecurID factor and a token profile. RSA tokens must be verified with the current pin+passcode as part of the enrollment request.
public func enroll(credentialId: String,
passCode: String,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
Selects a token factor from the list of required factors and verifies the passcode.
public func select(passCode: String,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
Sample app example
Verifies a passcode to a token factor.
public func verify(passCode: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void)
SDK doesn't implement swift classes for all available factors. SDK returns OktaFactorOther
instance for the non-implemented factors, e.g.: Google Authenticator, Symantec VIP Factor, U2F and etc. Use OktaFactorOther
class to send arbitary data to the Okta server. For more information regarding payload please refer to the API documentation
Sends arbitrary kayValuePayload
body in the https request.
public func sendRequest(with link: LinksResponse.Link,
keyValuePayload: Dictionary<String, Any>,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void)
You are open to subclass from any available status classes and add your custom implementation or extend with additional properties. In that case you have to also subclass from OktaAuthStatusResponseHandler
class and override handleServerResponse
or/and createAuthStatus
methods.
This is useful in these situations:
- Okta added a new status in the state machine and you need to handle it
- You want to change status polling logic
- You created a class that is inherited from OktaAuthStatus* class
class MyResponseHandler: OktaAuthStatusResponseHandler {
override func createAuthStatus(basedOn response: OktaAPISuccessResponse,
and currentStatus: OktaAuthStatus) throws -> OktaAuthStatus {
// implementation
}
}
let unauthenticatedStatus = OktaAuthStatusUnauthenticated(oktaDomain: URL(string: "https://{yourOktaDomain}")!,
responseHandler: MyResponseHandler())
You can inject HTTP request listener in order to send requests with your own HTTP library.
Implement OktaHTTPRequestListenerProtocol
and inject the instance which conforms to the protocol in OktaAPI through property injection. You can use provided URLRequest as-is or copy data from it to your own HTTP request before sending
public protocol OktaHTTPRequestListenerProtocol {
func sendRequest(_ request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void)
}
We're happy to accept contributions and PRs!