github.com/lestrrat-go/jwx/v2@v2.0.21/jwt/http.go (about)

     1  package jwt
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/url"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/lestrrat-go/jwx/v2/internal/pool"
    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.
    17  func 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, fmt.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.
    34  func ParseForm(values url.Values, name string, options ...ParseOption) (Token, error) {
    35  	v := strings.TrimSpace(values.Get(name))
    36  	if v == "" {
    37  		return nil, fmt.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, jwt.WithHeaderKey("x-my-token"))
    58  //
    59  //	# searches for "Authorization" AND "x-my-token"
    60  //	jwt.ParseRequest(req, jwt.WithHeaderKey("Authorization"), jwt.WithHeaderKey("x-my-token"))
    61  func 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  		//nolint:forcetypeassert
    67  		switch option.Ident() {
    68  		case identHeaderKey{}:
    69  			hdrkeys = append(hdrkeys, option.Value().(string))
    70  		case identFormKey{}:
    71  			formkeys = append(formkeys, option.Value().(string))
    72  		default:
    73  			parseOptions = append(parseOptions, option)
    74  		}
    75  	}
    76  	if len(hdrkeys) == 0 {
    77  		hdrkeys = append(hdrkeys, "Authorization")
    78  	}
    79  
    80  	mhdrs := pool.GetKeyToErrorMap()
    81  	defer pool.ReleaseKeyToErrorMap(mhdrs)
    82  	mfrms := pool.GetKeyToErrorMap()
    83  	defer pool.ReleaseKeyToErrorMap(mfrms)
    84  
    85  	for _, hdrkey := range hdrkeys {
    86  		// Check presence via a direct map lookup
    87  		if _, ok := req.Header[http.CanonicalHeaderKey(hdrkey)]; !ok {
    88  			// if non-existent, not error
    89  			continue
    90  		}
    91  
    92  		tok, err := ParseHeader(req.Header, hdrkey, parseOptions...)
    93  		if err != nil {
    94  			mhdrs[hdrkey] = err
    95  			continue
    96  		}
    97  		return tok, nil
    98  	}
    99  
   100  	if cl := req.ContentLength; cl > 0 {
   101  		if err := req.ParseForm(); err != nil {
   102  			return nil, fmt.Errorf(`failed to parse form: %w`, err)
   103  		}
   104  	}
   105  
   106  	for _, formkey := range formkeys {
   107  		// Check presence via a direct map lookup
   108  		if _, ok := req.Form[formkey]; !ok {
   109  			// if non-existent, not error
   110  			continue
   111  		}
   112  
   113  		tok, err := ParseForm(req.Form, formkey, parseOptions...)
   114  		if err != nil {
   115  			mfrms[formkey] = err
   116  			continue
   117  		}
   118  		return tok, nil
   119  	}
   120  
   121  	// Everything below is a preulde to error reporting.
   122  	var triedHdrs strings.Builder
   123  	for i, hdrkey := range hdrkeys {
   124  		if i > 0 {
   125  			triedHdrs.WriteString(", ")
   126  		}
   127  		triedHdrs.WriteString(strconv.Quote(hdrkey))
   128  	}
   129  
   130  	var triedForms strings.Builder
   131  	for i, formkey := range formkeys {
   132  		if i > 0 {
   133  			triedForms.WriteString(", ")
   134  		}
   135  		triedForms.WriteString(strconv.Quote(formkey))
   136  	}
   137  
   138  	var b strings.Builder
   139  	b.WriteString(`failed to find a valid token in any location of the request (tried: [header keys: `)
   140  	b.WriteString(triedHdrs.String())
   141  	b.WriteByte(']')
   142  	if triedForms.Len() > 0 {
   143  		b.WriteString(", form keys: [")
   144  		b.WriteString(triedForms.String())
   145  		b.WriteByte(']')
   146  	}
   147  	b.WriteByte(')')
   148  
   149  	lmhdrs := len(mhdrs)
   150  	lmfrms := len(mfrms)
   151  	if lmhdrs > 0 || lmfrms > 0 {
   152  		b.WriteString(". Additionally, errors were encountered during attempts to parse")
   153  
   154  		if lmhdrs > 0 {
   155  			b.WriteString(" headers: (")
   156  			count := 0
   157  			for hdrkey, err := range mhdrs {
   158  				if count > 0 {
   159  					b.WriteString(", ")
   160  				}
   161  				b.WriteString("[header key: ")
   162  				b.WriteString(strconv.Quote(hdrkey))
   163  				b.WriteString(", error: ")
   164  				b.WriteString(strconv.Quote(err.Error()))
   165  				b.WriteString("]")
   166  				count++
   167  			}
   168  			b.WriteString(")")
   169  		}
   170  
   171  		if lmfrms > 0 {
   172  			count := 0
   173  			b.WriteString(" forms: (")
   174  			for formkey, err := range mfrms {
   175  				if count > 0 {
   176  					b.WriteString(", ")
   177  				}
   178  				b.WriteString("[form key: ")
   179  				b.WriteString(strconv.Quote(formkey))
   180  				b.WriteString(", error: ")
   181  				b.WriteString(strconv.Quote(err.Error()))
   182  				b.WriteString("]")
   183  				count++
   184  			}
   185  		}
   186  	}
   187  	return nil, fmt.Errorf(b.String())
   188  }