(w http.ResponseWriter, r *http.Request, params *VerifyFactorParams)
| 867 | } |
| 868 | |
| 869 | func (a *API) verifyWebAuthnFactor(w http.ResponseWriter, r *http.Request, params *VerifyFactorParams) error { |
| 870 | ctx := r.Context() |
| 871 | config := a.config |
| 872 | user := getUser(ctx) |
| 873 | factor := getFactor(ctx) |
| 874 | db := a.db.WithContext(ctx) |
| 875 | |
| 876 | var webAuthn *webauthn.WebAuthn |
| 877 | var credential *webauthn.Credential |
| 878 | var err error |
| 879 | |
| 880 | switch { |
| 881 | case params.WebAuthn == nil: |
| 882 | return apierrors.NewBadRequestError(apierrors.ErrorCodeValidationFailed, "WebAuthn config required") |
| 883 | case params.WebAuthn.Type != "create" && params.WebAuthn.Type != "request": |
| 884 | return apierrors.NewBadRequestError(apierrors.ErrorCodeValidationFailed, "WebAuthn type must be create or request") |
| 885 | case params.WebAuthn.CredentialResponse == nil: |
| 886 | return apierrors.NewBadRequestError(apierrors.ErrorCodeValidationFailed, "credential_response required") |
| 887 | default: |
| 888 | webAuthn, err = a.getWebAuthnMFA() |
| 889 | if err != nil { |
| 890 | return err |
| 891 | } |
| 892 | } |
| 893 | |
| 894 | challenge, err := a.validateChallenge(r, db, factor, params.ChallengeID) |
| 895 | if err != nil { |
| 896 | return err |
| 897 | } |
| 898 | webAuthnSession := *challenge.WebAuthnSessionData.SessionData |
| 899 | |
| 900 | var parsedResponse interface{} |
| 901 | switch params.WebAuthn.Type { |
| 902 | case "create": |
| 903 | parsedResponse, err = wbnprotocol.ParseCredentialCreationResponseBody(bytes.NewReader(params.WebAuthn.CredentialResponse)) |
| 904 | if err != nil { |
| 905 | return apierrors.NewBadRequestError(apierrors.ErrorCodeValidationFailed, "Invalid credential_response") |
| 906 | } |
| 907 | credential, err = webAuthn.CreateCredential(user, webAuthnSession, parsedResponse.(*wbnprotocol.ParsedCredentialCreationData)) |
| 908 | if err != nil { |
| 909 | return err |
| 910 | } |
| 911 | |
| 912 | case "request": |
| 913 | parsedResponse, err = wbnprotocol.ParseCredentialRequestResponseBody(bytes.NewReader(params.WebAuthn.CredentialResponse)) |
| 914 | if err != nil { |
| 915 | return apierrors.NewBadRequestError(apierrors.ErrorCodeValidationFailed, "Invalid credential_response") |
| 916 | } |
| 917 | credential, err = webAuthn.ValidateLogin(user, webAuthnSession, parsedResponse.(*wbnprotocol.ParsedCredentialAssertionData)) |
| 918 | if err != nil { |
| 919 | return apierrors.NewInternalServerError("Failed to validate WebAuthn MFA response").WithInternalError(err) |
| 920 | } |
| 921 | } |
| 922 | |
| 923 | // Once the challenge is validated, we consume the challenge |
| 924 | if err := db.Destroy(challenge); err != nil { |
| 925 | return apierrors.NewInternalServerError("Database error deleting challenge").WithInternalError(err) |
| 926 | } |
no test coverage detected