1package jwt 2 3import ( 4 "net/http" 5 "net/url" 6 "strconv" 7 "strings" 8 9 "github.com/lestrrat-go/jwx/internal/pool" 10 "github.com/pkg/errors" 11) 12 13// ParseHeader parses a JWT stored in a http.Header. 14// 15// For the header "Authorization", it will strip the prefix "Bearer " and will 16// treat the remaining value as a JWT. 17func ParseHeader(hdr http.Header, name string, options ...ParseOption) (Token, error) { 18 key := http.CanonicalHeaderKey(name) 19 v := strings.TrimSpace(hdr.Get(key)) 20 if v == "" { 21 return nil, errors.Errorf(`empty header (%s)`, key) 22 } 23 24 if key == "Authorization" { 25 // Authorization header is an exception. We strip the "Bearer " from 26 // the prefix 27 v = strings.TrimSpace(strings.TrimPrefix(v, "Bearer")) 28 } 29 30 return ParseString(v, options...) 31} 32 33// ParseForm parses a JWT stored in a url.Value. 34func ParseForm(values url.Values, name string, options ...ParseOption) (Token, error) { 35 v := strings.TrimSpace(values.Get(name)) 36 if v == "" { 37 return nil, errors.Errorf(`empty value (%s)`, name) 38 } 39 40 return ParseString(v, options...) 41} 42 43// ParseRequest searches a http.Request object for a JWT token. 44// 45// Specifying WithHeaderKey() will tell it to search under a specific 46// header key. Specifying WithFormKey() will tell it to search under 47// a specific form field. 48// 49// By default, "Authorization" header will be searched. 50// 51// If WithHeaderKey() is used, you must explicitly re-enable searching for "Authorization" header. 52// 53// # searches for "Authorization" 54// jwt.ParseRequest(req) 55// 56// # searches for "x-my-token" ONLY. 57// jwt.ParseRequest(req, http.WithHeaderKey("x-my-token")) 58// 59// # searches for "Authorization" AND "x-my-token" 60// jwt.ParseRequest(req, http.WithHeaderKey("Authorization"), http.WithHeaderKey("x-my-token")) 61func ParseRequest(req *http.Request, options ...ParseOption) (Token, error) { 62 var hdrkeys []string 63 var formkeys []string 64 var parseOptions []ParseOption 65 for _, option := range options { 66 switch option.Ident() { 67 case identHeaderKey{}: 68 hdrkeys = append(hdrkeys, option.Value().(string)) 69 case identFormKey{}: 70 formkeys = append(formkeys, option.Value().(string)) 71 default: 72 parseOptions = append(parseOptions, option) 73 } 74 } 75 if len(hdrkeys) == 0 { 76 hdrkeys = append(hdrkeys, "Authorization") 77 } 78 79 mhdrs := pool.GetKeyToErrorMap() 80 defer pool.ReleaseKeyToErrorMap(mhdrs) 81 mfrms := pool.GetKeyToErrorMap() 82 defer pool.ReleaseKeyToErrorMap(mfrms) 83 84 for _, hdrkey := range hdrkeys { 85 // Check presence via a direct map lookup 86 if _, ok := req.Header[http.CanonicalHeaderKey(hdrkey)]; !ok { 87 // if non-existent, not error 88 continue 89 } 90 91 tok, err := ParseHeader(req.Header, hdrkey, parseOptions...) 92 if err != nil { 93 mhdrs[hdrkey] = err 94 continue 95 } 96 return tok, nil 97 } 98 99 if cl := req.ContentLength; cl > 0 { 100 if err := req.ParseForm(); err != nil { 101 return nil, errors.Wrap(err, `failed to parse form`) 102 } 103 } 104 105 for _, formkey := range formkeys { 106 // Check presence via a direct map lookup 107 if _, ok := req.Form[formkey]; !ok { 108 // if non-existent, not error 109 continue 110 } 111 112 tok, err := ParseForm(req.Form, formkey, parseOptions...) 113 if err != nil { 114 mfrms[formkey] = err 115 continue 116 } 117 return tok, nil 118 } 119 120 // Everything below is a preulde to error reporting. 121 var triedHdrs strings.Builder 122 for i, hdrkey := range hdrkeys { 123 if i > 0 { 124 triedHdrs.WriteString(", ") 125 } 126 triedHdrs.WriteString(strconv.Quote(hdrkey)) 127 } 128 129 var triedForms strings.Builder 130 for i, formkey := range formkeys { 131 if i > 0 { 132 triedForms.WriteString(", ") 133 } 134 triedForms.WriteString(strconv.Quote(formkey)) 135 } 136 137 var b strings.Builder 138 b.WriteString(`failed to find a valid token in any location of the request (tried: [header keys: `) 139 b.WriteString(triedHdrs.String()) 140 b.WriteByte(']') 141 if triedForms.Len() > 0 { 142 b.WriteString(", form keys: [") 143 b.WriteString(triedForms.String()) 144 b.WriteByte(']') 145 } 146 b.WriteByte(')') 147 148 lmhdrs := len(mhdrs) 149 lmfrms := len(mfrms) 150 if lmhdrs > 0 || lmfrms > 0 { 151 b.WriteString(". Additionally, errors were encountered during attempts to parse") 152 153 if lmhdrs > 0 { 154 b.WriteString(" headers: (") 155 count := 0 156 for hdrkey, err := range mhdrs { 157 if count > 0 { 158 b.WriteString(", ") 159 } 160 b.WriteString("[header key: ") 161 b.WriteString(strconv.Quote(hdrkey)) 162 b.WriteString(", error: ") 163 b.WriteString(strconv.Quote(err.Error())) 164 b.WriteString("]") 165 count++ 166 } 167 b.WriteString(")") 168 } 169 170 if lmfrms > 0 { 171 count := 0 172 b.WriteString(" forms: (") 173 for formkey, err := range mfrms { 174 if count > 0 { 175 b.WriteString(", ") 176 } 177 b.WriteString("[form key: ") 178 b.WriteString(strconv.Quote(formkey)) 179 b.WriteString(", error: ") 180 b.WriteString(strconv.Quote(err.Error())) 181 b.WriteString("]") 182 count++ 183 } 184 } 185 } 186 return nil, errors.New(b.String()) 187} 188