http.go 4.7 KB

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