Skip to content

Commit

Permalink
Merge pull request #78 from okta/iabdullin/indefinite_polling
Browse files Browse the repository at this point in the history
OKTA-234954-AuthN iOS SDK: Push factor polling doesn't stop if user taps on back button
  • Loading branch information
IldarAbdullin-okta authored Jul 1, 2019
2 parents 9e5d0f7 + 630e5d0 commit 7b7b9d6
Show file tree
Hide file tree
Showing 22 changed files with 131 additions and 359 deletions.
46 changes: 18 additions & 28 deletions Example/Source/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class ViewController: UIViewController {

case .success:
let successState: OktaAuthStatusSuccess = status as! OktaAuthStatusSuccess
handleSuccessStatus(sessionToken: successState.sessionToken)
handleSuccessStatus(sessionToken: successState.sessionToken!)

case .passwordWarning:
let warningPasswordStatus: OktaAuthStatusPasswordWarning = status as! OktaAuthStatusPasswordWarning
Expand Down Expand Up @@ -112,7 +112,7 @@ class ViewController: UIViewController {
.passwordReset,
.lockedOut,
.unauthenticated:
let alert = UIAlertController(title: "Error", message: "No handler for \(status.statusType.description)", preferredStyle: .alert)
let alert = UIAlertController(title: "Error", message: "No handler for \(status.statusType.rawValue)", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
self.cancelTransaction()
Expand All @@ -132,9 +132,9 @@ class ViewController: UIViewController {
}

if let factorResult = factorResult {
stateLabel.text = "\(status.statusType.description) \(factorResult.rawValue)"
stateLabel.text = "\(status.statusType.rawValue) \(factorResult.rawValue)"
} else {
stateLabel.text = status.statusType.description
stateLabel.text = status.statusType.rawValue
}
}

Expand Down Expand Up @@ -186,7 +186,7 @@ class ViewController: UIViewController {

let alert = UIAlertController(title: "Select verification factor", message: nil, preferredStyle: .actionSheet)
factorRequiredStatus.availableFactors.forEach { factor in
alert.addAction(UIAlertAction(title: factor.type.description, style: .default, handler: { _ in
alert.addAction(UIAlertAction(title: factor.type.rawValue, style: .default, handler: { _ in
factorRequiredStatus.selectFactor(factor,
onStatusChange: { status in
self.handleStatus(status: status)
Expand Down Expand Up @@ -215,7 +215,7 @@ class ViewController: UIViewController {
let alert = UIAlertController(title: "Select factor to enroll", message: nil, preferredStyle: .actionSheet)
let factors = enrollmentStatus.availableFactors
factors.forEach { factor in
var title = factor.type.description
var title = factor.type.rawValue
if let factorStatus = factor.status {
title = title + " - " + "(\(factorStatus))"
}
Expand Down Expand Up @@ -243,7 +243,7 @@ class ViewController: UIViewController {
self.handleError(error)
})
} else {
let alert = UIAlertController(title: "Error", message: "No handler for \(factor.type.description) factor", preferredStyle: .alert)
let alert = UIAlertController(title: "Error", message: "No handler for \(factor.type.rawValue) factor", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
self.cancelTransaction()
Expand All @@ -261,7 +261,7 @@ class ViewController: UIViewController {
let factor = status.factor
guard factor.type == .sms ||
factor.type == .push else {
let alert = UIAlertController(title: "Error", message: "No handler for \(factor.type.description) factor", preferredStyle: .alert)
let alert = UIAlertController(title: "Error", message: "No handler for \(factor.type.rawValue) factor", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
self.cancelTransaction()
Expand All @@ -281,9 +281,6 @@ class ViewController: UIViewController {
},
onError: { error in
self.handleError(error)
},
onFactorStatusUpdate: { factorResult in
self.updateStatus(status: self.currentStatus, factorResult: factorResult)
})
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in
Expand All @@ -297,9 +294,7 @@ class ViewController: UIViewController {
self.handleStatus(status: status)
}, onError: { error in
self.handleError(error)
}) { factorResult in
self.updateStatus(status: status, factorResult: factorResult)
}
})
}
}
}
Expand All @@ -315,9 +310,6 @@ class ViewController: UIViewController {
},
onError: { error in
self.handleError(error)
},
onFactorStatusUpdate: { factorResult in
self.updateStatus(status: self.currentStatus, factorResult: factorResult)
})
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in
Expand All @@ -337,9 +329,6 @@ class ViewController: UIViewController {
},
onError: { error in
self.handleError(error)
},
onFactorStatusUpdate: { factorResult in
self.updateStatus(status: self.currentStatus, factorResult: factorResult)
})
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in
Expand All @@ -359,9 +348,6 @@ class ViewController: UIViewController {
},
onError: { error in
self.handleError(error)
},
onFactorStatusUpdate: { factorResult in
self.updateStatus(status: self.currentStatus, factorResult: factorResult)
})
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in
Expand All @@ -371,14 +357,18 @@ class ViewController: UIViewController {
}

func handlePushChallenge(factor: OktaFactorPush) {

factor.verify(onStatusChange: { (status) in
self.handleStatus(status: status)
if status.factorResult == .waiting {
self.updateStatus(status: status)
DispatchQueue.main.asyncAfter(deadline:.now() + 5.0) {
self.handlePushChallenge(factor: factor)
}
} else {
self.handleStatus(status: status)
}
}, onError: { (error) in
self.handleError(error)
}) { _ in
self.updateStatus(status: self.currentStatus)
}
})
}

func cancelTransaction() {
Expand Down
69 changes: 34 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,7 @@ Activates sms, call and token:software:totp factors to complete the enrollment p
```swift
open func activateFactor(passCode: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```

#### resendFactor
Expand Down Expand Up @@ -455,8 +454,7 @@ Verifies an answer to a question factor or OTP code for SMS/Call/Totp/Token fact
open func verifyFactor(passCode: String?,
answerToSecurityQuestion: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```

#### resendFactor
Expand Down Expand Up @@ -663,8 +661,7 @@ Enrolls a user with the Okta SMS factor and an SMS profile. A text message with
```swift
public func enroll(phoneNumber: String?,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/Enrollment/MFAEnrollmentViewController.swift#L103-L117)

Expand All @@ -675,8 +672,7 @@ Activates an SMS factor by verifying the OTP.
```swift
public func activate(passCode: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/MFASMSViewController.swift#L88-L98)

Expand All @@ -697,8 +693,7 @@ Verifies an enrolled SMS factor by verifying the OTP.
```swift
public func verify(passCode: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/MFASMSViewController.swift#L73-L84)

Expand All @@ -711,8 +706,7 @@ Enrolls a user with the Okta call factor and a Call profile. A voice call with a
```swift
public func enroll(phoneNumber: String?,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/Enrollment/MFAEnrollmentViewController.swift#L103-L117)

Expand All @@ -723,8 +717,7 @@ Activates a call factor by verifying the OTP.
```swift
public func activate(passCode: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/MFASMSViewController.swift#L88-L98)

Expand All @@ -744,8 +737,7 @@ Verifies an enrolled call factor by verifying the OTP.
```swift
public func verify(passCode: String?,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/MFASMSViewController.swift#L73-L84)

Expand All @@ -765,13 +757,11 @@ Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign
#### [activate](https://developer.okta.com/docs/api/resources/authn/#activate-call-factor)

Activation of push factors are asynchronous and must be polled for completion when the factorResult returns a WAITING status.
**NOTE:** Polling is implemented by the SDK (default timer is 5 seconds), so you don't need to implement it in your code. SDK will notify your application about the last factor status via `onFactorStatusUpdate` closure.
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.

```swift
public func activate(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/Enrollment/MFActivatePushTotpViewController.swift#L48-L62)

Expand Down Expand Up @@ -807,14 +797,29 @@ Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign
#### [verify](https://developer.okta.com/docs/api/resources/authn/#verify-push-factor)

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.
**NOTE:** Polling is implemented by the SDK (default interval is 5 seconds), so you don't need to implement it in your code. SDK will notify your application about the last factor status via `onFactorStatusUpdate` closure.

```swift
public func verify(onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```
Implement polling logic in order to get updates for the push factor result.
**NOTE** Don't call `verify` function too often, keep polling interval within 3-5 seconds to not cause extra load on the server
Example of polling logic:
```swift
func handlePushChallenge(factor: OktaFactorPush) {
factor.verify(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)
})
}
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/MFAPushViewController.swift#L44-L57)

### OktaFactorTotp

Expand All @@ -835,8 +840,7 @@ Activates a token:software:totp factor by verifying the OTP (passcode).
```swift
public func activate(passCode: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/Enrollment/MFActivatePushTotpViewController.swift#L75-L90)

Expand All @@ -858,8 +862,7 @@ Verifies an OTP for a token:software:totp factor.
```swift
public func verify(passCode: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/MFATOTPViewController.swift#L39-L50)

Expand Down Expand Up @@ -895,8 +898,7 @@ Selects a question factor from the list of required factors and verifies the ans
```swift
public func select(answerToSecurityQuestion: String,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/MFAViewController.swift#L65-L71)

Expand All @@ -907,8 +909,7 @@ Verifies an answer to a question factor.
```swift
public func verify(answerToSecurityQuestion: String,
onStatusChange: @escaping (OktaAuthStatus) -> Void,
onError: @escaping (OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (OktaError) -> Void)
```
Sample app [example](https://github.com/okta/samples-ios/blob/master/custom-sign-in/OktaNativeLogin/MFA/MFASecurityQuestionViewController.swift#L36-L47)

Expand Down Expand Up @@ -943,8 +944,7 @@ Verifies a passcode to a token factor.
```swift
public func verify(passCode: String,
onStatusChange: @escaping (_ newStatus: OktaAuthStatus) -> Void,
onError: @escaping (_ error: OktaError) -> Void,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (_ error: OktaError) -> Void)
```

### OktaFactorOther
Expand All @@ -959,8 +959,7 @@ 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,
onFactorStatusUpdate: ((_ state: OktaAPISuccessResponse.FactorResult) -> Void)? = nil)
onError: @escaping (OktaError) -> Void)
```


Expand Down
Loading

0 comments on commit 7b7b9d6

Please sign in to comment.