@@ -759,76 +759,83 @@ func verifyMfa(oc *Client, oktaOrgHost string, loginDetails *creds.LoginDetails,
759759 return gjson .GetBytes (body , "sessionToken" ).String (), nil
760760
761761 case IdentifierFIDOWebAuthn :
762- var authErr error
763- var signedAssertion * SignedAssertion
764- challengeResponseBody := challengeContext .challengeResponseBody
765- lastMfaOption := mfaOption
766-
767- for {
768- nonce := gjson .Get (challengeResponseBody , "_embedded.factor._embedded.challenge.challenge" ).String ()
769- credentialID := gjson .Get (challengeResponseBody , "_embedded.factor.profile.credentialId" ).String ()
770- version := gjson .Get (challengeResponseBody , "_embedded.factor.profile.version" ).String ()
771-
772- fidoClient , err := NewFidoClient (
773- nonce ,
774- oktaOrgHost ,
775- version ,
776- credentialID ,
777- stateToken ,
778- new (U2FDeviceFinder ),
779- )
762+ return fidoWebAuthn (oc , oktaOrgHost , challengeContext , mfaOption , stateToken , mfaOptions , resp )
763+ }
780764
781- if err != nil {
782- return "" , err
783- }
765+ // catch all
766+ return "" , errors . New ( "no mfa options provided" )
767+ }
784768
785- signedAssertion , authErr = fidoClient .ChallengeU2F ()
769+ func fidoWebAuthn (oc * Client , oktaOrgHost string , challengeContext * mfaChallengeContext , mfaOption int , stateToken string , mfaOptions []string , resp string ) (string , error ) {
770+
771+ var signedAssertion * SignedAssertion
772+ challengeResponseBody := challengeContext .challengeResponseBody
773+ lastMfaOption := mfaOption
774+
775+ for {
776+ nonce := gjson .Get (challengeResponseBody , "_embedded.factor._embedded.challenge.challenge" ).String ()
777+ credentialID := gjson .Get (challengeResponseBody , "_embedded.factor.profile.credentialId" ).String ()
778+ version := gjson .Get (challengeResponseBody , "_embedded.factor.profile.version" ).String ()
779+
780+ fidoClient , err := NewFidoClient (
781+ nonce ,
782+ oktaOrgHost ,
783+ version ,
784+ credentialID ,
785+ stateToken ,
786+ new (U2FDeviceFinder ),
787+ )
788+ if err != nil {
789+ return "" , err
790+ }
786791
787- if _ , ok := authErr .( * u2fhost. BadKeyHandleError ); ok {
788- nextMfaOption := findMfaOption ( oc . mfa , mfaOptions , lastMfaOption )
789- if nextMfaOption <= lastMfaOption {
790- return "" , authErr
791- }
792- lastMfaOption = nextMfaOption
792+ signedAssertion , err = fidoClient . ChallengeU2F ()
793+ if err != nil {
794+ // if this error is not a bad key error we are done
795+ if _ , ok := err .( * u2fhost. BadKeyHandleError ); ! ok {
796+ return "" , errors . Wrap ( err , "failed to perform U2F challenge" )
797+ }
793798
794- nextChallengeContext , err := getMfaChallengeContext (oc , nextMfaOption , resp )
795- if err != nil {
796- return "" , err
797- }
798- challengeResponseBody = nextChallengeContext .challengeResponseBody
799- continue
799+ // check if there is another fido device and try that
800+ nextMfaOption := findMfaOption (oc .mfa , mfaOptions , lastMfaOption )
801+ if nextMfaOption <= lastMfaOption {
802+ return "" , errors .Wrap (err , "tried all MFA options" )
800803 }
804+ lastMfaOption = nextMfaOption
801805
806+ nextChallengeContext , err := getMfaChallengeContext (oc , nextMfaOption , resp )
802807 if err != nil {
803- return "" , err
808+ return "" , errors . Wrap ( err , "get mfa challenge failed for U2F device" )
804809 }
805-
806- break
810+ challengeResponseBody = nextChallengeContext . challengeResponseBody
811+ continue
807812 }
808813
809- payload , err := json .Marshal (signedAssertion )
810- if err != nil {
811- return "" , err
812- }
814+ break
815+ }
813816
814- webauthnCallback := gjson .Get (challengeResponseBody , "_links.next.href" ).String ()
815- req , err := http .NewRequest ("POST" , webauthnCallback , strings .NewReader (string (payload )))
816- if err != nil {
817- return "" , errors .Wrap (err , "error building authentication request" )
818- }
819- req .Header .Add ("Accept" , "application/json" )
820- req .Header .Add ("Content-Type" , "application/json" )
821- res , err := oc .client .Do (req )
822- if err != nil {
823- return "" , errors .Wrap (err , "error retrieving verify response" )
824- }
825- body , err := ioutil .ReadAll (res .Body )
826- if err != nil {
827- return "" , errors .Wrap (err , "error retrieving body from response" )
828- }
829- return gjson .GetBytes (body , "sessionToken" ).String (), nil
817+ payload , err := json .Marshal (signedAssertion )
818+ if err != nil {
819+ return "" , err
830820 }
831821
832- // catch all
833- return "" , errors .New ("no mfa options provided" )
822+ webauthnCallback := gjson .Get (challengeResponseBody , "_links.next.href" ).String ()
823+ req , err := http .NewRequest ("POST" , webauthnCallback , strings .NewReader (string (payload )))
824+ if err != nil {
825+ return "" , errors .Wrap (err , "error building authentication request" )
826+ }
827+
828+ req .Header .Add ("Accept" , "application/json" )
829+ req .Header .Add ("Content-Type" , "application/json" )
830+ res , err := oc .client .Do (req )
831+ if err != nil {
832+ return "" , errors .Wrap (err , "error retrieving verify response" )
833+ }
834+
835+ body , err := ioutil .ReadAll (res .Body )
836+ if err != nil {
837+ return "" , errors .Wrap (err , "error retrieving body from response" )
838+ }
839+
840+ return gjson .GetBytes (body , "sessionToken" ).String (), nil
834841}
0 commit comments