-
Notifications
You must be signed in to change notification settings - Fork 21
/
tokenFactory.go
90 lines (75 loc) · 2.71 KB
/
tokenFactory.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// SPDX-FileCopyrightText: 2017 Comcast Cable Communications Management, LLC
// SPDX-License-Identifier: Apache-2.0
package main
import (
"context"
"errors"
"net/http"
"github.com/golang-jwt/jwt"
"github.com/goph/emperror"
"github.com/xmidt-org/bascule"
"github.com/xmidt-org/bascule/basculehttp"
"github.com/xmidt-org/clortho"
)
const (
jwtPrincipalKey = "sub"
)
// RawAttributesBearerTokenFactory parses and does basic validation for a JWT token, creating a token with RawAttributes
// instead of bascule processed attributes
type RawAttributesBearerTokenFactory struct {
DefaultKeyID string
Resolver clortho.Resolver
Parser bascule.JWTParser
Leeway bascule.Leeway
}
func defaultKeyFunc(ctx context.Context, defaultKeyID string, resolver clortho.Resolver) jwt.Keyfunc {
return func(token *jwt.Token) (interface{}, error) {
keyID, ok := token.Header["kid"].(string)
if !ok {
keyID = defaultKeyID
}
pair, err := resolver.Resolve(ctx, keyID)
if err != nil {
return nil, emperror.Wrap(err, "failed to resolve key")
}
return pair.Public(), nil
}
}
// ParseAndValidate expects the given value to be a JWT with a kid header. The
// kid should be resolvable by the Resolver and the JWT should be Parseable and
// pass any basic validation checks done by the Parser. If everything goes
// well, a Token of type "jwt" is returned.
func (r RawAttributesBearerTokenFactory) ParseAndValidate(ctx context.Context, _ *http.Request, _ bascule.Authorization, value string) (bascule.Token, error) {
if len(value) == 0 {
return nil, errors.New("empty value")
}
leewayclaims := bascule.ClaimsWithLeeway{
MapClaims: make(jwt.MapClaims),
Leeway: r.Leeway,
}
jwtToken, err := r.Parser.ParseJWT(value, &leewayclaims, defaultKeyFunc(ctx, r.DefaultKeyID, r.Resolver))
if err != nil {
return nil, emperror.Wrap(err, "failed to parse JWT")
}
if !jwtToken.Valid {
return nil, basculehttp.ErrInvalidToken
}
claims, ok := jwtToken.Claims.(*bascule.ClaimsWithLeeway)
if !ok {
return nil, emperror.Wrap(basculehttp.ErrUnexpectedClaims, "failed to parse JWT")
}
claimsMap, err := claims.GetMap()
if err != nil {
return nil, emperror.WrapWith(err, "failed to get map of claims", "claims struct", claims)
}
jwtClaims := NewRawAttributes(claimsMap)
principalVal, ok := jwtClaims.Get(jwtPrincipalKey)
if !ok {
return nil, emperror.WrapWith(basculehttp.ErrInvalidPrincipal, "principal value not found", "principal key", jwtPrincipalKey, "jwtClaims", claimsMap)
}
principal, ok := principalVal.(string)
if !ok {
return nil, emperror.WrapWith(basculehttp.ErrInvalidPrincipal, "principal value not a string", "principal", principalVal)
}
return bascule.NewToken("jwt", principal, jwtClaims), nil
}