@@ -16,6 +16,7 @@ import (
1616
1717 "github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops"
1818 "github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority"
19+ "golang.org/x/sync/singleflight"
1920)
2021
2122type cacheEntry struct {
@@ -35,6 +36,8 @@ type authorityEndpoint struct {
3536
3637 mu sync.Mutex
3738 cache map [string ]cacheEntry
39+
40+ resolveGroup singleflight.Group
3841}
3942
4043// newAuthorityEndpoint is the constructor for AuthorityEndpoint.
@@ -50,35 +53,49 @@ func (m *authorityEndpoint) ResolveEndpoints(ctx context.Context, authorityInfo
5053 return endpoints , nil
5154 }
5255
53- endpoint , err := m .openIDConfigurationEndpoint (ctx , authorityInfo )
54- if err != nil {
55- return authority.Endpoints {}, err
56- }
56+ key := authorityInfo .CanonicalAuthorityURI
57+ v , err , _ := m .resolveGroup .Do (key , func () (interface {}, error ) {
58+ // Double-check inside the singleflight group: another goroutine may
59+ // have populated the cache while we were waiting.
60+ if endpoints , found := m .cachedEndpoints (authorityInfo , userPrincipalName ); found {
61+ return endpoints , nil
62+ }
5763
58- resp , err := m .rest .Authority ().GetTenantDiscoveryResponse (ctx , endpoint )
59- if err != nil {
60- return authority.Endpoints {}, err
61- }
62- if err := resp .Validate (); err != nil {
63- return authority.Endpoints {}, fmt .Errorf ("ResolveEndpoints(): %w" , err )
64- }
64+ endpoint , err := m .openIDConfigurationEndpoint (ctx , authorityInfo )
65+ if err != nil {
66+ return authority.Endpoints {}, err
67+ }
68+
69+ resp , err := m .rest .Authority ().GetTenantDiscoveryResponse (ctx , endpoint )
70+ if err != nil {
71+ return authority.Endpoints {}, err
72+ }
73+ if err := resp .Validate (); err != nil {
74+ return authority.Endpoints {}, fmt .Errorf ("ResolveEndpoints(): %w" , err )
75+ }
6576
66- tenant := authorityInfo .Tenant
77+ tenant := authorityInfo .Tenant
6778
68- endpoints := authority .NewEndpoints (
69- strings .Replace (resp .AuthorizationEndpoint , "{tenant}" , tenant , - 1 ),
70- strings .Replace (resp .TokenEndpoint , "{tenant}" , tenant , - 1 ),
71- strings .Replace (resp .Issuer , "{tenant}" , tenant , - 1 ),
72- authorityInfo .Host )
79+ endpoints := authority .NewEndpoints (
80+ strings .Replace (resp .AuthorizationEndpoint , "{tenant}" , tenant , - 1 ),
81+ strings .Replace (resp .TokenEndpoint , "{tenant}" , tenant , - 1 ),
82+ strings .Replace (resp .Issuer , "{tenant}" , tenant , - 1 ),
83+ authorityInfo .Host )
7384
74- m .addCachedEndpoints (authorityInfo , userPrincipalName , endpoints )
85+ aliases := m .addCachedEndpoints (authorityInfo , userPrincipalName , endpoints )
7586
76- if err := resp .ValidateIssuerMatchesAuthority (authorityInfo .CanonicalAuthorityURI ,
77- m .cache [authorityInfo .CanonicalAuthorityURI ].Aliases ); err != nil {
78- return authority.Endpoints {}, fmt .Errorf ("ResolveEndpoints(): %w" , err )
87+ if err := resp .ValidateIssuerMatchesAuthority (authorityInfo .CanonicalAuthorityURI ,
88+ aliases ); err != nil {
89+ return authority.Endpoints {}, fmt .Errorf ("ResolveEndpoints(): %w" , err )
90+ }
91+
92+ return endpoints , nil
93+ })
94+ if err != nil {
95+ return authority.Endpoints {}, err
7996 }
8097
81- return endpoints , nil
98+ return v .(authority. Endpoints ) , nil
8299}
83100
84101// cachedEndpoints returns the cached endpoints if they exist. If not, we return false.
@@ -100,7 +117,7 @@ func (m *authorityEndpoint) cachedEndpoints(authorityInfo authority.Info, userPr
100117 return authority.Endpoints {}, false
101118}
102119
103- func (m * authorityEndpoint ) addCachedEndpoints (authorityInfo authority.Info , userPrincipalName string , endpoints authority.Endpoints ) {
120+ func (m * authorityEndpoint ) addCachedEndpoints (authorityInfo authority.Info , userPrincipalName string , endpoints authority.Endpoints ) map [ string ] bool {
104121 m .mu .Lock ()
105122 defer m .mu .Unlock ()
106123
@@ -128,6 +145,7 @@ func (m *authorityEndpoint) addCachedEndpoints(authorityInfo authority.Info, use
128145 }
129146
130147 m .cache [authorityInfo .CanonicalAuthorityURI ] = updatedCacheEntry
148+ return updatedCacheEntry .Aliases
131149}
132150
133151func (m * authorityEndpoint ) openIDConfigurationEndpoint (ctx context.Context , authorityInfo authority.Info ) (string , error ) {
0 commit comments