okta/okta-auth-swift

Triggering non 'success' OktaAuthSdk status

t3ddyK opened this issue · 2 comments

I am trying to understand how the success statuses are triggered and their particular use case.

Say for example I have a method that invokes the below:

        OktaAuthSdk.authenticate(
            with: instance,
            username: username,
            password: password,
            onStatusChange: { status in self.onStatusChange(status, completion) },
            onError: { error in self.onAuthError(error, completion) }
        )

The methods called are:
onStatusChange

    private func onStatusChange(_ status: OktaAuthStatus, _ completion: @escaping ((Error?) -> Void)) {
        switch status.statusType {
        case .success:
            let state: OktaAuthStatusSuccess = status as! OktaAuthStatusSuccess
            handleSuccessStatus(state, completion)
            
        case .passwordWarning:
            let state: OktaAuthStatusPasswordWarning = status as! OktaAuthStatusPasswordWarning
            handlePasswordWarning(state, completion)
            
        case .passwordExpired:
            let state: OktaAuthStatusPasswordExpired = status as! OktaAuthStatusPasswordExpired
            handleChangePassword(state, completion)
            
        case .MFAEnroll:
            let state: OktaAuthStatusFactorEnroll = status as! OktaAuthStatusFactorEnroll
            handleEnrollment(state, completion)
            
        case .MFAEnrollActivate:
            let state: OktaAuthStatusFactorEnrollActivate = status as! OktaAuthStatusFactorEnrollActivate
            handleActivateEnrollment(state, completion)
            
        case .MFARequired:
            let state: OktaAuthStatusFactorRequired = status as! OktaAuthStatusFactorRequired
            handleFactorRequired(state, completion)
            
        case .MFAChallenge:
            let state: OktaAuthStatusFactorChallenge = status as! OktaAuthStatusFactorChallenge
            handleFactorChallenge(state, completion)
            
        case .recovery:
            let state: OktaAuthStatusRecovery = status as! OktaAuthStatusRecovery
            handleRecovery(state, completion)
            
        case .recoveryChallenge:
            let state: OktaAuthStatusRecoveryChallenge = status as! OktaAuthStatusRecoveryChallenge
            handleRecoveryChallenge(state, completion)
            
        case .passwordReset:
            let state: OktaAuthStatusPasswordReset = status as! OktaAuthStatusPasswordReset
            handlePasswordReset(state, completion)
            
        case .lockedOut:
            let state: OktaAuthStatusLockedOut = status as! OktaAuthStatusLockedOut
            handleLockedOut(state, completion)
            
        case .unauthenticated:
            let state: OktaAuthStatusUnauthenticated = status as! OktaAuthStatusUnauthenticated
            handleUnauthenticated(state, completion)
        case .unknown:
            break
        }
    }

and also onAuthError

    private func onAuthError(_ error: OktaError, _ completion: @escaping ((Error?) -> Void)) {
        print(error)
        print("\(#function) not implemented")
    }

If for example I fail login, wrong username and password or my account is suspended, onAuthError is triggered w/ Authentication failed

Screenshot 2019-08-05 at 09 14 40

However there is no call on the .unauthenticated flow of my status change switch statement.

In what scenario should these statuses be returned?

Hi @t3ddyK,

Each status class in SDK represents status in authentication state machine on server side. Please take a look on state machine diagram: https://camo.githubusercontent.com/55f77b31bb3e54665186b1aee1c2f4f65f7f8ed7/68747470733a2f2f646576656c6f7065722e6f6b74612e636f6d2f696d672f617574682d73746174652d6d6f64656c2e706e67

Your authentication journey starts with Unauthenticated status and according to the diagram you can't return to this status from any other statuses. So you don't need handler for this status. State machine doesn't switch states in case of errors. In your example if you fail to login server doesn't move to the next status. It returns error and keeps state machine in current state which is Unauthenticated state in your case.

Other states are required for different authentication cases. For example if your org wants to use multifactor authentication(MFA), then you have to implement MFAEnroll, MFAEnrollActivate, MFARequired and MFAChallenge. So everything depends on your org and it's current configuration.
For the basic configuration you usually must implement the following status handlers:
.success - successful login. Retrieve session token and exchange it to access token via Okta OIDC SDK
.passwordReset - warn customer that password is about to expire. Customer can change password now or skip it
.passwordExpired - warn customer that password is expired. Customer must change password

More info about statuses you can find in developer documentation: https://developer.okta.com/docs/reference/api/authn/

Also please check our sample application that shows how to handle all available statues: https://github.com/okta/samples-ios/tree/master/custom-sign-in

Please let me know if you have any other questions

Ildar

Amazing, thank you I really appreciates this