Skip to content

Commit 3f5cdcd

Browse files
committed
token will only expose credentials
1 parent 77fe203 commit 3f5cdcd

File tree

3 files changed

+121
-38
lines changed

3 files changed

+121
-38
lines changed

identity_provider.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ type IdentityProviderResponse interface {
2323
AccessToken() *azcore.AccessToken
2424
}
2525

26+
// IdentityProviderResponseParserFunc is a function that parses the token and returns the username and password.
27+
type IdentityProviderResponseParserFunc func(response IdentityProviderResponse) (*Token, error)
28+
2629
// IdentityProvider is an interface that defines the methods for an identity provider.
2730
// It is used to request a token for authentication.
2831
// The identity provider is responsible for providing the raw authentication token.

token.go

Lines changed: 71 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,86 @@
11
package entraid
22

3-
import "time"
3+
import (
4+
"time"
5+
)
46

57
// Token represents the authentication token used to access the Entraid API.
68
// It contains the username, password, expiration time, time to live, and the raw token.
79
// The token is used to authenticate the user and authorize access to the API.
810
// The token is typically obtained from an identity provider and is used to access the Entraid API.
911
// The token is valid for a limited time and must be refreshed periodically.
1012
type Token struct {
11-
// Username is the username of the user.
12-
Username string `json:"username"`
13-
// Password is the password of the user.
14-
Password string `json:"password"`
15-
// ExpiresOn is the expiration time of the token.
16-
ExpiresOn time.Time `json:"expires_on"`
17-
// TTL is the time to live of the token.
18-
TTL int64 `json:"ttl"`
19-
// RawToken is the authentication token.
20-
RawToken string `json:"raw_token"`
13+
// username is the username of the user.
14+
username string `json:"username"`
15+
// password is the password of the user.
16+
password string `json:"password"`
17+
// expiresOn is the expiration time of the token.
18+
expiresOn time.Time `json:"expires_on"`
19+
// ttl is the time to live of the token.
20+
ttl int64 `json:"ttl"`
21+
// rawToken is the authentication token.
22+
rawToken string `json:"raw_token"`
23+
// receivedAt is the time when the token was received.
24+
receivedAt time.Time `json:"received_at"`
2125
}
2226

23-
// IdentityProviderResponseParserFunc is a function that parses the token and returns the username and password.
24-
type IdentityProviderResponseParserFunc func(response IdentityProviderResponse) (*Token, error)
27+
// BasicAuth returns the username and password for basic authentication.
28+
// It implements the auth.Credentials interface.
29+
func (t *Token) BasicAuth() (string, string) {
30+
return t.username, t.password
31+
}
2532

26-
// copyToken creates a copy of the token.
27-
func copyToken(token *Token) *Token {
33+
// RawCredentials returns the raw credentials for authentication.
34+
// It implements the auth.Credentials interface.
35+
func (t *Token) RawCredentials() string {
36+
return t.rawToken
37+
}
38+
39+
// IsExpired checks if the token is expired.
40+
// It returns true if the token is expired, false otherwise.
41+
func (t *Token) IsExpired() bool {
42+
return t.expiresOn.Before(time.Now())
43+
}
44+
45+
// IsValid checks if the token is valid.
46+
// It returns true if the token is valid, false otherwise.
47+
func (t *Token) IsValid() bool {
48+
return !t.IsExpired()
49+
}
50+
51+
// ExpirationOn returns the expiration time of the token.
52+
func (t *Token) ExpirationOn() time.Time {
53+
return t.expiresOn
54+
}
55+
56+
// NewToken creates a new token with the specified username, password, raw token, expiration time, received at time, and time to live.
57+
func NewToken(username, password, rawToken string, expiresOn, receivedAt time.Time, ttl int64) *Token {
2858
return &Token{
29-
Username: token.Username,
30-
Password: token.Password,
31-
ExpiresOn: token.ExpiresOn,
32-
TTL: token.TTL,
33-
RawToken: token.RawToken,
59+
username: username,
60+
password: password,
61+
expiresOn: expiresOn,
62+
receivedAt: receivedAt,
63+
ttl: ttl,
64+
rawToken: rawToken,
3465
}
3566
}
67+
68+
// copyToken creates a copy of the token.
69+
func copyToken(token *Token) *Token {
70+
return NewToken(token.username, token.password, token.rawToken, token.expiresOn, token.receivedAt, token.ttl)
71+
}
72+
73+
// compareCredentials two tokens if they are the same credentials
74+
func (t *Token) compareCredentials(token *Token) bool {
75+
return t.username == token.username && t.password == token.password
76+
}
77+
78+
// compareRawCredentials two tokens if they are the same raw credentials
79+
func (t *Token) compareRawCredentials(token *Token) bool {
80+
return t.rawToken == token.rawToken
81+
}
82+
83+
// compareToken compares two tokens if they are the same token
84+
func (t *Token) compareToken(token *Token) bool {
85+
return t.compareCredentials(token) && t.compareRawCredentials(token)
86+
}

token_manager.go

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"net"
88
"sync"
99
"time"
10+
11+
"github.com/golang-jwt/jwt/v5"
1012
)
1113

