-
Notifications
You must be signed in to change notification settings - Fork 10
/
authn.go
171 lines (141 loc) · 5.43 KB
/
authn.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
165
166
167
168
169
170
171
package authn
import (
"errors"
"net/http"
"time"
jwt "github.com/go-jose/go-jose/v3/jwt"
)
// TODO: jose/jwt references are all over the place. Refactor possible?
// ErrInvalidOptions is returned by SubjectFrom if invalid options are used
var ErrInvalidOptions = errors.New("invalid options for SubjectFrom")
// Client provides JWT verification for ID tokens generated by the AuthN server. In the future it
// will also implement the server's private APIs (aka admin actions).
type Client struct {
config Config
iclient *internalClient
kchain *keychainCache
verifier JWTClaimsExtractor
}
// NewClient returns an initialized and configured Client.
func NewClient(config Config) (*Client, error) {
var err error
config.setDefaults()
ac := Client{}
ac.config = config
ac.iclient, err = newInternalClient(config.PrivateBaseURL, config.Username, config.Password)
if err != nil {
return nil, err
}
ac.kchain = newKeychainCache(time.Duration(config.KeychainTTL)*time.Minute, ac.iclient)
ac.verifier, err = NewIDTokenVerifier(config.Issuer, config.Audience, ac.kchain)
if err != nil {
return nil, err
}
return &ac, nil
}
// SubjectFrom will return the subject inside the given idToken if and only if the token is a valid
// JWT that passes all verification requirements. The returned value is the AuthN server's account
// ID and should be used as a unique foreign key in your users data.
//
// If the JWT does not verify, the returned error will explain why. This is for debugging purposes.
func (ac *Client) SubjectFrom(idToken string) (string, error) {
return ac.subjectFromVerifier(idToken, ac.verifier)
}
// SubjectFromWithAudience works like SubjectFrom but allows specifying a different
// JWT audience.
func (ac *Client) SubjectFromWithAudience(idToken string, audience jwt.Audience) (string, error) {
verifier, err := newIDTokenVerifierWithAudiences(ac.config.Issuer, audience, ac.kchain)
if err != nil {
return "", err
}
return ac.subjectFromVerifier(idToken, verifier)
}
// ClaimsFrom will return all verified claims inside the given idToken
// if and only if the token is a valid JWT that passes all
// verification requirements. If the JWT does not verify, the returned
// error will explain why. This is for debugging purposes.
func (ac *Client) ClaimsFrom(idToken string) (*Claims, error) {
return ac.claimsFromVerifier(idToken, ac.verifier)
}
// ClaimsFromWithAudience works like ClaimsFrom but allows
// specifying a different JWT audience.
func (ac *Client) ClaimsFromWithAudience(idToken string, audience jwt.Audience) (*Claims, error) {
verifier, err := newIDTokenVerifierWithAudiences(ac.config.Issuer, audience, ac.kchain)
if err != nil {
return nil, err
}
return ac.claimsFromVerifier(idToken, verifier)
}
func (ac *Client) subjectFromVerifier(idToken string, verifier JWTClaimsExtractor) (string, error) {
claims, err := ac.claimsFromVerifier(idToken, verifier)
if err != nil {
return "", err
}
return claims.Subject, nil
}
func (ac *Client) claimsFromVerifier(idToken string, verifier JWTClaimsExtractor) (*Claims, error) {
claims, err := verifier.GetVerifiedClaims(idToken)
if err != nil {
return nil, err
}
return claims, nil
}
// GetAccount gets the account with the associated id
func (ac *Client) GetAccount(id string) (*Account, error) { // Should this be a string or an int?
return ac.iclient.GetAccount(id)
}
// Update updates the account with the associated id
func (ac *Client) Update(id, username string) error {
return ac.iclient.Update(id, username)
}
// LockAccount locks the account with the associated id
func (ac *Client) LockAccount(id string) error {
return ac.iclient.LockAccount(id)
}
// UnlockAccount unlocks the account with the associated id
func (ac *Client) UnlockAccount(id string) error {
return ac.iclient.UnlockAccount(id)
}
// ArchiveAccount archives the account with the associated id
func (ac *Client) ArchiveAccount(id string) error {
return ac.iclient.ArchiveAccount(id)
}
// ImportAccount imports an account with the provided information, returns the imported account id
func (ac *Client) ImportAccount(username, password string, locked bool) (int, error) {
return ac.iclient.ImportAccount(username, password, locked)
}
// ExpirePassword expires the password of the account with the associated id
func (ac *Client) ExpirePassword(id string) error {
return ac.iclient.ExpirePassword(id)
}
// ServiceStats gets the http response object from calling the service stats endpoint
func (ac *Client) ServiceStats() (*http.Response, error) {
return ac.iclient.ServiceStats()
}
// ServerStats gets the http response object from calling the server stats endpoint
func (ac *Client) ServerStats() (*http.Response, error) {
return ac.iclient.ServerStats()
}
// DefaultClient can be initialized by Configure and used by SubjectFrom.
var DefaultClient *Client
func defaultClient() *Client {
if DefaultClient == nil {
panic("Please initialize DefaultClient using Configure")
}
return DefaultClient
}
// Configure initializes the default AuthN client with the given config. This is necessary to
// use authn.SubjectFrom without keeping a reference to your own AuthN client.
func Configure(config Config) error {
client, err := NewClient(config)
if err != nil {
return err
}
DefaultClient = client
return nil
}
// SubjectFrom will use the the client configured by Configure to extract a subject from the
// given idToken.
func SubjectFrom(idToken string) (string, error) {
return defaultClient().SubjectFrom(idToken)
}