| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- package jwt
- import (
- "net/http"
- "net/url"
- "strconv"
- "strings"
- "github.com/lestrrat-go/jwx/internal/pool"
- "github.com/pkg/errors"
- )
- // ParseHeader parses a JWT stored in a http.Header.
- //
- // For the header "Authorization", it will strip the prefix "Bearer " and will
- // treat the remaining value as a JWT.
- func ParseHeader(hdr http.Header, name string, options ...ParseOption) (Token, error) {
- key := http.CanonicalHeaderKey(name)
- v := strings.TrimSpace(hdr.Get(key))
- if v == "" {
- return nil, errors.Errorf(`empty header (%s)`, key)
- }
- if key == "Authorization" {
- // Authorization header is an exception. We strip the "Bearer " from
- // the prefix
- v = strings.TrimSpace(strings.TrimPrefix(v, "Bearer"))
- }
- return ParseString(v, options...)
- }
- // ParseForm parses a JWT stored in a url.Value.
- func ParseForm(values url.Values, name string, options ...ParseOption) (Token, error) {
- v := strings.TrimSpace(values.Get(name))
- if v == "" {
- return nil, errors.Errorf(`empty value (%s)`, name)
- }
- return ParseString(v, options...)
- }
- // ParseRequest searches a http.Request object for a JWT token.
- //
- // Specifying WithHeaderKey() will tell it to search under a specific
- // header key. Specifying WithFormKey() will tell it to search under
- // a specific form field.
- //
- // By default, "Authorization" header will be searched.
- //
- // If WithHeaderKey() is used, you must explicitly re-enable searching for "Authorization" header.
- //
- // # searches for "Authorization"
- // jwt.ParseRequest(req)
- //
- // # searches for "x-my-token" ONLY.
- // jwt.ParseRequest(req, jwt.WithHeaderKey("x-my-token"))
- //
- // # searches for "Authorization" AND "x-my-token"
- // jwt.ParseRequest(req, jwt.WithHeaderKey("Authorization"), jwt.WithHeaderKey("x-my-token"))
- func ParseRequest(req *http.Request, options ...ParseOption) (Token, error) {
- var hdrkeys []string
- var formkeys []string
- var parseOptions []ParseOption
- for _, option := range options {
- //nolint:forcetypeassert
- switch option.Ident() {
- case identHeaderKey{}:
- hdrkeys = append(hdrkeys, option.Value().(string))
- case identFormKey{}:
- formkeys = append(formkeys, option.Value().(string))
- default:
- parseOptions = append(parseOptions, option)
- }
- }
- if len(hdrkeys) == 0 {
- hdrkeys = append(hdrkeys, "Authorization")
- }
- mhdrs := pool.GetKeyToErrorMap()
- defer pool.ReleaseKeyToErrorMap(mhdrs)
- mfrms := pool.GetKeyToErrorMap()
- defer pool.ReleaseKeyToErrorMap(mfrms)
- for _, hdrkey := range hdrkeys {
- // Check presence via a direct map lookup
- if _, ok := req.Header[http.CanonicalHeaderKey(hdrkey)]; !ok {
- // if non-existent, not error
- continue
- }
- tok, err := ParseHeader(req.Header, hdrkey, parseOptions...)
- if err != nil {
- mhdrs[hdrkey] = err
- continue
- }
- return tok, nil
- }
- if cl := req.ContentLength; cl > 0 {
- if err := req.ParseForm(); err != nil {
- return nil, errors.Wrap(err, `failed to parse form`)
- }
- }
- for _, formkey := range formkeys {
- // Check presence via a direct map lookup
- if _, ok := req.Form[formkey]; !ok {
- // if non-existent, not error
- continue
- }
- tok, err := ParseForm(req.Form, formkey, parseOptions...)
- if err != nil {
- mfrms[formkey] = err
- continue
- }
- return tok, nil
- }
- // Everything below is a preulde to error reporting.
- var triedHdrs strings.Builder
- for i, hdrkey := range hdrkeys {
- if i > 0 {
- triedHdrs.WriteString(", ")
- }
- triedHdrs.WriteString(strconv.Quote(hdrkey))
- }
- var triedForms strings.Builder
- for i, formkey := range formkeys {
- if i > 0 {
- triedForms.WriteString(", ")
- }
- triedForms.WriteString(strconv.Quote(formkey))
- }
- var b strings.Builder
- b.WriteString(`failed to find a valid token in any location of the request (tried: [header keys: `)
- b.WriteString(triedHdrs.String())
- b.WriteByte(']')
- if triedForms.Len() > 0 {
- b.WriteString(", form keys: [")
- b.WriteString(triedForms.String())
- b.WriteByte(']')
- }
- b.WriteByte(')')
- lmhdrs := len(mhdrs)
- lmfrms := len(mfrms)
- if lmhdrs > 0 || lmfrms > 0 {
- b.WriteString(". Additionally, errors were encountered during attempts to parse")
- if lmhdrs > 0 {
- b.WriteString(" headers: (")
- count := 0
- for hdrkey, err := range mhdrs {
- if count > 0 {
- b.WriteString(", ")
- }
- b.WriteString("[header key: ")
- b.WriteString(strconv.Quote(hdrkey))
- b.WriteString(", error: ")
- b.WriteString(strconv.Quote(err.Error()))
- b.WriteString("]")
- count++
- }
- b.WriteString(")")
- }
- if lmfrms > 0 {
- count := 0
- b.WriteString(" forms: (")
- for formkey, err := range mfrms {
- if count > 0 {
- b.WriteString(", ")
- }
- b.WriteString("[form key: ")
- b.WriteString(strconv.Quote(formkey))
- b.WriteString(", error: ")
- b.WriteString(strconv.Quote(err.Error()))
- b.WriteString("]")
- count++
- }
- }
- }
- return nil, errors.New(b.String())
- }
|