1214
const MinTokenTTL = 30 * time.Minute
@@ -98,11 +100,30 @@ var defaultIdentityProviderResponseParser = func(response IdentityProviderRespon
98100
if authResult == nil {
99101
return nil, fmt.Errorf("auth result is nil")
100102
}
103+
rawToken = authResult.IDToken.RawToken
104+
105+
username = authResult.IDToken.Oid
106+
password = rawToken
107+
expiresOn = authResult.ExpiresOn.UTC()
101108
case typeAccessToken:
102109
accessToken := response.AccessToken()
103110
if accessToken == nil {
104111
return nil, fmt.Errorf("access token is nil")
105112
}
113+
114+
claims := struct {
115+
jwt.RegisteredClaims
116+
Oid string `json:"oid"`
117+
}{}
118+
119+
_, err := jwt.ParseWithClaims(accessToken.Token, claims, nil)
120+
if err != nil {
121+
return nil, fmt.Errorf("failed to parse jwt token: %w", err)
122+
}
123+
rawToken = accessToken.Token
124+
username = claims.Oid
125+
password = rawToken
126+
expiresOn = accessToken.ExpiresOn.UTC()
106127
default:
107128
return nil, fmt.Errorf("unknown response type: %s", response.Type())
108129
}
@@ -118,13 +139,14 @@ var defaultIdentityProviderResponseParser = func(response IdentityProviderRespon
118139
}
119140
// parse token as jwt token and get claims
120141

121-
return &Token{
122-
Username: "username",
123-
Password: rawToken,
124-
ExpiresOn: expiresOn.UTC(),
125-
TTL: expiresOn.UTC().Unix() - time.Now().UTC().Unix(),
126-
RawToken: rawToken,
127-
}, nil
142+
return NewToken(
143+
username,
144+
password,
145+
rawToken,
146+
expiresOn,
147+
time.Now().UTC(),
148+
int64(expiresOn.Sub(time.Now()).Seconds()),
149+
), nil
128150
}
129151

130152
// NewTokenManager creates a new TokenManager.
@@ -142,12 +164,12 @@ func NewTokenManager(idp IdentityProvider, options TokenManagerOptions) (TokenMa
142164
}
143165

144166
return &entraidTokenManager{
145-
idp: idp,
146-
token: nil,
147-
closed: make(chan struct{}),
148-
expirationRefreshRatio: options.ExpirationRefreshRatio,
149-
tokenParser: options.TokenParser,
150-
retryOptions: options.RetryOptions,
167+
idp: idp,
168+
token: nil,
169+
closed: make(chan struct{}),
170+
expirationRefreshRatio: options.ExpirationRefreshRatio,
171+
identityProviderResponseParser: options.IdentityProviderResponseParser,
172+
retryOptions: options.RetryOptions,
151173
}, nil
152174
}
153175

@@ -183,7 +205,7 @@ type entraidTokenManager struct {
183205
}
184206

185207
func (e *entraidTokenManager) GetToken() (*Token, error) {
186-
if e.token != nil && e.token.ExpiresOn.After(time.Now().Add(MinTokenTTL)) {
208+
if e.token != nil && e.token.expiresOn.After(time.Now().Add(MinTokenTTL)) {
187209
// copy the token so the caller can't modify it
188210
return copyToken(e.token), nil
189211
}
@@ -240,9 +262,19 @@ func (e *entraidTokenManager) Start(listener TokenListener) (cancelFunc, error)
240262
// Simulate token refresh
241263
for {
242264
select {
243-
case <-time.After(time.Until(token.ExpiresOn) * time.Duration(e.expirationRefreshRatio)):
265+
case <-e.closed:
266+
// Token manager is closed, stop the loop
267+
return
268+
case <-time.After(time.Until(token.expiresOn) * time.Duration(e.expirationRefreshRatio)):
244269
// Token is about to expire, refresh it
245270
for i := 0; i < e.retryOptions.MaxAttempts; i++ {
271+
select {
272+
case <-e.closed:
273+
// Token manager is closed, stop the loop
274+
return
275+
default:
276+
// continue to next attempt
277+
}
246278
token, err := e.GetToken()
247279
if err == nil {
248280
listener.OnTokenNext(token)
@@ -276,9 +308,6 @@ func (e *entraidTokenManager) Start(listener TokenListener) (cancelFunc, error)
276308
delay = time.Duration(e.retryOptions.MaxDelayMs) * time.Millisecond
277309
}
278310
}
279-
case <-e.closed:
280-
// Token manager is closed, stop the loop
281-
return
282311
}
283312
}
284313
}(e.listener)

0 commit comments

Comments
 (0)