mirror of
https://github.com/distribution/distribution.git
synced 2025-09-16 23:29:30 +00:00
bump azure sdk
v1.3.0 of azidentity introduces support to workload identity. Signed-off-by: Flavian Missi <fmissi@redhat.com>
This commit is contained in:
194
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/base.go
generated
vendored
194
vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/base.go
generated
vendored
@@ -10,6 +10,7 @@ import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/cache"
|
||||
@@ -27,27 +28,21 @@ const (
|
||||
)
|
||||
|
||||
// manager provides an internal cache. It is defined to allow faking the cache in tests.
|
||||
// In all production use it is a *storage.Manager.
|
||||
// In production it's a *storage.Manager or *storage.PartitionedManager.
|
||||
type manager interface {
|
||||
Read(ctx context.Context, authParameters authority.AuthParams, account shared.Account) (storage.TokenResponse, error)
|
||||
Write(authParameters authority.AuthParams, tokenResponse accesstokens.TokenResponse) (shared.Account, error)
|
||||
cache.Serializer
|
||||
Read(context.Context, authority.AuthParams) (storage.TokenResponse, error)
|
||||
Write(authority.AuthParams, accesstokens.TokenResponse) (shared.Account, error)
|
||||
}
|
||||
|
||||
// accountManager is a manager that also caches accounts. In production it's a *storage.Manager.
|
||||
type accountManager interface {
|
||||
manager
|
||||
AllAccounts() []shared.Account
|
||||
Account(homeAccountID string) shared.Account
|
||||
RemoveAccount(account shared.Account, clientID string)
|
||||
}
|
||||
|
||||
// partitionedManager provides an internal cache. It is defined to allow faking the cache in tests.
|
||||
// In all production use it is a *storage.PartitionedManager.
|
||||
type partitionedManager interface {
|
||||
Read(ctx context.Context, authParameters authority.AuthParams) (storage.TokenResponse, error)
|
||||
Write(authParameters authority.AuthParams, tokenResponse accesstokens.TokenResponse) (shared.Account, error)
|
||||
}
|
||||
|
||||
type noopCacheAccessor struct{}
|
||||
|
||||
func (n noopCacheAccessor) Replace(cache cache.Unmarshaler, key string) {}
|
||||
func (n noopCacheAccessor) Export(cache cache.Marshaler, key string) {}
|
||||
|
||||
// AcquireTokenSilentParameters contains the parameters to acquire a token silently (from cache).
|
||||
type AcquireTokenSilentParameters struct {
|
||||
Scopes []string
|
||||
@@ -133,12 +128,14 @@ func NewAuthResult(tokenResponse accesstokens.TokenResponse, account shared.Acco
|
||||
// Client is a base client that provides access to common methods and primatives that
|
||||
// can be used by multiple clients.
|
||||
type Client struct {
|
||||
Token *oauth.Client
|
||||
manager manager // *storage.Manager or fakeManager in tests
|
||||
pmanager partitionedManager // *storage.PartitionedManager or fakeManager in tests
|
||||
Token *oauth.Client
|
||||
manager accountManager // *storage.Manager or fakeManager in tests
|
||||
// pmanager is a partitioned cache for OBO authentication. *storage.PartitionedManager or fakeManager in tests
|
||||
pmanager manager
|
||||
|
||||
AuthParams authority.AuthParams // DO NOT EVER MAKE THIS A POINTER! See "Note" in New().
|
||||
cacheAccessor cache.ExportReplace
|
||||
AuthParams authority.AuthParams // DO NOT EVER MAKE THIS A POINTER! See "Note" in New().
|
||||
cacheAccessor cache.ExportReplace
|
||||
cacheAccessorMu *sync.RWMutex
|
||||
}
|
||||
|
||||
// Option is an optional argument to the New constructor.
|
||||
@@ -210,11 +207,11 @@ func New(clientID string, authorityURI string, token *oauth.Client, options ...O
|
||||
}
|
||||
authParams := authority.NewAuthParams(clientID, authInfo)
|
||||
client := Client{ // Note: Hey, don't even THINK about making Base into *Base. See "design notes" in public.go and confidential.go
|
||||
Token: token,
|
||||
AuthParams: authParams,
|
||||
cacheAccessor: noopCacheAccessor{},
|
||||
manager: storage.New(token),
|
||||
pmanager: storage.NewPartitionedManager(token),
|
||||
Token: token,
|
||||
AuthParams: authParams,
|
||||
cacheAccessorMu: &sync.RWMutex{},
|
||||
manager: storage.New(token),
|
||||
pmanager: storage.NewPartitionedManager(token),
|
||||
}
|
||||
for _, o := range options {
|
||||
if err = o(&client); err != nil {
|
||||
@@ -280,11 +277,12 @@ func (b Client) AuthCodeURL(ctx context.Context, clientID, redirectURI string, s
|
||||
}
|
||||
|
||||
func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilentParameters) (AuthResult, error) {
|
||||
// when tenant == "", the caller didn't specify a tenant and WithTenant will use the client's configured tenant
|
||||
ar := AuthResult{}
|
||||
// when tenant == "", the caller didn't specify a tenant and WithTenant will choose the client's configured tenant
|
||||
tenant := silent.TenantID
|
||||
authParams, err := b.AuthParams.WithTenant(tenant)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
return ar, err
|
||||
}
|
||||
authParams.Scopes = silent.Scopes
|
||||
authParams.HomeAccountID = silent.Account.HomeAccountID
|
||||
@@ -292,52 +290,45 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen
|
||||
authParams.Claims = silent.Claims
|
||||
authParams.UserAssertion = silent.UserAssertion
|
||||
|
||||
var storageTokenResponse storage.TokenResponse
|
||||
if authParams.AuthorizationType == authority.ATOnBehalfOf {
|
||||
if s, ok := b.pmanager.(cache.Serializer); ok {
|
||||
suggestedCacheKey := authParams.CacheKey(silent.IsAppCache)
|
||||
b.cacheAccessor.Replace(s, suggestedCacheKey)
|
||||
defer b.cacheAccessor.Export(s, suggestedCacheKey)
|
||||
}
|
||||
storageTokenResponse, err = b.pmanager.Read(ctx, authParams)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
} else {
|
||||
if s, ok := b.manager.(cache.Serializer); ok {
|
||||
suggestedCacheKey := authParams.CacheKey(silent.IsAppCache)
|
||||
b.cacheAccessor.Replace(s, suggestedCacheKey)
|
||||
defer b.cacheAccessor.Export(s, suggestedCacheKey)
|
||||
}
|
||||
m := b.pmanager
|
||||
if authParams.AuthorizationType != authority.ATOnBehalfOf {
|
||||
authParams.AuthorizationType = authority.ATRefreshToken
|
||||
storageTokenResponse, err = b.manager.Read(ctx, authParams, silent.Account)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
m = b.manager
|
||||
}
|
||||
if b.cacheAccessor != nil {
|
||||
key := authParams.CacheKey(silent.IsAppCache)
|
||||
b.cacheAccessorMu.RLock()
|
||||
err = b.cacheAccessor.Replace(ctx, m, cache.ReplaceHints{PartitionKey: key})
|
||||
b.cacheAccessorMu.RUnlock()
|
||||
}
|
||||
if err != nil {
|
||||
return ar, err
|
||||
}
|
||||
storageTokenResponse, err := m.Read(ctx, authParams)
|
||||
if err != nil {
|
||||
return ar, err
|
||||
}
|
||||
|
||||
// ignore cached access tokens when given claims
|
||||
if silent.Claims == "" {
|
||||
result, err := AuthResultFromStorage(storageTokenResponse)
|
||||
ar, err = AuthResultFromStorage(storageTokenResponse)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
return ar, err
|
||||
}
|
||||
}
|
||||
|
||||
// redeem a cached refresh token, if available
|
||||
if reflect.ValueOf(storageTokenResponse.RefreshToken).IsZero() {
|
||||
return AuthResult{}, errors.New("no token found")
|
||||
return ar, errors.New("no token found")
|
||||
}
|
||||
var cc *accesstokens.Credential
|
||||
if silent.RequestType == accesstokens.ATConfidential {
|
||||
cc = silent.Credential
|
||||
}
|
||||
|
||||
token, err := b.Token.Refresh(ctx, silent.RequestType, authParams, cc, storageTokenResponse.RefreshToken)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
return ar, err
|
||||
}
|
||||
|
||||
return b.AuthResultFromToken(ctx, authParams, token, true)
|
||||
}
|
||||
|
||||
@@ -405,63 +396,72 @@ func (b Client) AuthResultFromToken(ctx context.Context, authParams authority.Au
|
||||
if !cacheWrite {
|
||||
return NewAuthResult(token, shared.Account{})
|
||||
}
|
||||
|
||||
var account shared.Account
|
||||
var err error
|
||||
var m manager = b.manager
|
||||
if authParams.AuthorizationType == authority.ATOnBehalfOf {
|
||||
if s, ok := b.pmanager.(cache.Serializer); ok {
|
||||
suggestedCacheKey := token.CacheKey(authParams)
|
||||
b.cacheAccessor.Replace(s, suggestedCacheKey)
|
||||
defer b.cacheAccessor.Export(s, suggestedCacheKey)
|
||||
}
|
||||
account, err = b.pmanager.Write(authParams, token)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
} else {
|
||||
if s, ok := b.manager.(cache.Serializer); ok {
|
||||
suggestedCacheKey := token.CacheKey(authParams)
|
||||
b.cacheAccessor.Replace(s, suggestedCacheKey)
|
||||
defer b.cacheAccessor.Export(s, suggestedCacheKey)
|
||||
}
|
||||
account, err = b.manager.Write(authParams, token)
|
||||
m = b.pmanager
|
||||
}
|
||||
key := token.CacheKey(authParams)
|
||||
if b.cacheAccessor != nil {
|
||||
b.cacheAccessorMu.Lock()
|
||||
defer b.cacheAccessorMu.Unlock()
|
||||
err := b.cacheAccessor.Replace(ctx, m, cache.ReplaceHints{PartitionKey: key})
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
}
|
||||
return NewAuthResult(token, account)
|
||||
account, err := m.Write(authParams, token)
|
||||
if err != nil {
|
||||
return AuthResult{}, err
|
||||
}
|
||||
ar, err := NewAuthResult(token, account)
|
||||
if err == nil && b.cacheAccessor != nil {
|
||||
err = b.cacheAccessor.Export(ctx, b.manager, cache.ExportHints{PartitionKey: key})
|
||||
}
|
||||
return ar, err
|
||||
}
|
||||
|
||||
func (b Client) AllAccounts() []shared.Account {
|
||||
if s, ok := b.manager.(cache.Serializer); ok {
|
||||
suggestedCacheKey := b.AuthParams.CacheKey(false)
|
||||
b.cacheAccessor.Replace(s, suggestedCacheKey)
|
||||
defer b.cacheAccessor.Export(s, suggestedCacheKey)
|
||||
func (b Client) AllAccounts(ctx context.Context) ([]shared.Account, error) {
|
||||
if b.cacheAccessor != nil {
|
||||
b.cacheAccessorMu.RLock()
|
||||
defer b.cacheAccessorMu.RUnlock()
|
||||
key := b.AuthParams.CacheKey(false)
|
||||
err := b.cacheAccessor.Replace(ctx, b.manager, cache.ReplaceHints{PartitionKey: key})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
accounts := b.manager.AllAccounts()
|
||||
return accounts
|
||||
return b.manager.AllAccounts(), nil
|
||||
}
|
||||
|
||||
func (b Client) Account(homeAccountID string) shared.Account {
|
||||
authParams := b.AuthParams // This is a copy, as we dont' have a pointer receiver and .AuthParams is not a pointer.
|
||||
authParams.AuthorizationType = authority.AccountByID
|
||||
authParams.HomeAccountID = homeAccountID
|
||||
if s, ok := b.manager.(cache.Serializer); ok {
|
||||
suggestedCacheKey := b.AuthParams.CacheKey(false)
|
||||
b.cacheAccessor.Replace(s, suggestedCacheKey)
|
||||
defer b.cacheAccessor.Export(s, suggestedCacheKey)
|
||||
func (b Client) Account(ctx context.Context, homeAccountID string) (shared.Account, error) {
|
||||
if b.cacheAccessor != nil {
|
||||
b.cacheAccessorMu.RLock()
|
||||
defer b.cacheAccessorMu.RUnlock()
|
||||
authParams := b.AuthParams // This is a copy, as we don't have a pointer receiver and .AuthParams is not a pointer.
|
||||
authParams.AuthorizationType = authority.AccountByID
|
||||
authParams.HomeAccountID = homeAccountID
|
||||
key := b.AuthParams.CacheKey(false)
|
||||
err := b.cacheAccessor.Replace(ctx, b.manager, cache.ReplaceHints{PartitionKey: key})
|
||||
if err != nil {
|
||||
return shared.Account{}, err
|
||||
}
|
||||
}
|
||||
account := b.manager.Account(homeAccountID)
|
||||
return account
|
||||
return b.manager.Account(homeAccountID), nil
|
||||
}
|
||||
|
||||
// RemoveAccount removes all the ATs, RTs and IDTs from the cache associated with this account.
|
||||
func (b Client) RemoveAccount(account shared.Account) {
|
||||
if s, ok := b.manager.(cache.Serializer); ok {
|
||||
suggestedCacheKey := b.AuthParams.CacheKey(false)
|
||||
b.cacheAccessor.Replace(s, suggestedCacheKey)
|
||||
defer b.cacheAccessor.Export(s, suggestedCacheKey)
|
||||
func (b Client) RemoveAccount(ctx context.Context, account shared.Account) error {
|
||||
if b.cacheAccessor == nil {
|
||||
b.manager.RemoveAccount(account, b.AuthParams.ClientID)
|
||||
return nil
|
||||
}
|
||||
b.cacheAccessorMu.Lock()
|
||||
defer b.cacheAccessorMu.Unlock()
|
||||
key := b.AuthParams.CacheKey(false)
|
||||
err := b.cacheAccessor.Replace(ctx, b.manager, cache.ReplaceHints{PartitionKey: key})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.manager.RemoveAccount(account, b.AuthParams.ClientID)
|
||||
return b.cacheAccessor.Export(ctx, b.manager, cache.ExportHints{PartitionKey: key})
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ func isMatchingScopes(scopesOne []string, scopesTwo string) bool {
|
||||
}
|
||||
|
||||
// Read reads a storage token from the cache if it exists.
|
||||
func (m *Manager) Read(ctx context.Context, authParameters authority.AuthParams, account shared.Account) (TokenResponse, error) {
|
||||
func (m *Manager) Read(ctx context.Context, authParameters authority.AuthParams) (TokenResponse, error) {
|
||||
tr := TokenResponse{}
|
||||
homeAccountID := authParameters.HomeAccountID
|
||||
realm := authParameters.AuthorityInfo.Tenant
|
||||
@@ -103,7 +103,8 @@ func (m *Manager) Read(ctx context.Context, authParameters authority.AuthParams,
|
||||
accessToken := m.readAccessToken(homeAccountID, aliases, realm, clientID, scopes)
|
||||
tr.AccessToken = accessToken
|
||||
|
||||
if account.IsZero() {
|
||||
if homeAccountID == "" {
|
||||
// caller didn't specify a user, so there's no reason to search for an ID or refresh token
|
||||
return tr, nil
|
||||
}
|
||||
// errors returned by read* methods indicate a cache miss and are therefore non-fatal. We continue populating
|
||||
@@ -122,7 +123,7 @@ func (m *Manager) Read(ctx context.Context, authParameters authority.AuthParams,
|
||||
}
|
||||
}
|
||||
|
||||
account, err = m.readAccount(homeAccountID, aliases, realm)
|
||||
account, err := m.readAccount(homeAccountID, aliases, realm)
|
||||
if err == nil {
|
||||
tr.Account = account
|
||||
}
|
||||
@@ -493,6 +494,8 @@ func (m *Manager) update(cache *Contract) {
|
||||
|
||||
// Marshal implements cache.Marshaler.
|
||||
func (m *Manager) Marshal() ([]byte, error) {
|
||||
m.contractMu.RLock()
|
||||
defer m.contractMu.RUnlock()
|
||||
return json.Marshal(m.contract)
|
||||
}
|
||||
|
||||
|
@@ -76,12 +76,17 @@ func (t *Client) ResolveEndpoints(ctx context.Context, authorityInfo authority.I
|
||||
return t.Resolver.ResolveEndpoints(ctx, authorityInfo, userPrincipalName)
|
||||
}
|
||||
|
||||
// AADInstanceDiscovery attempts to discover a tenant endpoint (used in OIDC auth with an authorization endpoint).
|
||||
// This is done by AAD which allows for aliasing of tenants (windows.sts.net is the same as login.windows.com).
|
||||
func (t *Client) AADInstanceDiscovery(ctx context.Context, authorityInfo authority.Info) (authority.InstanceDiscoveryResponse, error) {
|
||||
return t.Authority.AADInstanceDiscovery(ctx, authorityInfo)
|
||||
}
|
||||
|
||||
// AuthCode returns a token based on an authorization code.
|
||||
func (t *Client) AuthCode(ctx context.Context, req accesstokens.AuthCodeRequest) (accesstokens.TokenResponse, error) {
|
||||
if err := scopeError(req.AuthParams); err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
if err := t.resolveEndpoint(ctx, &req.AuthParams, ""); err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
@@ -107,6 +112,10 @@ func (t *Client) Credential(ctx context.Context, authParams authority.AuthParams
|
||||
}
|
||||
tr, 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)
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
return accesstokens.TokenResponse{
|
||||
@@ -134,6 +143,9 @@ func (t *Client) Credential(ctx context.Context, authParams authority.AuthParams
|
||||
|
||||
// Credential acquires a token from the authority using a client credentials grant.
|
||||
func (t *Client) OnBehalfOf(ctx context.Context, authParams authority.AuthParams, cred *accesstokens.Credential) (accesstokens.TokenResponse, error) {
|
||||
if err := scopeError(authParams); err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
if err := t.resolveEndpoint(ctx, &authParams, ""); err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
@@ -145,20 +157,35 @@ func (t *Client) OnBehalfOf(ctx context.Context, authParams authority.AuthParams
|
||||
if err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
return t.AccessTokens.FromUserAssertionClientCertificate(ctx, authParams, authParams.UserAssertion, jwt)
|
||||
tr, err := t.AccessTokens.FromUserAssertionClientCertificate(ctx, authParams, authParams.UserAssertion, jwt)
|
||||
if err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
func (t *Client) Refresh(ctx context.Context, reqType accesstokens.AppType, authParams authority.AuthParams, cc *accesstokens.Credential, refreshToken accesstokens.RefreshToken) (accesstokens.TokenResponse, error) {
|
||||
if err := scopeError(authParams); err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
if err := t.resolveEndpoint(ctx, &authParams, ""); err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
|
||||
return t.AccessTokens.FromRefreshToken(ctx, reqType, authParams, cc, refreshToken.Secret)
|
||||
tr, err := t.AccessTokens.FromRefreshToken(ctx, reqType, authParams, cc, refreshToken.Secret)
|
||||
if err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// UsernamePassword retrieves a token where a username and password is used. However, if this is
|
||||
// a user realm of "Federated", this uses SAML tokens. If "Managed", uses normal username/password.
|
||||
func (t *Client) UsernamePassword(ctx context.Context, authParams authority.AuthParams) (accesstokens.TokenResponse, error) {
|
||||
if err := scopeError(authParams); err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
|
||||
if authParams.AuthorityInfo.AuthorityType == authority.ADFS {
|
||||
if err := t.resolveEndpoint(ctx, &authParams, authParams.Username); err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
@@ -171,22 +198,32 @@ func (t *Client) UsernamePassword(ctx context.Context, authParams authority.Auth
|
||||
|
||||
userRealm, err := t.Authority.UserRealm(ctx, authParams)
|
||||
if err != nil {
|
||||
return accesstokens.TokenResponse{}, fmt.Errorf("problem getting user realm(user: %s) from authority: %w", authParams.Username, err)
|
||||
return accesstokens.TokenResponse{}, fmt.Errorf("problem getting user realm from authority: %w", err)
|
||||
}
|
||||
|
||||
switch userRealm.AccountType {
|
||||
case authority.Federated:
|
||||
mexDoc, err := t.WSTrust.Mex(ctx, userRealm.FederationMetadataURL)
|
||||
if err != nil {
|
||||
return accesstokens.TokenResponse{}, fmt.Errorf("problem getting mex doc from federated url(%s): %w", userRealm.FederationMetadataURL, err)
|
||||
err = fmt.Errorf("problem getting mex doc from federated url(%s): %w", userRealm.FederationMetadataURL, err)
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
|
||||
saml, err := t.WSTrust.SAMLTokenInfo(ctx, authParams, userRealm.CloudAudienceURN, mexDoc.UsernamePasswordEndpoint)
|
||||
if err != nil {
|
||||
return accesstokens.TokenResponse{}, fmt.Errorf("problem getting SAML token info: %w", err)
|
||||
err = fmt.Errorf("problem getting SAML token info: %w", err)
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
return t.AccessTokens.FromSamlGrant(ctx, authParams, saml)
|
||||
tr, err := t.AccessTokens.FromSamlGrant(ctx, authParams, saml)
|
||||
if err != nil {
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
return tr, nil
|
||||
case authority.Managed:
|
||||
if len(authParams.Scopes) == 0 {
|
||||
err = fmt.Errorf("token request had an empty authority.AuthParams.Scopes, which may cause the following error: %w", err)
|
||||
return accesstokens.TokenResponse{}, err
|
||||
}
|
||||
return t.AccessTokens.FromUsernamePassword(ctx, authParams)
|
||||
}
|
||||
return accesstokens.TokenResponse{}, errors.New("unknown account type")
|
||||
@@ -212,7 +249,6 @@ func (d DeviceCode) Token(ctx context.Context) (accesstokens.TokenResponse, erro
|
||||
}
|
||||
|
||||
var cancel context.CancelFunc
|
||||
d.Result.ExpiresOn.Sub(time.Now().UTC())
|
||||
if deadline, ok := ctx.Deadline(); !ok || d.Result.ExpiresOn.Before(deadline) {
|
||||
ctx, cancel = context.WithDeadline(ctx, d.Result.ExpiresOn)
|
||||
} else {
|
||||
@@ -275,6 +311,10 @@ func isWaitDeviceCodeErr(err error) bool {
|
||||
// DeviceCode returns a DeviceCode object that can be used to get the code that must be entered on the second
|
||||
// device and optionally the token once the code has been entered on the second device.
|
||||
func (t *Client) DeviceCode(ctx context.Context, authParams authority.AuthParams) (DeviceCode, error) {
|
||||
if err := scopeError(authParams); err != nil {
|
||||
return DeviceCode{}, err
|
||||
}
|
||||
|
||||
if err := t.resolveEndpoint(ctx, &authParams, ""); err != nil {
|
||||
return DeviceCode{}, err
|
||||
}
|
||||
@@ -295,3 +335,19 @@ func (t *Client) resolveEndpoint(ctx context.Context, authParams *authority.Auth
|
||||
authParams.Endpoints = endpoints
|
||||
return nil
|
||||
}
|
||||
|
||||
// scopeError takes an authority.AuthParams and returns an error
|
||||
// if len(AuthParams.Scope) == 0.
|
||||
func scopeError(a authority.AuthParams) error {
|
||||
// TODO(someone): we could look deeper at the message to determine if
|
||||
// it's a scope error, but this is a good start.
|
||||
/*
|
||||
{error":"invalid_scope","error_description":"AADSTS1002012: The provided value for scope
|
||||
openid offline_access profile is not valid. Client credential flows must have a scope value
|
||||
with /.default suffixed to the resource identifier (application ID URI)...}
|
||||
*/
|
||||
if len(a.Scopes) == 0 {
|
||||
return fmt.Errorf("token request had an empty authority.AuthParams.Scopes, which is invalid")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -28,10 +28,19 @@ const (
|
||||
regionName = "REGION_NAME"
|
||||
defaultAPIVersion = "2021-10-01"
|
||||
imdsEndpoint = "http://169.254.169.254/metadata/instance/compute/location?format=text&api-version=" + defaultAPIVersion
|
||||
defaultHost = "login.microsoftonline.com"
|
||||
autoDetectRegion = "TryAutoDetect"
|
||||
)
|
||||
|
||||
// These are various hosts that host AAD Instance discovery endpoints.
|
||||
const (
|
||||
defaultHost = "login.microsoftonline.com"
|
||||
loginMicrosoft = "login.microsoft.com"
|
||||
loginWindows = "login.windows.net"
|
||||
loginSTSWindows = "sts.windows.net"
|
||||
loginMicrosoftOnline = defaultHost
|
||||
)
|
||||
|
||||
// jsonCaller is an interface that allows us to mock the JSONCall method.
|
||||
type jsonCaller interface {
|
||||
JSONCall(ctx context.Context, endpoint string, headers http.Header, qv url.Values, body, resp interface{}) error
|
||||
}
|
||||
@@ -54,6 +63,8 @@ func TrustedHost(host string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// OAuthResponseBase is the base JSON return message for an OAuth call.
|
||||
// This is embedded in other calls to get the base fields from every response.
|
||||
type OAuthResponseBase struct {
|
||||
Error string `json:"error"`
|
||||
SubError string `json:"suberror"`
|
||||
@@ -309,31 +320,24 @@ func firstPathSegment(u *url.URL) (string, error) {
|
||||
return pathParts[1], nil
|
||||
}
|
||||
|
||||
return "", errors.New("authority does not have two segments")
|
||||
return "", errors.New(`authority must be an https URL such as "https://login.microsoftonline.com/<your tenant>"`)
|
||||
}
|
||||
|
||||
// NewInfoFromAuthorityURI creates an AuthorityInfo instance from the authority URL provided.
|
||||
func NewInfoFromAuthorityURI(authorityURI string, validateAuthority bool, instanceDiscoveryDisabled bool) (Info, error) {
|
||||
authorityURI = strings.ToLower(authorityURI)
|
||||
var authorityType string
|
||||
u, err := url.Parse(authorityURI)
|
||||
if err != nil {
|
||||
return Info{}, fmt.Errorf("authorityURI passed could not be parsed: %w", err)
|
||||
}
|
||||
if u.Scheme != "https" {
|
||||
return Info{}, fmt.Errorf("authorityURI(%s) must have scheme https", authorityURI)
|
||||
func NewInfoFromAuthorityURI(authority string, validateAuthority bool, instanceDiscoveryDisabled bool) (Info, error) {
|
||||
u, err := url.Parse(strings.ToLower(authority))
|
||||
if err != nil || u.Scheme != "https" {
|
||||
return Info{}, errors.New(`authority must be an https URL such as "https://login.microsoftonline.com/<your tenant>"`)
|
||||
}
|
||||
|
||||
tenant, err := firstPathSegment(u)
|
||||
if tenant == "adfs" {
|
||||
authorityType = ADFS
|
||||
} else {
|
||||
authorityType = AAD
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return Info{}, err
|
||||
}
|
||||
authorityType := AAD
|
||||
if tenant == "adfs" {
|
||||
authorityType = ADFS
|
||||
}
|
||||
|
||||
// u.Host includes the port, if any, which is required for private cloud deployments
|
||||
return Info{
|
||||
@@ -449,6 +453,8 @@ func (c Client) GetTenantDiscoveryResponse(ctx context.Context, openIDConfigurat
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// AADInstanceDiscovery attempts to discover a tenant endpoint (used in OIDC auth with an authorization endpoint).
|
||||
// This is done by AAD which allows for aliasing of tenants (windows.sts.net is the same as login.windows.com).
|
||||
func (c Client) AADInstanceDiscovery(ctx context.Context, authorityInfo Info) (InstanceDiscoveryResponse, error) {
|
||||
region := ""
|
||||
var err error
|
||||
@@ -461,9 +467,10 @@ func (c Client) AADInstanceDiscovery(ctx context.Context, authorityInfo Info) (I
|
||||
if region != "" {
|
||||
environment := authorityInfo.Host
|
||||
switch environment {
|
||||
case "login.microsoft.com", "login.windows.net", "sts.windows.net", defaultHost:
|
||||
environment = "r." + defaultHost
|
||||
case loginMicrosoft, loginWindows, loginSTSWindows, defaultHost:
|
||||
environment = loginMicrosoft
|
||||
}
|
||||
|
||||
resp.TenantDiscoveryEndpoint = fmt.Sprintf(tenantDiscoveryEndpointWithRegion, region, environment, authorityInfo.Tenant)
|
||||
metadata := InstanceDiscoveryMetadata{
|
||||
PreferredNetwork: fmt.Sprintf("%v.%v", region, authorityInfo.Host),
|
||||
|
@@ -5,4 +5,4 @@
|
||||
package version
|
||||
|
||||
// Version is the version of this client package that is communicated to the server.
|
||||
const Version = "0.8.1"
|
||||
const Version = "1.0.0"
|
||||
|
Reference in New Issue
Block a user