/
creator.go
116 lines (99 loc) · 3.63 KB
/
creator.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
package tokens
import (
"crypto/rsa"
"errors"
"time"
authz "github.com/contiamo/go-base/v4/pkg/http/middlewares/authorization"
"github.com/golang-jwt/jwt/v4"
uuid "github.com/satori/go.uuid"
)
var (
// ErrNoPrivateKeySpecified occurs when the private key was not set
// and there was an attempt to create a token
ErrNoPrivateKeySpecified = errors.New("private key is nil")
)
type token authz.Claims
// Valid implements jwt.Claims interface.
func (t token) Valid() error {
return authz.Claims(t).Validate()
}
// Options control the value or the generation of the claims in the resulting token.
// All values are optional and the empty value will be ignored.
type Options struct {
// Audience is a name of the service that receives the request. Other
// services should not validate tokens intended for other services.
Audience string
// ProjectID is the UUID string for a project that the token should be
// considered a member and an admin of. This value is deprecated, but
// exists for backwards compatibility during the transition to `azp`.
ProjectID string
// ID is the UUID string to identify this token.
// It will be a random UUID if not specified.
ID string
// UserID is the UUID string to identify the user that the token is
// intended for. It will be the null UUID when not specified
UserID string
}
// Creator creates all kinds of signed tokens for the background tasks
type Creator interface {
// Create creates a signed token that can be used for interservice communication.
Create(reference string, opts Options) (string, error)
}
// NewCreator creates a new token creator for tasks
func NewCreator(issuer string, privateKey *rsa.PrivateKey, lifetime time.Duration) Creator {
if lifetime < 5 {
lifetime = 5 * time.Second
}
return &tokenCreator{
issuer: issuer,
jwtKey: privateKey,
lifetime: lifetime,
}
}
type tokenCreator struct {
issuer string
jwtKey *rsa.PrivateKey
lifetime time.Duration
}
func (t *tokenCreator) Create(reference string, opts Options) (string, error) {
if t.jwtKey == nil {
return "", ErrNoPrivateKeySpecified
}
if opts.ID == "" {
opts.ID = uuid.NewV4().String()
}
if opts.UserID == "" {
opts.UserID = uuid.Nil.String()
}
now := time.Now()
maxSkew := 5 * time.Second
requestToken := token{
ID: opts.ID,
Issuer: t.issuer,
IssuedAt: authz.FromTime(now),
NotBefore: authz.FromTime(now.Add(-1 * maxSkew)),
Expires: authz.FromTime(now.Add(t.lifetime)),
UserID: opts.UserID,
UserName: "@" + t.issuer,
Email: t.issuer + "@contiamo.com",
RealmIDs: []string{},
GroupIDs: []string{},
AllowedIPs: []string{},
AdminRealmIDs: []string{},
AuthenticationMethodReferences: []string{reference},
AuthorizedParty: t.issuer,
Audience: opts.Audience,
}
if opts.ProjectID != "" {
requestToken.AdminRealmIDs = append(requestToken.AdminRealmIDs, opts.ProjectID)
requestToken.RealmIDs = append(requestToken.RealmIDs, opts.ProjectID)
}
// alternatively we could have use `claims.ToJWT`
// because t.jwtKey is an rsa.PrivateKey, we will get the same
// token signed using jwt.SigningMethodRS512
// return requestToken.ToJWT(t.jwtKey)
// wrapping authz.Claims as a jwt.Claims allows us to avoid a json.Marshal -> json.Unmarshal
// that authz.Claims.ToJWT uses internally
token := jwt.NewWithClaims(jwt.SigningMethodRS512, requestToken)
return token.SignedString(t.jwtKey)
}