-
Notifications
You must be signed in to change notification settings - Fork 2
/
jwt_user_provider.go
164 lines (139 loc) · 4.58 KB
/
jwt_user_provider.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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package cypress
import (
"context"
"crypto/rsa"
"net/http"
"strings"
"go.uber.org/zap"
"gopkg.in/square/go-jose.v2/jwt"
)
const (
DefaultJwtProviderName = "JWT"
BackupKeyNameSuffix = "$backup"
bearerAuthPrefix = "Bearer "
)
// JwtUserPrincipal a user principal created from JWT token
type JwtUserPrincipal struct {
UserPrincipal
SecurityToken string
DisplayName string
Parent string
Source string
}
// UserPrincipalLoader loads a user principal by user domain and id
type UserPrincipalLoader interface {
Load(domain, id string) *UserPrincipal
}
// UserPrincipalLoaderFunc delegates function to UserPrincipalLoader interface
type UserPrincipalLoaderFunc func(domain, id string) *UserPrincipal
func (f UserPrincipalLoaderFunc) Load(domain, id string) *UserPrincipal {
return f(domain, id)
}
// JwtUserClaims Microsoft claims spec compatible user claims
type JwtUserClaims struct {
jwt.Claims
Sid string `json:"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/sid,omitempty"`
Name string `json:"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name,omitempty"`
AccountName string `json:"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn,omitempty"`
Roles []string `json:"http://schemas.microsoft.com/ws/2008/06/identity/claims/role,omitempty"`
Domain string `json:"TenantId,omitempty"`
SecurityToken string `json:"sectok,omitempty"`
Parent string `json:"ParentAccountName,omitempty"`
}
func (claims *JwtUserClaims) toUserPrincipal() *UserPrincipal {
jwtPrincipal := &JwtUserPrincipal{
UserPrincipal: UserPrincipal{
ID: claims.Sid,
Domain: claims.Domain,
Name: claims.AccountName,
Roles: claims.Roles,
},
SecurityToken: claims.SecurityToken,
DisplayName: claims.Name,
Parent: claims.Parent,
Source: claims.Issuer,
}
jwtPrincipal.Self = jwtPrincipal
return &jwtPrincipal.UserPrincipal
}
// JwtKeyProvider RSA public key provider for retrieving issuer public keys
type JwtKeyProvider interface {
GetKey(ctx context.Context, issuer string) *rsa.PublicKey
GetBackupKey(ctx context.Context, issuer string) *rsa.PublicKey
}
// JwtKeyMap maps issuer to a public key
type JwtKeyMap map[string]*rsa.PublicKey
func (m JwtKeyMap) GetKey(_ context.Context, issuer string) *rsa.PublicKey {
key, ok := m[issuer]
if !ok {
return nil
}
return key
}
func (m JwtKeyMap) GetBackupKey(_ context.Context, issuer string) *rsa.PublicKey {
backupName := issuer + BackupKeyNameSuffix
key, ok := m[backupName]
if !ok {
return nil
}
return key
}
// JwtUserProvider jwt token based user provider
type JwtUserProvider struct {
keyProvider JwtKeyProvider
userLoader UserPrincipalLoader
}
// NewJwtUserProvider creates a new instances of jwt user provider
func NewJwtUserProvider(keyProvider JwtKeyProvider, userLoader UserPrincipalLoader) *JwtUserProvider {
return &JwtUserProvider{
keyProvider: keyProvider,
userLoader: userLoader,
}
}
func (provider *JwtUserProvider) GetName() string {
return DefaultJwtProviderName
}
func (provider *JwtUserProvider) Load(domain, id string) *UserPrincipal {
if provider.userLoader != nil {
return provider.userLoader.Load(domain, id)
}
return nil
}
func (provider *JwtUserProvider) Authenticate(request *http.Request) *UserPrincipal {
authHeader := strings.TrimSpace(request.Header.Get("Authorization"))
if len(authHeader) > 0 && strings.HasPrefix(authHeader, bearerAuthPrefix) {
authToken := authHeader[len(bearerAuthPrefix):]
token, err := jwt.ParseSigned(authToken)
if err != nil {
zap.L().Warn("failed to parse signed jwt token", zap.String("token", authToken), zap.Error(err))
return nil
}
ctx := request.Context()
defaultClaims := new(jwt.Claims)
if err = token.UnsafeClaimsWithoutVerification(defaultClaims); err == nil {
for i := 0; i < 2; i++ {
var key *rsa.PublicKey
keyName := defaultClaims.Issuer
if i == 0 {
key = provider.keyProvider.GetKey(ctx, defaultClaims.Issuer)
} else {
key = provider.keyProvider.GetBackupKey(ctx, defaultClaims.Issuer)
keyName = keyName + BackupKeyNameSuffix
}
if key != nil {
claims := new(JwtUserClaims)
if err = token.Claims(key, claims); err == nil {
return claims.toUserPrincipal()
} else {
zap.L().Warn("failed to verify signature of jwt token", zap.String("key", keyName), zap.Error(err))
}
} else {
zap.L().Warn("jwt key not found", zap.String("key", keyName))
}
}
} else {
zap.L().Warn("failed to parse claims from token", zap.String("token", authToken), zap.Error(err))
}
}
return nil
}