Codebase list golang-github-go-kit-kit / f9e217b
Improvements based off PR comments Brian Kassouf 7 years ago
4 changed file(s) with 96 addition(s) and 80 deletion(s). Raw diff Collapse all Expand all
00 # package auth/jwt
11
2 `package auth/jwt` provides a set of interfaces for service authorization through [JSON Web Tokens](https://jwt.io/).
2 `package auth/jwt` provides a set of interfaces for service authorization
3 through [JSON Web Tokens](https://jwt.io/).
34
45 ## Usage
56
6 NewParser takes a key function and an expected signing method and returns an `endpoint.Middleware`.
7 The middleware will parse a token passed into the context via the `jwt.JWTTokenContextKey`.
8 If the token is valid, any claims will be added to the context via the `jwt.JWTClaimsContextKey`.
7 NewParser takes a key function and an expected signing method and returns an
8 `endpoint.Middleware`. The middleware will parse a token passed into the
9 context via the `jwt.JWTTokenContextKey`. If the token is valid, any claims
10 will be added to the context via the `jwt.JWTClaimsContextKey`.
911
1012 ```go
1113 import (
1214 stdjwt "github.com/dgrijalva/jwt-go"
13
14 "github.com/go-kit/kit/auth/jwt"
15 "github.com/go-kit/kit/endpoint"
15
16 "github.com/go-kit/kit/auth/jwt"
17 "github.com/go-kit/kit/endpoint"
1618 )
1719
1820 func main() {
1921 var exampleEndpoint endpoint.Endpoint
2022 {
21 keyFunc := func(token *stdjwt.Token) (interface{}, error) { return []byte("SigningString"), nil }
22 jwtParser := jwt.NewParser(keyFunc, stdjwt.SigningMethodHS256)
23
23 kf := func(token *stdjwt.Token) (interface{}, error) { return []byte("SigningString"), nil }
2424 exampleEndpoint = MakeExampleEndpoint(service)
25 exampleEndpoint = jwtParser(exampleEndpoint)
25 exampleEndpoint = jwt.NewParser(kf, stdjwt.SigningMethodHS256)(exampleEndpoint)
2626 }
2727 }
2828 ```
2929
30 NewSigner takes a JWT key id header, the signing key, signing method, and a claims object. It returns an `endpoint.Middleware`.
31 The middleware will build the token string and add it to the context via the `jwt.JWTTokenContextKey`.
30 NewSigner takes a JWT key ID header, the signing key, signing method, and a
31 claims object. It returns an `endpoint.Middleware`. The middleware will build
32 the token string and add it to the context via the `jwt.JWTTokenContextKey`.
3233
3334 ```go
3435 import (
3536 stdjwt "github.com/dgrijalva/jwt-go"
36
37 "github.com/go-kit/kit/auth/jwt"
38 "github.com/go-kit/kit/endpoint"
37
38 "github.com/go-kit/kit/auth/jwt"
39 "github.com/go-kit/kit/endpoint"
3940 )
4041
4142 func main() {
4243 var exampleEndpoint endpoint.Endpoint
4344 {
44 jwtSigner := jwt.NewSigner("kid-header", []byte("SigningString"), stdjwt.SigningMethodHS256, jwt.Claims{})
45
46 exampleEndpoint = grpctransport.NewClient(
47 . // build client endpoint here
48 .
49 .
50 ).Endpoint()
51
52 exampleEndpoint = jwtSigner(exampleEndpoint)
45 exampleEndpoint = grpctransport.NewClient(...).Endpoint()
46 exampleEndpoint = jwt.NewSigner(
47 "kid-header",
48 []byte("SigningString"),
49 stdjwt.SigningMethodHS256,
50 jwt.Claims{},
51 )(exampleEndpoint)
5352 }
5453 }
5554 ```
5655
57 In order for the parser and the signer to work, the authorization headers need to be passed between the request and the context.
58 `ToHTTPContext()`, `FromHTTPContext()`, `ToGRPCContext()`, and `FromGRPCContext()` are given as helpers to do this.
59 These functions implement the correlating transport's RequestFunc interface and can be passed as ClientBefore or ServerBefore options.
56 In order for the parser and the signer to work, the authorization headers need
57 to be passed between the request and the context. `ToHTTPContext()`,
58 `FromHTTPContext()`, `ToGRPCContext()`, and `FromGRPCContext()` are given as
59 helpers to do this. These functions implement the correlating transport's
60 RequestFunc interface and can be passed as ClientBefore or ServerBefore
61 options.
6062
6163 Example of use in a client:
6264
6365 ```go
6466 import (
65 stdjwt "github.com/dgrijalva/jwt-go"
66 grpctransport "github.com/go-kit/kit/transport/grpc"
67
68 "github.com/go-kit/kit/auth/jwt"
69 "github.com/go-kit/kit/endpoint"
67 stdjwt "github.com/dgrijalva/jwt-go"
68
69 grpctransport "github.com/go-kit/kit/transport/grpc"
70 "github.com/go-kit/kit/auth/jwt"
71 "github.com/go-kit/kit/endpoint"
7072 )
7173
7274 func main() {
7375
74 options := []httptransport.ClientOption{}
76 options := []httptransport.ClientOption{}
7577 var exampleEndpoint endpoint.Endpoint
7678 {
77 jwtSigner := jwt.NewSigner("kid-header", []byte("SigningString"), stdjwt.SigningMethodHS256, jwt.Claims{})
78
79 options = append(options, grpctransport.ClientBefore(jwt.FromGRPCContext()))
80 exampleEndpoint = grpctransport.NewClient(
81 . // build client endpoint here
82 .
83 options....
84 ).Endpoint()
85
86 exampleEndpoint = jwtSigner(exampleEndpoint)
79 exampleEndpoint = grpctransport.NewClient(..., grpctransport.ClientBefore(jwt.FromGRPCContext())).Endpoint()
80 exampleEndpoint = jwt.NewSigner(
81 "kid-header",
82 []byte("SigningString"),
83 stdjwt.SigningMethodHS256,
84 jwt.Claims{},
85 )(exampleEndpoint)
8786 }
8887 }
8988 ```
22 import (
33 "errors"
44
5 jwt "github.com/dgrijalva/jwt-go"
56 "golang.org/x/net/context"
67
7 jwt "github.com/dgrijalva/jwt-go"
88 "github.com/go-kit/kit/endpoint"
99 )
1010
1111 type contextKey string
1212
1313 const (
14 // JWTTokenContextKey holds the key used to store a JWT Token in the context
14 // JWTTokenContextKey holds the key used to store a JWT Token in the
15 // context.
1516 JWTTokenContextKey contextKey = "JWTToken"
16 // JWTClaimsContxtKey holds the key used to store the JWT Claims in the context
17 // JWTClaimsContxtKey holds the key used to store the JWT Claims in the
18 // context.
1719 JWTClaimsContextKey contextKey = "JWTClaims"
1820 )
1921
2022 var (
21 ErrTokenContextMissing = errors.New("Token up for parsing was not passed through the context")
22 ErrTokenInvalid = errors.New("JWT Token was invalid")
23 ErrTokenExpired = errors.New("JWT Token is expired")
24 ErrTokenMalformed = errors.New("JWT Token is malformed")
25 ErrTokenNotActive = errors.New("Token is not valid yet")
26 ErrUnexpectedSigningMethod = errors.New("Unexpected signing method")
23 // ErrTokenContextMissing denotes a token was not passed into the parsing
24 // middleware's context.
25 ErrTokenContextMissing = errors.New("token up for parsing was not passed through the context")
26 // ErrTokenInvalid denotes a token was not able to be validated.
27 ErrTokenInvalid = errors.New("JWT Token was invalid")
28 // ErrTokenExpired denotes a token's expire header (exp) has since passed.
29 ErrTokenExpired = errors.New("JWT Token is expired")
30 // ErrTokenMalformed denotes a token was not formatted as a JWT token.
31 ErrTokenMalformed = errors.New("JWT Token is malformed")
32 // ErrTokenNotActive denotes a token's not before header (nbf) is in the
33 // future.
34 ErrTokenNotActive = errors.New("token is not valid yet")
35 // ErrUncesptedSigningMethod denotes a token was signed with an unexpected
36 // signing method.
37 ErrUnexpectedSigningMethod = errors.New("unexpected signing method")
2738 )
2839
2940 type Claims map[string]interface{}
3041
31 // Create a new JWT token generating middleware, specifying signing method and the claims
32 // you would like it to contain. Particularly useful for clients.
42 // NewSigner creates a new JWT token generating middleware, specifying key ID,
43 // signing string, signing method and the claims you would like it to contain.
44 // Tokens are signed with a Key ID header (kid) which is useful for determining
45 // the key to use for parsing. Particularly useful for clients.
3346 func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims Claims) endpoint.Middleware {
3447 return func(next endpoint.Endpoint) endpoint.Endpoint {
3548 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
4861 }
4962 }
5063
51 // Create a new JWT token parsing middleware, specifying a jwt.Keyfunc interface and the
52 // signing method. Adds the resulting claims to endpoint context or returns error on invalid
53 // token. Particularly useful for servers.
64 // NewParser creates a new JWT token parsing middleware, specifying a
65 // jwt.Keyfunc interface and the signing method. NewParser adds the resulting
66 // claims to endpoint context or returns error on invalid token. Particularly
67 // useful for servers.
5468 func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod) endpoint.Middleware {
5569 return func(next endpoint.Endpoint) endpoint.Endpoint {
5670 return func(ctx context.Context, request interface{}) (response interface{}, err error) {
57 // tokenString is stored in the context from the transport handlers
71 // tokenString is stored in the context from the transport handlers.
5872 tokenString, ok := ctx.Value(JWTTokenContextKey).(string)
5973 if !ok {
6074 return nil, ErrTokenContextMissing
6175 }
6276
63 // Parse takes the token string and a function for looking up the key. The latter is especially
64 // useful if you use multiple keys for your application. The standard is to use 'kid' in the
65 // head of the token to identify which key to use, but the parsed token (head and claims) is provided
66 // to the callback, providing flexibility.
77 // Parse takes the token string and a function for looking up the
78 // key. The latter is especially useful if you use multiple keys
79 // for your application. The standard is to use 'kid' in the head
80 // of the token to identify which key to use, but the parsed token
81 // (head and claims) is provided to the callback, providing
82 // flexibility.
6783 token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
6884 // Don't forget to validate the alg is what you expect:
6985 if token.Method != method {
1313 method = jwt.SigningMethodHS256
1414 invalidMethod = jwt.SigningMethodRS256
1515 claims = Claims{"user": "go-kit"}
16 signedKey = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ28ta2l0In0.14M2VmYyApdSlV_LZ88ajjwuaLeIFplB8JpyNy0A19E"
17 invalidKey = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.vKVCKto-Wn6rgz3vBdaZaCBGfCBDTXOENSo_X2Gq7qA"
16 // Signed tokens generated at https://jwt.io/
17 signedKey = "eyJhbGciOiJIUzI1NiIsImtpZCI6ImtpZCIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ28ta2l0In0.14M2VmYyApdSlV_LZ88ajjwuaLeIFplB8JpyNy0A19E"
18 invalidKey = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.e30.vKVCKto-Wn6rgz3vBdaZaCBGfCBDTXOENSo_X2Gq7qA"
1819 )
1920
2021 func TestSigner(t *testing.T) {
44 stdhttp "net/http"
55 "strings"
66
7 "golang.org/x/net/context"
8 "google.golang.org/grpc/metadata"
9
710 "github.com/go-kit/kit/transport/grpc"
811 "github.com/go-kit/kit/transport/http"
9 "golang.org/x/net/context"
10 "google.golang.org/grpc/metadata"
1112 )
1213
1314 const (
14 BEARER string = "bearer"
15 BEARER_FORMAT string = "Bearer %s"
15 bearer string = "bearer"
16 bearerFormat string = "Bearer %s"
1617 )
1718
18 // moves JWT token from request header to context
19 // particularly useful for servers
19 // ToHTTPContext moves JWT token from request header to contexti. Particularly
20 // useful for servers
2021 func ToHTTPContext() http.RequestFunc {
2122 return func(ctx context.Context, r *stdhttp.Request) context.Context {
2223 token, ok := extractTokenFromAuthHeader(r.Header.Get("Authorization"))
2829 }
2930 }
3031
31 // moves JWT token from context to request header
32 // particularly useful for clients
32 // FromHTTPContext moves JWT token from context to request header. Particularly
33 // useful for clients
3334 func FromHTTPContext() http.RequestFunc {
3435 return func(ctx context.Context, r *stdhttp.Request) context.Context {
3536 token, ok := ctx.Value(JWTTokenContextKey).(string)
4041 }
4142 }
4243
43 // moves JWT token from grpc metadata to context
44 // particularly userful for servers
44 // ToGRPCContext moves JWT token from grpc metadata to context. Particularly
45 // userful for servers
4546 func ToGRPCContext() grpc.RequestFunc {
4647 return func(ctx context.Context, md *metadata.MD) context.Context {
4748 // capital "Key" is illegal in HTTP/2.
5960 }
6061 }
6162
62 // moves JWT token from context to grpc metadata
63 // particularly useful for clients
63 // FromGRPCContext moves JWT token from context to grpc metadata. Particularly
64 // useful for clients
6465 func FromGRPCContext() grpc.RequestFunc {
6566 return func(ctx context.Context, md *metadata.MD) context.Context {
6667 token, ok := ctx.Value(JWTTokenContextKey).(string)
7374 }
7475 }
7576
76 // extractTokenFromAuthHeader returns the token from the value of the Authorzation header
7777 func extractTokenFromAuthHeader(val string) (token string, ok bool) {
7878 authHeaderParts := strings.Split(val, " ")
79 if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != BEARER {
79 if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != bearer {
8080 return "", false
8181 }
8282
8484 }
8585
8686 func generateAuthHeaderFromToken(token string) string {
87 return fmt.Sprintf(BEARER_FORMAT, token)
87 return fmt.Sprintf(bearerFormat, token)
8888 }