forked from golang-jwt/jwt
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'release_3_0_0' of github.com:dgrijalva/jwt-go into rele…
…ase_3_0_0
- Loading branch information
Showing
7 changed files
with
275 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// Utility package for extracting JWT tokens from | ||
// HTTP requests. | ||
// | ||
// The main function is ParseFromRequest and it's WithClaims variant. | ||
// See examples for how to use the various Extractor implementations | ||
// or roll your own. | ||
package request |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package request | ||
|
||
import ( | ||
"errors" | ||
"net/http" | ||
) | ||
|
||
// Errors | ||
var ( | ||
ErrNoTokenInRequest = errors.New("no token present in request") | ||
) | ||
|
||
// Interface for extracting a token from an HTTP request. | ||
// The ExtractToken method should return a token string or an error. | ||
// If no token is present, you must return ErrNoTokenInRequest. | ||
type Extractor interface { | ||
ExtractToken(*http.Request) (string, error) | ||
} | ||
|
||
// Extractor for finding a token in a header. Looks at each specified | ||
// header in order until there's a match | ||
type HeaderExtractor []string | ||
|
||
func (e HeaderExtractor) ExtractToken(req *http.Request) (string, error) { | ||
// loop over header names and return the first one that contains data | ||
for _, header := range e { | ||
if ah := req.Header.Get(header); ah != "" { | ||
return ah, nil | ||
} | ||
} | ||
return "", ErrNoTokenInRequest | ||
} | ||
|
||
// Extract token from request arguments. This includes a POSTed form or | ||
// GET URL arguments. Argument names are tried in order until there's a match. | ||
type ArgumentExtractor []string | ||
|
||
func (e ArgumentExtractor) ExtractToken(req *http.Request) (string, error) { | ||
// Make sure form is parsed | ||
req.ParseMultipartForm(10e6) | ||
|
||
// loop over arg names and return the first one that contains data | ||
for _, arg := range e { | ||
if ah := req.Form.Get(arg); ah != "" { | ||
return ah, nil | ||
} | ||
} | ||
|
||
return "", ErrNoTokenInRequest | ||
} | ||
|
||
// Tries Extractors in order until one returns a token string or an error occurs | ||
type MultiExtractor []Extractor | ||
|
||
func (e MultiExtractor) ExtractToken(req *http.Request) (string, error) { | ||
// loop over header names and return the first one that contains data | ||
for _, extractor := range e { | ||
if tok, err := extractor.ExtractToken(req); tok != "" { | ||
return tok, nil | ||
} else if err != ErrNoTokenInRequest { | ||
return "", err | ||
} | ||
} | ||
return "", ErrNoTokenInRequest | ||
} | ||
|
||
// Wrap an Extractor in this to post-process the value before it's handed off. | ||
// See AuthorizationHeaderExtractor for an example | ||
type PostExtractionFilter struct { | ||
Extractor | ||
Filter func(string) (string, error) | ||
} | ||
|
||
func (e *PostExtractionFilter) ExtractToken(req *http.Request) (string, error) { | ||
if tok, err := e.Extractor.ExtractToken(req); tok != "" { | ||
return e.Filter(tok) | ||
} else { | ||
return "", err | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package request | ||
|
||
import ( | ||
"fmt" | ||
"net/url" | ||
) | ||
|
||
const ( | ||
exampleTokenA = "A" | ||
) | ||
|
||
func ExampleHeaderExtractor() { | ||
req := makeExampleRequest("GET", "/", map[string]string{"Token": exampleTokenA}, nil) | ||
tokenString, err := HeaderExtractor{"Token"}.ExtractToken(req) | ||
if err == nil { | ||
fmt.Println(tokenString) | ||
} else { | ||
fmt.Println(err) | ||
} | ||
//Output: A | ||
} | ||
|
||
func ExampleArgumentExtractor() { | ||
req := makeExampleRequest("GET", "/", nil, url.Values{"token": {extractorTestTokenA}}) | ||
tokenString, err := ArgumentExtractor{"token"}.ExtractToken(req) | ||
if err == nil { | ||
fmt.Println(tokenString) | ||
} else { | ||
fmt.Println(err) | ||
} | ||
//Output: A | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package request | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"testing" | ||
) | ||
|
||
var extractorTestTokenA = "A" | ||
var extractorTestTokenB = "B" | ||
|
||
var extractorTestData = []struct { | ||
name string | ||
extractor Extractor | ||
headers map[string]string | ||
query url.Values | ||
token string | ||
err error | ||
}{ | ||
{ | ||
name: "simple header", | ||
extractor: HeaderExtractor{"Foo"}, | ||
headers: map[string]string{"Foo": extractorTestTokenA}, | ||
query: nil, | ||
token: extractorTestTokenA, | ||
err: nil, | ||
}, | ||
{ | ||
name: "simple argument", | ||
extractor: ArgumentExtractor{"token"}, | ||
headers: map[string]string{}, | ||
query: url.Values{"token": {extractorTestTokenA}}, | ||
token: extractorTestTokenA, | ||
err: nil, | ||
}, | ||
{ | ||
name: "multiple extractors", | ||
extractor: MultiExtractor{ | ||
HeaderExtractor{"Foo"}, | ||
ArgumentExtractor{"token"}, | ||
}, | ||
headers: map[string]string{"Foo": extractorTestTokenA}, | ||
query: url.Values{"token": {extractorTestTokenB}}, | ||
token: extractorTestTokenA, | ||
err: nil, | ||
}, | ||
{ | ||
name: "simple miss", | ||
extractor: HeaderExtractor{"This-Header-Is-Not-Set"}, | ||
headers: map[string]string{"Foo": extractorTestTokenA}, | ||
query: nil, | ||
token: "", | ||
err: ErrNoTokenInRequest, | ||
}, | ||
{ | ||
name: "filter", | ||
extractor: AuthorizationHeaderExtractor, | ||
headers: map[string]string{"Authorization": "Bearer " + extractorTestTokenA}, | ||
query: nil, | ||
token: extractorTestTokenA, | ||
err: nil, | ||
}, | ||
} | ||
|
||
func TestExtractor(t *testing.T) { | ||
// Bearer token request | ||
for _, data := range extractorTestData { | ||
// Make request from test struct | ||
r := makeExampleRequest("GET", "/", data.headers, data.query) | ||
|
||
// Test extractor | ||
token, err := data.extractor.ExtractToken(r) | ||
if token != data.token { | ||
t.Errorf("[%v] Expected token '%v'. Got '%v'", data.name, data.token, token) | ||
continue | ||
} | ||
if err != data.err { | ||
t.Errorf("[%v] Expected error '%v'. Got '%v'", data.name, data.err, err) | ||
continue | ||
} | ||
} | ||
} | ||
|
||
func makeExampleRequest(method, path string, headers map[string]string, urlArgs url.Values) *http.Request { | ||
r, _ := http.NewRequest(method, fmt.Sprintf("%v?%v", path, urlArgs.Encode()), nil) | ||
for k, v := range headers { | ||
r.Header.Set(k, v) | ||
} | ||
return r | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package request | ||
|
||
import ( | ||
"strings" | ||
) | ||
|
||
// Strips 'Bearer ' prefix from bearer token string | ||
func stripBearerPrefixFromTokenString(tok string) (string, error) { | ||
// Should be a bearer token | ||
if len(tok) > 6 && strings.ToUpper(tok[0:7]) == "BEARER " { | ||
return tok[7:], nil | ||
} | ||
return tok, nil | ||
} | ||
|
||
// Extract bearer token from Authorization header | ||
// Uses PostExtractionFilter to strip "Bearer " prefix from header | ||
var AuthorizationHeaderExtractor = &PostExtractionFilter{ | ||
HeaderExtractor{"Authorization"}, | ||
stripBearerPrefixFromTokenString, | ||
} | ||
|
||
// Extractor for OAuth2 access tokens. Looks in 'Authorization' | ||
// header then 'access_token' argument for a token. | ||
var OAuth2Extractor = &MultiExtractor{ | ||
AuthorizationHeaderExtractor, | ||
ArgumentExtractor{"access_token"}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,24 @@ | ||
package request | ||
|
||
import ( | ||
"errors" | ||
"github.com/dgrijalva/jwt-go" | ||
"net/http" | ||
"strings" | ||
) | ||
|
||
// Errors | ||
var ( | ||
ErrNoTokenInRequest = errors.New("no token present in request") | ||
) | ||
|
||
// Try to find the token in an http.Request. | ||
// This method will call ParseMultipartForm if there's no token in the header. | ||
// Currently, it looks in the Authorization header as well as | ||
// looking for an 'access_token' request parameter in req.Form. | ||
func ParseFromRequest(req *http.Request, keyFunc jwt.Keyfunc) (token *jwt.Token, err error) { | ||
return ParseFromRequestWithClaims(req, jwt.MapClaims{}, keyFunc) | ||
// Extract and parse a JWT token from an HTTP request. | ||
// This behaves the same as Parse, but accepts a request and an extractor | ||
// instead of a token string. The Extractor interface allows you to define | ||
// the logic for extracting a token. Several useful implementations are provided. | ||
func ParseFromRequest(req *http.Request, extractor Extractor, keyFunc jwt.Keyfunc) (token *jwt.Token, err error) { | ||
return ParseFromRequestWithClaims(req, extractor, jwt.MapClaims{}, keyFunc) | ||
} | ||
|
||
func ParseFromRequestWithClaims(req *http.Request, claims jwt.Claims, keyFunc jwt.Keyfunc) (token *jwt.Token, err error) { | ||
// Look for an Authorization header | ||
if ah := req.Header.Get("Authorization"); ah != "" { | ||
// Should be a bearer token | ||
if len(ah) > 6 && strings.ToUpper(ah[0:7]) == "BEARER " { | ||
return jwt.ParseWithClaims(ah[7:], claims, keyFunc) | ||
} | ||
} | ||
|
||
// Look for "access_token" parameter | ||
req.ParseMultipartForm(10e6) | ||
if tokStr := req.Form.Get("access_token"); tokStr != "" { | ||
// ParseFromRequest but with custom Claims type | ||
func ParseFromRequestWithClaims(req *http.Request, extractor Extractor, claims jwt.Claims, keyFunc jwt.Keyfunc) (token *jwt.Token, err error) { | ||
// Extract token from request | ||
if tokStr, err := extractor.ExtractToken(req); err == nil { | ||
return jwt.ParseWithClaims(tokStr, claims, keyFunc) | ||
} else { | ||
return nil, err | ||
} | ||
|
||
return nil, ErrNoTokenInRequest | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters