Skip to content

Commit e71ebf9

Browse files
author
Kalyan Krishna
committed
ready for merge
1 parent a4721fe commit e71ebf9

File tree

1 file changed

+104
-104
lines changed

1 file changed

+104
-104
lines changed

Microsoft.Identity.Web/Resource/AadIssuerValidator.cs

Lines changed: 104 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -24,119 +24,119 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2424

2525
using Microsoft.Identity.Web.InstanceDiscovery;
2626
using Microsoft.IdentityModel.JsonWebTokens;
27-
using Microsoft.IdentityModel.Protocols;
28-
using Microsoft.IdentityModel.Tokens;
27+
using Microsoft.IdentityModel.Protocols;
28+
using Microsoft.IdentityModel.Tokens;
2929
using System;
3030
using System.Collections.Concurrent;
31-
using System.Collections.Generic;
32-
using System.IdentityModel.Tokens.Jwt;
33-
using System.Linq;
34-
35-
namespace Microsoft.Identity.Web.Resource
36-
{
37-
/// <summary>
38-
/// Generic class that validates token issuer from the provided Azure AD authority. Use the <see cref="AadIssuerValidatorFactory"/> to create instaces of this class.
39-
/// </summary>
40-
public class AadIssuerValidator
41-
{
31+
using System.Collections.Generic;
32+
using System.IdentityModel.Tokens.Jwt;
33+
using System.Linq;
34+
35+
namespace Microsoft.Identity.Web.Resource
36+
{
37+
/// <summary>
38+
/// Generic class that validates token issuer from the provided Azure AD authority. Use the <see cref="AadIssuerValidatorFactory"/> to create instaces of this class.
39+
/// </summary>
40+
public class AadIssuerValidator
41+
{
4242
private const string AzureADIssuerMetadataUrl = "https://login.microsoftonline.com/common/discovery/instance?authorization_endpoint=https://login.microsoftonline.com/common/oauth2/v2.0/authorize&api-version=1.1";
4343
private const string FallbackAuthority = "https://login.microsoftonline.com/";
4444

4545
// TODO: separate AadIssuerValidator creation logic from the validation logic in order to unit test it
4646
private static readonly IDictionary<string, AadIssuerValidator> s_issuerValidators = new ConcurrentDictionary<string, AadIssuerValidator>();
4747

48-
private static readonly ConfigurationManager<IssuerMetadata> s_configManager = new ConfigurationManager<IssuerMetadata>(AzureADIssuerMetadataUrl, new IssuerConfigurationRetriever());
49-
50-
/// <summary>
51-
/// A list of all Issuers across the various Azure AD instances
52-
/// </summary>
48+
private static readonly ConfigurationManager<IssuerMetadata> s_configManager = new ConfigurationManager<IssuerMetadata>(AzureADIssuerMetadataUrl, new IssuerConfigurationRetriever());
49+
50+
/// <summary>
51+
/// A list of all Issuers across the various Azure AD instances
52+
/// </summary>
5353
private readonly ISet<string> _issuerAliases;
54-
55-
internal /* internal for test */ AadIssuerValidator(IEnumerable<string> aliases)
56-
{
57-
_issuerAliases = new HashSet<string>(aliases, StringComparer.OrdinalIgnoreCase);
54+
55+
internal /* internal for test */ AadIssuerValidator(IEnumerable<string> aliases)
56+
{
57+
_issuerAliases = new HashSet<string>(aliases, StringComparer.OrdinalIgnoreCase);
5858
}
5959

60-
/// <summary>
61-
/// Gets a <see cref="AadIssuerValidator"/> for an authority.
62-
/// </summary>
63-
/// <param name="aadAuthority">The authority to create the validator for, e.g. https://login.microsoftonline.com/ </param>
64-
/// <returns>A <see cref="AadIssuerValidator"/> for the aadAuthority.</returns>
65-
/// <exception cref="ArgumentNullException">if <paramref name="aadAuthority"/> is null or empty.</exception>
66-
public static AadIssuerValidator GetIssuerValidator(string aadAuthority)
67-
{
68-
if (string.IsNullOrEmpty(aadAuthority))
69-
throw new ArgumentNullException(nameof(aadAuthority));
70-
71-
if (s_issuerValidators.TryGetValue(aadAuthority, out AadIssuerValidator aadIssuerValidator))
72-
{
73-
return aadIssuerValidator;
74-
}
75-
else
76-
{
77-
// In the constructor, we hit the Azure AD issuer metadata endpoint and cache the aliases. The data is cached for 24 hrs.
60+
/// <summary>
61+
/// Gets a <see cref="AadIssuerValidator"/> for an authority.
62+
/// </summary>
63+
/// <param name="aadAuthority">The authority to create the validator for, e.g. https://login.microsoftonline.com/ </param>
64+
/// <returns>A <see cref="AadIssuerValidator"/> for the aadAuthority.</returns>
65+
/// <exception cref="ArgumentNullException">if <paramref name="aadAuthority"/> is null or empty.</exception>
66+
public static AadIssuerValidator GetIssuerValidator(string aadAuthority)
67+
{
68+
if (string.IsNullOrEmpty(aadAuthority))
69+
throw new ArgumentNullException(nameof(aadAuthority));
70+
71+
if (s_issuerValidators.TryGetValue(aadAuthority, out AadIssuerValidator aadIssuerValidator))
72+
{
73+
return aadIssuerValidator;
74+
}
75+
else
76+
{
77+
// In the constructor, we hit the Azure AD issuer metadata endpoint and cache the aliases. The data is cached for 24 hrs.
7878
var issuerMetadata = s_configManager.GetConfigurationAsync().ConfigureAwait(false).GetAwaiter().GetResult();
7979
string authorityHost;
8080
try
8181
{
82-
authorityHost = new Uri(aadAuthority).Authority;
83-
}
84-
catch
82+
authorityHost = new Uri(aadAuthority).Authority;
83+
}
84+
catch
8585
{
8686
authorityHost = null;
87-
}
88-
89-
// Add issuer aliases of the chosen authority
87+
}
88+
89+
// Add issuer aliases of the chosen authority
9090
string authority = authorityHost ?? new Uri(FallbackAuthority).Host;
9191
var aliases = issuerMetadata.Metadata
9292
.Where(m => m.Aliases.Any(a => string.Equals(a, authority, StringComparison.OrdinalIgnoreCase)))
9393
.SelectMany(m => m.Aliases)
94-
.Distinct();
95-
s_issuerValidators[authority] = new AadIssuerValidator(aliases);
96-
return s_issuerValidators[authority];
97-
}
98-
}
99-
100-
/// <summary>
101-
/// Validate the issuer for multi-tenant applications of various audience (Work and School account, or Work and School accounts +
102-
/// Personal accounts)
103-
/// </summary>
104-
/// <param name="actualIssuer">Issuer to validate (will be tenanted)</param>
105-
/// <param name="securityToken">Received Security Token</param>
106-
/// <param name="validationParameters">Token Validation parameters</param>
107-
/// <remarks>The issuer is considered as valid if it has the same http scheme and authority as the
108-
/// authority from the configuration file, has a tenant Id, and optionally v2.0 (this web api
109-
/// accepts both V1 and V2 tokens).
110-
/// Authority aliasing is also taken into account</remarks>
111-
/// <returns>The <c>issuer</c> if it's valid, or otherwise <c>SecurityTokenInvalidIssuerException</c> is thrown</returns>
112-
/// <exception cref="ArgumentNullException"> if <paramref name="securityToken"/> is null.</exception>
113-
/// <exception cref="ArgumentNullException"> if <paramref name="validationParameters"/> is null.</exception>
114-
/// <exception cref="SecurityTokenInvalidIssuerException">if the issuer </exception>
115-
public string Validate(string actualIssuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
94+
.Distinct();
95+
s_issuerValidators[authority] = new AadIssuerValidator(aliases);
96+
return s_issuerValidators[authority];
97+
}
98+
}
99+
100+
/// <summary>
101+
/// Validate the issuer for multi-tenant applications of various audience (Work and School account, or Work and School accounts +
102+
/// Personal accounts)
103+
/// </summary>
104+
/// <param name="actualIssuer">Issuer to validate (will be tenanted)</param>
105+
/// <param name="securityToken">Received Security Token</param>
106+
/// <param name="validationParameters">Token Validation parameters</param>
107+
/// <remarks>The issuer is considered as valid if it has the same http scheme and authority as the
108+
/// authority from the configuration file, has a tenant Id, and optionally v2.0 (this web api
109+
/// accepts both V1 and V2 tokens).
110+
/// Authority aliasing is also taken into account</remarks>
111+
/// <returns>The <c>issuer</c> if it's valid, or otherwise <c>SecurityTokenInvalidIssuerException</c> is thrown</returns>
112+
/// <exception cref="ArgumentNullException"> if <paramref name="securityToken"/> is null.</exception>
113+
/// <exception cref="ArgumentNullException"> if <paramref name="validationParameters"/> is null.</exception>
114+
/// <exception cref="SecurityTokenInvalidIssuerException">if the issuer </exception>
115+
public string Validate(string actualIssuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
116116
{
117-
if (String.IsNullOrEmpty(actualIssuer))
118-
throw new ArgumentNullException(nameof(actualIssuer));
119-
120-
if (securityToken == null)
121-
throw new ArgumentNullException(nameof(securityToken));
122-
123-
if (validationParameters == null)
124-
throw new ArgumentNullException(nameof(validationParameters));
125-
126-
string tenantId = GetTenantIdFromToken(securityToken);
127-
if (string.IsNullOrWhiteSpace(tenantId))
128-
throw new SecurityTokenInvalidIssuerException("Neither `tid` nor `tenantId` claim is present in the token obtained from Microsoft Identity Platform.");
129-
130-
if (validationParameters.ValidIssuers != null)
131-
foreach (var validIssuerTemplate in validationParameters.ValidIssuers)
132-
if (IsValidIssuer(validIssuerTemplate, tenantId, actualIssuer))
133-
return actualIssuer;
134-
117+
if (String.IsNullOrEmpty(actualIssuer))
118+
throw new ArgumentNullException(nameof(actualIssuer));
119+
120+
if (securityToken == null)
121+
throw new ArgumentNullException(nameof(securityToken));
122+
123+
if (validationParameters == null)
124+
throw new ArgumentNullException(nameof(validationParameters));
125+
126+
string tenantId = GetTenantIdFromToken(securityToken);
127+
if (string.IsNullOrWhiteSpace(tenantId))
128+
throw new SecurityTokenInvalidIssuerException("Neither `tid` nor `tenantId` claim is present in the token obtained from Microsoft Identity Platform.");
129+
130+
if (validationParameters.ValidIssuers != null)
131+
foreach (var validIssuerTemplate in validationParameters.ValidIssuers)
132+
if (IsValidIssuer(validIssuerTemplate, tenantId, actualIssuer))
133+
return actualIssuer;
134+
135135
if (IsValidIssuer(validationParameters.ValidIssuer, tenantId, actualIssuer))
136136
return actualIssuer;
137137

138-
// If a valid issuer is not found, throw
139-
// brentsch - todo, create a list of all the possible valid issuers in TokenValidationParameters
138+
// If a valid issuer is not found, throw
139+
// brentsch - todo, create a list of all the possible valid issuers in TokenValidationParameters
140140
throw new SecurityTokenInvalidIssuerException($"Issuer: '{actualIssuer}', does not match any of the valid issuers provided for this application.");
141141
}
142142

@@ -165,21 +165,21 @@ private bool IsValidIssuer(string validIssuerTemplate, string tenantId, string a
165165
}
166166

167167
return false;
168-
}
169-
168+
}
169+
170170
private static bool IsValidTidInLocalPath(string tenantId, Uri uri)
171171
{
172172
string trimmedLocalPath = uri.LocalPath.Trim('/');
173173
return trimmedLocalPath == tenantId || trimmedLocalPath == $"{tenantId}/v2.0";
174-
}
175-
176-
/// <summary>Gets the tenant id from a token.</summary>
177-
/// <param name="securityToken">A JWT token.</param>
178-
/// <returns>A string containing tenantId, if found or <see cref="string.Empty"/>.</returns>
179-
/// <remarks>Only <see cref="JwtSecurityToken"/> and <see cref="JsonWebToken"/> are acceptable types.</remarks>
180-
private static string GetTenantIdFromToken(SecurityToken securityToken)
181-
{
182-
if (securityToken is JwtSecurityToken jwtSecurityToken)
174+
}
175+
176+
/// <summary>Gets the tenant id from a token.</summary>
177+
/// <param name="securityToken">A JWT token.</param>
178+
/// <returns>A string containing tenantId, if found or <see cref="string.Empty"/>.</returns>
179+
/// <remarks>Only <see cref="JwtSecurityToken"/> and <see cref="JsonWebToken"/> are acceptable types.</remarks>
180+
private static string GetTenantIdFromToken(SecurityToken securityToken)
181+
{
182+
if (securityToken is JwtSecurityToken jwtSecurityToken)
183183
{
184184
if (jwtSecurityToken.Payload.TryGetValue(ClaimConstants.Tid, out object tenantId))
185185
return tenantId as string;
@@ -191,9 +191,9 @@ private static string GetTenantIdFromToken(SecurityToken securityToken)
191191
var tid = jsonWebToken.GetPayloadValue<string>(ClaimConstants.Tid);
192192
if (tid != null)
193193
return tid;
194-
}
195-
196-
return string.Empty;
197-
}
198-
}
194+
}
195+
196+
return string.Empty;
197+
}
198+
}
199199
}

0 commit comments

Comments
 (0)