rebase: bump the github-dependencies group with 3 updates

Bumps the github-dependencies group with 3 updates: [github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://github.com/Azure/azure-sdk-for-go), [github.com/aws/aws-sdk-go-v2/service/sts](https://github.com/aws/aws-sdk-go-v2) and [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang).


Updates `github.com/Azure/azure-sdk-for-go/sdk/azidentity` from 1.8.2 to 1.9.0
- [Release notes](https://github.com/Azure/azure-sdk-for-go/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md)
- [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azidentity/v1.8.2...sdk/azcore/v1.9.0)

Updates `github.com/aws/aws-sdk-go-v2/service/sts` from 1.33.18 to 1.33.19
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Changelog](https://github.com/aws/aws-sdk-go-v2/blob/main/changelog-template.json)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/sns/v1.33.18...service/sns/v1.33.19)

Updates `github.com/prometheus/client_golang` from 1.21.1 to 1.22.0
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.21.1...v1.22.0)

---
updated-dependencies:
- dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azidentity
  dependency-version: 1.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-dependencies
- dependency-name: github.com/aws/aws-sdk-go-v2/service/sts
  dependency-version: 1.33.19
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-dependencies
- dependency-name: github.com/prometheus/client_golang
  dependency-version: 1.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2025-04-14 20:28:11 +00:00
committed by mergify[bot]
parent 0b811ff8b1
commit 72c19ab743
134 changed files with 1349 additions and 27327 deletions

View File

@ -5,16 +5,17 @@ package base
import (
"context"
"errors"
"fmt"
"net/url"
"reflect"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/cache"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/internal/storage"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority"
@ -94,6 +95,7 @@ type AuthResult struct {
// AuthResultMetadata which contains meta data for the AuthResult
type AuthResultMetadata struct {
RefreshOn time.Time
TokenSource TokenSource
}
@ -101,9 +103,8 @@ type TokenSource int
// These are all the types of token flows.
const (
SourceUnknown TokenSource = 0
IdentityProvider TokenSource = 1
Cache TokenSource = 2
TokenSourceIdentityProvider TokenSource = 0
TokenSourceCache TokenSource = 1
)
// AuthResultFromStorage creates an AuthResult from a storage token response (which is generated from the cache).
@ -111,7 +112,6 @@ func AuthResultFromStorage(storageTokenResponse storage.TokenResponse) (AuthResu
if err := storageTokenResponse.AccessToken.Validate(); err != nil {
return AuthResult{}, fmt.Errorf("problem with access token in StorageTokenResponse: %w", err)
}
account := storageTokenResponse.Account
accessToken := storageTokenResponse.AccessToken.Secret
grantedScopes := strings.Split(storageTokenResponse.AccessToken.Scopes, scopeSeparator)
@ -132,7 +132,8 @@ func AuthResultFromStorage(storageTokenResponse storage.TokenResponse) (AuthResu
GrantedScopes: grantedScopes,
DeclinedScopes: nil,
Metadata: AuthResultMetadata{
TokenSource: Cache,
TokenSource: TokenSourceCache,
RefreshOn: storageTokenResponse.AccessToken.RefreshOn.T,
},
}, nil
}
@ -146,10 +147,11 @@ func NewAuthResult(tokenResponse accesstokens.TokenResponse, account shared.Acco
Account: account,
IDToken: tokenResponse.IDToken,
AccessToken: tokenResponse.AccessToken,
ExpiresOn: tokenResponse.ExpiresOn.T,
ExpiresOn: tokenResponse.ExpiresOn,
GrantedScopes: tokenResponse.GrantedScopes.Slice,
Metadata: AuthResultMetadata{
TokenSource: IdentityProvider,
TokenSource: TokenSourceIdentityProvider,
RefreshOn: tokenResponse.RefreshOn.T,
},
}, nil
}
@ -165,6 +167,8 @@ type Client struct {
AuthParams authority.AuthParams // DO NOT EVER MAKE THIS A POINTER! See "Note" in New().
cacheAccessor cache.ExportReplace
cacheAccessorMu *sync.RWMutex
canRefresh map[string]*atomic.Value
canRefreshMu *sync.Mutex
}
// Option is an optional argument to the New constructor.
@ -241,6 +245,8 @@ func New(clientID string, authorityURI string, token *oauth.Client, options ...O
cacheAccessorMu: &sync.RWMutex{},
manager: storage.New(token),
pmanager: storage.NewPartitionedManager(token),
canRefresh: make(map[string]*atomic.Value),
canRefreshMu: &sync.Mutex{},
}
for _, o := range options {
if err = o(&client); err != nil {
@ -345,6 +351,28 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen
if silent.Claims == "" {
ar, err = AuthResultFromStorage(storageTokenResponse)
if err == nil {
if rt := storageTokenResponse.AccessToken.RefreshOn.T; !rt.IsZero() && Now().After(rt) {
b.canRefreshMu.Lock()
refreshValue, ok := b.canRefresh[tenant]
if !ok {
refreshValue = &atomic.Value{}
refreshValue.Store(false)
b.canRefresh[tenant] = refreshValue
}
b.canRefreshMu.Unlock()
if refreshValue.CompareAndSwap(false, true) {
defer refreshValue.Store(false)
// Added a check to see if the token is still same because there is a chance
// that the token is already refreshed by another thread.
// If the token is not same, we don't need to refresh it.
// Which means it refreshed.
if str, err := m.Read(ctx, authParams); err == nil && str.AccessToken.Secret == ar.AccessToken {
if tr, er := b.Token.Credential(ctx, authParams, silent.Credential); er == nil {
return b.AuthResultFromToken(ctx, authParams, tr)
}
}
}
}
ar.AccessToken, err = authParams.AuthnScheme.FormatAccessToken(ar.AccessToken)
return ar, err
}
@ -362,7 +390,7 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen
if err != nil {
return ar, err
}
return b.AuthResultFromToken(ctx, authParams, token, true)
return b.AuthResultFromToken(ctx, authParams, token)
}
func (b Client) AcquireTokenByAuthCode(ctx context.Context, authCodeParams AcquireTokenAuthCodeParameters) (AuthResult, error) {
@ -391,7 +419,7 @@ func (b Client) AcquireTokenByAuthCode(ctx context.Context, authCodeParams Acqui
return AuthResult{}, err
}
return b.AuthResultFromToken(ctx, authParams, token, true)
return b.AuthResultFromToken(ctx, authParams, token)
}
// AcquireTokenOnBehalfOf acquires a security token for an app using middle tier apps access token.
@ -420,15 +448,12 @@ func (b Client) AcquireTokenOnBehalfOf(ctx context.Context, onBehalfOfParams Acq
authParams.UserAssertion = onBehalfOfParams.UserAssertion
token, err := b.Token.OnBehalfOf(ctx, authParams, onBehalfOfParams.Credential)
if err == nil {
ar, err = b.AuthResultFromToken(ctx, authParams, token, true)
ar, err = b.AuthResultFromToken(ctx, authParams, token)
}
return ar, err
}
func (b Client) AuthResultFromToken(ctx context.Context, authParams authority.AuthParams, token accesstokens.TokenResponse, cacheWrite bool) (AuthResult, error) {
if !cacheWrite {
return NewAuthResult(token, shared.Account{})
}
func (b Client) AuthResultFromToken(ctx context.Context, authParams authority.AuthParams, token accesstokens.TokenResponse) (AuthResult, error) {
var m manager = b.manager
if authParams.AuthorizationType == authority.ATOnBehalfOf {
m = b.pmanager
@ -458,6 +483,10 @@ func (b Client) AuthResultFromToken(ctx context.Context, authParams authority.Au
return ar, err
}
// This function wraps time.Now() and is used for refreshing the application
// was created to test the function against refreshin
var Now = time.Now
func (b Client) AllAccounts(ctx context.Context) ([]shared.Account, error) {
if b.cacheAccessor != nil {
b.cacheAccessorMu.RLock()

View File

@ -72,6 +72,7 @@ type AccessToken struct {
ClientID string `json:"client_id,omitempty"`
Secret string `json:"secret,omitempty"`
Scopes string `json:"target,omitempty"`
RefreshOn internalTime.Unix `json:"refresh_on,omitempty"`
ExpiresOn internalTime.Unix `json:"expires_on,omitempty"`
ExtendedExpiresOn internalTime.Unix `json:"extended_expires_on,omitempty"`
CachedAt internalTime.Unix `json:"cached_at,omitempty"`
@ -83,7 +84,7 @@ type AccessToken struct {
}
// NewAccessToken is the constructor for AccessToken.
func NewAccessToken(homeID, env, realm, clientID string, cachedAt, expiresOn, extendedExpiresOn time.Time, scopes, token, tokenType, authnSchemeKeyID string) AccessToken {
func NewAccessToken(homeID, env, realm, clientID string, cachedAt, refreshOn, expiresOn, extendedExpiresOn time.Time, scopes, token, tokenType, authnSchemeKeyID string) AccessToken {
return AccessToken{
HomeAccountID: homeID,
Environment: env,
@ -93,6 +94,7 @@ func NewAccessToken(homeID, env, realm, clientID string, cachedAt, expiresOn, ex
Secret: token,
Scopes: scopes,
CachedAt: internalTime.Unix{T: cachedAt.UTC()},
RefreshOn: internalTime.Unix{T: refreshOn.UTC()},
ExpiresOn: internalTime.Unix{T: expiresOn.UTC()},
ExtendedExpiresOn: internalTime.Unix{T: extendedExpiresOn.UTC()},
TokenType: tokenType,
@ -102,8 +104,9 @@ func NewAccessToken(homeID, env, realm, clientID string, cachedAt, expiresOn, ex
// Key outputs the key that can be used to uniquely look up this entry in a map.
func (a AccessToken) Key() string {
ks := []string{a.HomeAccountID, a.Environment, a.CredentialType, a.ClientID, a.Realm, a.Scopes}
key := strings.Join(
[]string{a.HomeAccountID, a.Environment, a.CredentialType, a.ClientID, a.Realm, a.Scopes},
ks,
shared.CacheKeySeparator,
)
// add token type to key for new access tokens types. skip for bearer token type to

View File

@ -114,7 +114,8 @@ func (m *PartitionedManager) Write(authParameters authority.AuthParams, tokenRes
realm,
clientID,
cachedAt,
tokenResponse.ExpiresOn.T,
tokenResponse.RefreshOn.T,
tokenResponse.ExpiresOn,
tokenResponse.ExtExpiresOn.T,
target,
tokenResponse.AccessToken,

View File

@ -173,6 +173,7 @@ func (m *Manager) Write(authParameters authority.AuthParams, tokenResponse acces
environment := authParameters.AuthorityInfo.Host
realm := authParameters.AuthorityInfo.Tenant
clientID := authParameters.ClientID
target := strings.Join(tokenResponse.GrantedScopes.Slice, scopeSeparator)
cachedAt := time.Now()
authnSchemeKeyID := authParameters.AuthnScheme.KeyID()
@ -193,7 +194,8 @@ func (m *Manager) Write(authParameters authority.AuthParams, tokenResponse acces
realm,
clientID,
cachedAt,
tokenResponse.ExpiresOn.T,
tokenResponse.RefreshOn.T,
tokenResponse.ExpiresOn,
tokenResponse.ExtExpiresOn.T,
target,
tokenResponse.AccessToken,
@ -265,6 +267,9 @@ func (m *Manager) aadMetadataFromCache(ctx context.Context, authorityInfo author
}
func (m *Manager) aadMetadata(ctx context.Context, authorityInfo authority.Info) (authority.InstanceDiscoveryMetadata, error) {
if m.requests == nil {
return authority.InstanceDiscoveryMetadata{}, fmt.Errorf("httpclient in oauth instance for fetching metadata is nil")
}
m.aadCacheMu.Lock()
defer m.aadCacheMu.Unlock()
discoveryResponse, err := m.requests.AADInstanceDiscovery(ctx, authorityInfo)
@ -459,6 +464,7 @@ func (m *Manager) readAccount(homeAccountID string, envAliases []string, realm s
func (m *Manager) writeAccount(account shared.Account) error {
key := account.Key()
m.contractMu.Lock()
defer m.contractMu.Unlock()
m.contract.Accounts[key] = account

View File

@ -31,4 +31,6 @@ type TokenProviderResult struct {
AccessToken string
// ExpiresInSeconds is the lifetime of the token in seconds
ExpiresInSeconds int
// RefreshInSeconds indicates the suggested time to refresh the token, if any
RefreshInSeconds int
}

View File

@ -146,7 +146,8 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
// Note: It is a little weird we handle some errors by not going to the failPage. If they all should,
// change this to s.error() and make s.error() write the failPage instead of an error code.
_, _ = w.Write([]byte(fmt.Sprintf(failPage, headerErr, desc)))
s.putResult(Result{Err: fmt.Errorf(desc)})
s.putResult(Result{Err: fmt.Errorf("%s", desc)})
return
}

View File

@ -111,7 +111,7 @@ func (t *Client) Credential(ctx context.Context, authParams authority.AuthParams
Scopes: scopes,
TenantID: authParams.AuthorityInfo.Tenant,
}
tr, err := cred.TokenProvider(ctx, params)
pr, err := cred.TokenProvider(ctx, params)
if err != nil {
if len(scopes) == 0 {
err = fmt.Errorf("token request had an empty authority.AuthParams.Scopes, which may cause the following error: %w", err)
@ -119,14 +119,18 @@ func (t *Client) Credential(ctx context.Context, authParams authority.AuthParams
}
return accesstokens.TokenResponse{}, err
}
return accesstokens.TokenResponse{
TokenType: authParams.AuthnScheme.AccessTokenType(),
AccessToken: tr.AccessToken,
ExpiresOn: internalTime.DurationTime{
T: now.Add(time.Duration(tr.ExpiresInSeconds) * time.Second),
},
tr := accesstokens.TokenResponse{
TokenType: authParams.AuthnScheme.AccessTokenType(),
AccessToken: pr.AccessToken,
ExpiresOn: now.Add(time.Duration(pr.ExpiresInSeconds) * time.Second),
GrantedScopes: accesstokens.Scopes{Slice: authParams.Scopes},
}, nil
}
if pr.RefreshInSeconds > 0 {
tr.RefreshOn = internalTime.DurationTime{
T: now.Add(time.Duration(pr.RefreshInSeconds) * time.Second),
}
}
return tr, nil
}
if err := t.resolveEndpoint(ctx, &authParams, ""); err != nil {

View File

@ -17,6 +17,7 @@ import (
/* #nosec */
"crypto/sha1"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
@ -68,7 +69,7 @@ type DeviceCodeResponse struct {
UserCode string `json:"user_code"`
DeviceCode string `json:"device_code"`
VerificationURL string `json:"verification_url"`
VerificationURL string `json:"verification_uri"`
ExpiresIn int `json:"expires_in"`
Interval int `json:"interval"`
Message string `json:"message"`
@ -112,19 +113,31 @@ func (c *Credential) JWT(ctx context.Context, authParams authority.AuthParams) (
}
return c.AssertionCallback(ctx, options)
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
claims := jwt.MapClaims{
"aud": authParams.Endpoints.TokenEndpoint,
"exp": json.Number(strconv.FormatInt(time.Now().Add(10*time.Minute).Unix(), 10)),
"iss": authParams.ClientID,
"jti": uuid.New().String(),
"nbf": json.Number(strconv.FormatInt(time.Now().Unix(), 10)),
"sub": authParams.ClientID,
})
}
isADFSorDSTS := authParams.AuthorityInfo.AuthorityType == authority.ADFS ||
authParams.AuthorityInfo.AuthorityType == authority.DSTS
var signingMethod jwt.SigningMethod = jwt.SigningMethodPS256
thumbprintKey := "x5t#S256"
if isADFSorDSTS {
signingMethod = jwt.SigningMethodRS256
thumbprintKey = "x5t"
}
token := jwt.NewWithClaims(signingMethod, claims)
token.Header = map[string]interface{}{
"alg": "RS256",
"typ": "JWT",
"x5t": base64.StdEncoding.EncodeToString(thumbprint(c.Cert)),
"alg": signingMethod.Alg(),
"typ": "JWT",
thumbprintKey: base64.StdEncoding.EncodeToString(thumbprint(c.Cert, signingMethod.Alg())),
}
if authParams.SendX5C {
@ -133,17 +146,23 @@ func (c *Credential) JWT(ctx context.Context, authParams authority.AuthParams) (
assertion, err := token.SignedString(c.Key)
if err != nil {
return "", fmt.Errorf("unable to sign a JWT token using private key: %w", err)
return "", fmt.Errorf("unable to sign JWT token: %w", err)
}
return assertion, nil
}
// thumbprint runs the asn1.Der bytes through sha1 for use in the x5t parameter of JWT.
// https://tools.ietf.org/html/rfc7517#section-4.8
func thumbprint(cert *x509.Certificate) []byte {
/* #nosec */
a := sha1.Sum(cert.Raw)
return a[:]
func thumbprint(cert *x509.Certificate, alg string) []byte {
switch alg {
case jwt.SigningMethodRS256.Name: // identity providers like ADFS don't support SHA256 assertions, so need to support this
hash := sha1.Sum(cert.Raw) /* #nosec */
return hash[:]
default:
hash := sha256.Sum256(cert.Raw)
return hash[:]
}
}
// Client represents the REST calls to get tokens from token generator backends.
@ -262,11 +281,7 @@ func (c Client) FromClientSecret(ctx context.Context, authParameters authority.A
qv.Set(clientID, authParameters.ClientID)
addScopeQueryParam(qv, authParameters)
token, err := c.doTokenResp(ctx, authParameters, qv)
if err != nil {
return token, fmt.Errorf("FromClientSecret(): %w", err)
}
return token, nil
return c.doTokenResp(ctx, authParameters, qv)
}
func (c Client) FromAssertion(ctx context.Context, authParameters authority.AuthParams, assertion string) (TokenResponse, error) {
@ -281,11 +296,7 @@ func (c Client) FromAssertion(ctx context.Context, authParameters authority.Auth
qv.Set(clientInfo, clientInfoVal)
addScopeQueryParam(qv, authParameters)
token, err := c.doTokenResp(ctx, authParameters, qv)
if err != nil {
return token, fmt.Errorf("FromAssertion(): %w", err)
}
return token, nil
return c.doTokenResp(ctx, authParameters, qv)
}
func (c Client) FromUserAssertionClientSecret(ctx context.Context, authParameters authority.AuthParams, userAssertion string, clientSecret string) (TokenResponse, error) {

View File

@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"time"
@ -173,14 +174,75 @@ type TokenResponse struct {
FamilyID string `json:"foci"`
IDToken IDToken `json:"id_token"`
ClientInfo ClientInfo `json:"client_info"`
ExpiresOn internalTime.DurationTime `json:"expires_in"`
RefreshOn internalTime.DurationTime `json:"refresh_in,omitempty"`
ExpiresOn time.Time `json:"-"`
ExtExpiresOn internalTime.DurationTime `json:"ext_expires_in"`
GrantedScopes Scopes `json:"scope"`
DeclinedScopes []string // This is derived
AdditionalFields map[string]interface{}
scopesComputed bool
}
scopesComputed bool
func (tr *TokenResponse) UnmarshalJSON(data []byte) error {
type Alias TokenResponse
aux := &struct {
ExpiresIn internalTime.DurationTime `json:"expires_in,omitempty"`
ExpiresOn any `json:"expires_on,omitempty"`
*Alias
}{
Alias: (*Alias)(tr),
}
// Unmarshal the JSON data into the aux struct
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
// Function to parse different date formats
// This is a workaround for the issue described here:
// https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/issues/4963
parseExpiresOn := func(expiresOn string) (time.Time, error) {
var formats = []string{
"01/02/2006 15:04:05", // MM/dd/yyyy HH:mm:ss
"2006-01-02 15:04:05", // yyyy-MM-dd HH:mm:ss
time.RFC3339Nano, // ISO 8601 (with nanosecond precision)
}
for _, format := range formats {
if t, err := time.Parse(format, expiresOn); err == nil {
return t, nil
}
}
return time.Time{}, fmt.Errorf("invalid ExpiresOn format: %s", expiresOn)
}
if expiresOnStr, ok := aux.ExpiresOn.(string); ok {
if ts, err := strconv.ParseInt(expiresOnStr, 10, 64); err == nil {
tr.ExpiresOn = time.Unix(ts, 0)
return nil
}
if expiresOnStr != "" {
if t, err := parseExpiresOn(expiresOnStr); err != nil {
return err
} else {
tr.ExpiresOn = t
return nil
}
}
}
// Check if ExpiresOn is a number (Unix timestamp or ISO 8601)
if expiresOnNum, ok := aux.ExpiresOn.(float64); ok {
tr.ExpiresOn = time.Unix(int64(expiresOnNum), 0)
return nil
}
if !aux.ExpiresIn.T.IsZero() {
tr.ExpiresOn = aux.ExpiresIn.T
return nil
}
return errors.New("expires_in and expires_on are both missing or invalid")
}
// ComputeScope computes the final scopes based on what was granted by the server and

View File

@ -98,7 +98,7 @@ func (c *Client) JSONCall(ctx context.Context, endpoint string, headers http.Hea
if resp != nil {
if err := unmarshal(data, resp); err != nil {
return fmt.Errorf("json decode error: %w\njson message bytes were: %s", err, string(data))
return errors.InvalidJsonErr{Err: fmt.Errorf("json decode error: %w\njson message bytes were: %s", err, string(data))}
}
}
return nil
@ -221,7 +221,7 @@ func (c *Client) URLFormCall(ctx context.Context, endpoint string, qv url.Values
}
if resp != nil {
if err := unmarshal(data, resp); err != nil {
return fmt.Errorf("json decode error: %w\nraw message was: %s", err, string(data))
return errors.InvalidJsonErr{Err: fmt.Errorf("json decode error: %w\nraw message was: %s", err, string(data))}
}
}
return nil

View File

@ -5,4 +5,4 @@
package version
// Version is the version of this client package that is communicated to the server.
const Version = "1.2.0"
const Version = "1.4.2"