Initial QSfera import
This commit is contained in:
+295
@@ -0,0 +1,295 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/pool"
|
||||
"github.com/lestrrat-go/jwx/v3/internal/tokens"
|
||||
)
|
||||
|
||||
// ParseCookie parses a JWT stored in a http.Cookie with the given name.
|
||||
// If the specified cookie is not found, http.ErrNoCookie is returned.
|
||||
func ParseCookie(req *http.Request, name string, options ...ParseOption) (Token, error) {
|
||||
var dst **http.Cookie
|
||||
for _, option := range options {
|
||||
switch option.Ident() {
|
||||
case identCookie{}:
|
||||
if err := option.Value(&dst); err != nil {
|
||||
return nil, fmt.Errorf(`jws.ParseCookie: value to option WithCookie must be **http.Cookie: %w`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cookie, err := req.Cookie(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tok, err := ParseString(cookie.Value, options...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`jws.ParseCookie: failed to parse token stored in cookie: %w`, err)
|
||||
}
|
||||
|
||||
if dst != nil {
|
||||
*dst = cookie
|
||||
}
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
// 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, fmt.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"))
|
||||
}
|
||||
|
||||
tok, err := ParseString(v, options...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse token stored in header (%s): %w`, key, err)
|
||||
}
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
// 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, fmt.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.
|
||||
//
|
||||
// If none of jwt.WithHeaderKey()/jwt.WithCookieKey()/jwt.WithFormKey() is
|
||||
// used, "Authorization" header will be searched. If any of these options
|
||||
// are specified, you must explicitly re-enable searching for "Authorization" header
|
||||
// if you also want to search for it.
|
||||
//
|
||||
// # 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"))
|
||||
//
|
||||
// Cookies are searched using (http.Request).Cookie(). If you have multiple
|
||||
// cookies with the same name, and you want to search for a specific one that
|
||||
// (http.Request).Cookie() would not return, you will need to implement your
|
||||
// own logic to extract the cookie and use jwt.ParseString().
|
||||
func ParseRequest(req *http.Request, options ...ParseOption) (Token, error) {
|
||||
var hdrkeys []string
|
||||
var formkeys []string
|
||||
var cookiekeys []string
|
||||
var parseOptions []ParseOption
|
||||
for _, option := range options {
|
||||
switch option.Ident() {
|
||||
case identHeaderKey{}:
|
||||
var v string
|
||||
if err := option.Value(&v); err != nil {
|
||||
return nil, fmt.Errorf(`jws.ParseRequest: value to option WithHeaderKey must be string: %w`, err)
|
||||
}
|
||||
hdrkeys = append(hdrkeys, v)
|
||||
case identFormKey{}:
|
||||
var v string
|
||||
if err := option.Value(&v); err != nil {
|
||||
return nil, fmt.Errorf(`jws.ParseRequest: value to option WithFormKey must be string: %w`, err)
|
||||
}
|
||||
formkeys = append(formkeys, v)
|
||||
case identCookieKey{}:
|
||||
var v string
|
||||
if err := option.Value(&v); err != nil {
|
||||
return nil, fmt.Errorf(`jws.ParseRequest: value to option WithCookieKey must be string: %w`, err)
|
||||
}
|
||||
cookiekeys = append(cookiekeys, v)
|
||||
default:
|
||||
parseOptions = append(parseOptions, option)
|
||||
}
|
||||
}
|
||||
|
||||
if len(hdrkeys) == 0 && len(formkeys) == 0 && len(cookiekeys) == 0 {
|
||||
hdrkeys = append(hdrkeys, "Authorization")
|
||||
}
|
||||
|
||||
mhdrs := pool.KeyToErrorMap().Get()
|
||||
defer pool.KeyToErrorMap().Put(mhdrs)
|
||||
mfrms := pool.KeyToErrorMap().Get()
|
||||
defer pool.KeyToErrorMap().Put(mfrms)
|
||||
mcookies := pool.KeyToErrorMap().Get()
|
||||
defer pool.KeyToErrorMap().Put(mcookies)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
for _, name := range cookiekeys {
|
||||
tok, err := ParseCookie(req, name, parseOptions...)
|
||||
if err != nil {
|
||||
if err == http.ErrNoCookie {
|
||||
// not fatal
|
||||
mcookies[name] = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
if cl := req.ContentLength; cl > 0 {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, fmt.Errorf(`failed to parse form: %w`, err)
|
||||
}
|
||||
}
|
||||
|
||||
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 prelude 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 triedCookies strings.Builder
|
||||
for i, cookiekey := range cookiekeys {
|
||||
if i > 0 {
|
||||
triedCookies.WriteString(", ")
|
||||
}
|
||||
triedCookies.WriteString(strconv.Quote(cookiekey))
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString(`failed to find a valid token in any location of the request (tried: `)
|
||||
olen := b.Len()
|
||||
if triedHdrs.Len() > 0 {
|
||||
b.WriteString(`header keys: [`)
|
||||
b.WriteString(triedHdrs.String())
|
||||
b.WriteByte(tokens.CloseSquareBracket)
|
||||
}
|
||||
if triedForms.Len() > 0 {
|
||||
if b.Len() > olen {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
b.WriteString("form keys: [")
|
||||
b.WriteString(triedForms.String())
|
||||
b.WriteByte(tokens.CloseSquareBracket)
|
||||
}
|
||||
|
||||
if triedCookies.Len() > 0 {
|
||||
if b.Len() > olen {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
b.WriteString("cookie keys: [")
|
||||
b.WriteString(triedCookies.String())
|
||||
b.WriteByte(tokens.CloseSquareBracket)
|
||||
}
|
||||
b.WriteByte(')')
|
||||
|
||||
lmhdrs := len(mhdrs)
|
||||
lmfrms := len(mfrms)
|
||||
lmcookies := len(mcookies)
|
||||
var errors []any
|
||||
if lmhdrs > 0 || lmfrms > 0 || lmcookies > 0 {
|
||||
b.WriteString(". Additionally, errors were encountered during attempts to verify using:")
|
||||
|
||||
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: %w]")
|
||||
errors = append(errors, err)
|
||||
count++
|
||||
}
|
||||
b.WriteString(")")
|
||||
}
|
||||
|
||||
if lmcookies > 0 {
|
||||
count := 0
|
||||
b.WriteString(" cookies: (")
|
||||
for cookiekey, err := range mcookies {
|
||||
if count > 0 {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
b.WriteString("[cookie key: ")
|
||||
b.WriteString(strconv.Quote(cookiekey))
|
||||
b.WriteString(", error: %w]")
|
||||
errors = append(errors, err)
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
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: %w]")
|
||||
errors = append(errors, err)
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf(b.String(), errors...)
|
||||
}
|
||||
Reference in New Issue
Block a user