github.com/greenpau/go-authcrunch@v1.1.4/pkg/authn/api_test_user_webauthn_token.go (about) 1 // Copyright 2024 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package authn 16 17 import ( 18 "context" 19 "net/http" 20 21 "github.com/greenpau/go-authcrunch/pkg/authn/enums/operator" 22 "github.com/greenpau/go-authcrunch/pkg/identity" 23 "github.com/greenpau/go-authcrunch/pkg/ids" 24 "github.com/greenpau/go-authcrunch/pkg/requests" 25 "github.com/greenpau/go-authcrunch/pkg/user" 26 "go.uber.org/zap" 27 ) 28 29 // TestUserWebAuthnToken tests WebAuthn token. 30 func (p *Portal) TestUserWebAuthnToken( 31 ctx context.Context, 32 w http.ResponseWriter, 33 r *http.Request, 34 rr *requests.Request, 35 parsedUser *user.User, 36 resp map[string]interface{}, 37 usr *user.User, 38 backend ids.IdentityStore, 39 bodyData map[string]interface{}) error { 40 41 rr.MfaToken.Type = "u2f" 42 43 // Extract data. 44 if v, exists := bodyData["id"]; exists { 45 switch keyID := v.(type) { 46 case string: 47 rr.MfaToken.ID = keyID 48 default: 49 resp["message"] = "Profile API did find key id in the request payload, but it is malformed" 50 return handleAPIProfileResponse(w, rr, http.StatusBadRequest, resp) 51 } 52 } else { 53 resp["message"] = "Profile API did not find key id in the request payload" 54 return handleAPIProfileResponse(w, rr, http.StatusBadRequest, resp) 55 } 56 57 if v, exists := bodyData["webauthn_challenge"]; exists { 58 switch exp := v.(type) { 59 case string: 60 rr.WebAuthn.Challenge = exp 61 default: 62 resp["message"] = "Profile API did find key webauthn_challenge in the request payload, but it is malformed" 63 return handleAPIProfileResponse(w, rr, http.StatusBadRequest, resp) 64 } 65 } else { 66 resp["message"] = "Profile API did not find key webauthn_challenge in the request payload" 67 return handleAPIProfileResponse(w, rr, http.StatusBadRequest, resp) 68 } 69 70 if v, exists := bodyData["webauthn_request"]; exists { 71 switch exp := v.(type) { 72 case string: 73 rr.WebAuthn.Request = exp 74 default: 75 resp["message"] = "Profile API did find key webauthn_request in the request payload, but it is malformed" 76 return handleAPIProfileResponse(w, rr, http.StatusBadRequest, resp) 77 } 78 } else { 79 resp["message"] = "Profile API did not find key webauthn_request in the request payload" 80 return handleAPIProfileResponse(w, rr, http.StatusBadRequest, resp) 81 } 82 83 // Get MFA Token 84 if err := backend.Request(operator.GetMfaToken, rr); err != nil { 85 resp["message"] = "Profile API failed to get token" 86 return handleAPIProfileResponse(w, rr, http.StatusInternalServerError, resp) 87 } 88 89 token := rr.Response.Payload.(*identity.MfaToken) 90 91 wr, err := token.WebAuthnRequest(rr.WebAuthn.Request) 92 if err != nil { 93 p.logger.Warn( 94 "u2f token validation failed", 95 zap.String("session_id", rr.Upstream.SessionID), 96 zap.String("request_id", rr.ID), 97 zap.Any("webauthn_request", wr), 98 zap.Any("error", err), 99 ) 100 resp["message"] = "Profile API failed to validate U2F token" 101 return handleAPIProfileResponse(w, rr, http.StatusBadRequest, resp) 102 } 103 104 p.logger.Debug( 105 "Profile API successfully validated u2f token", 106 zap.String("session_id", rr.Upstream.SessionID), 107 zap.String("request_id", rr.ID), 108 zap.Any("webauthn_request", wr), 109 ) 110 111 respData := make(map[string]interface{}) 112 respData["success"] = true 113 resp["entry"] = respData 114 return handleAPIProfileResponse(w, rr, http.StatusOK, resp) 115 }