JWT validation - sts.windows.net vs login.windows.net: Why are the issuers not consistent?
Aug 8
I am developing an application that is spread across multiple Function App running on .net5.
I need to authenticate HTTP calls between functions. To do so, I am using Azure Active Directory. I have created a registered application in my tenant and generated a new secret. Whenever Function1 needs to contact Function2, I retrieve an access token from AAD, like this:
var stringContent = new StringContent($"grant_type=client_credentials&client_id={Uri.EscapeUriString(clientId)}&client_secret={Uri.EscapeUriString(clientSecret)}&scope={Uri.EscapeUriString(scope)}", Encoding.UTF8, "application/x-www-form-urlencoded");
string tokenUrl = "https://login.microsoftonline.com/57cc008d-ba7c-4887-acfd-93089c705640/oauth2/v2.0/token";
HttpResponseMessage result = await _httpClient.PostAsync(tokenUrl, stringContent);
string content = await result.Content.ReadAsStringAsync();
With this call, I get a token which has the following information:
{
"typ": "JWT",
"alg": "RS256",
"x5t": "XXXXXXXXXXXXX_KXEg",
"kid": "XXXXXXXXXXXXX_KXEg"
}.{
"aud": "api://f87cc6ac-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"iss": "https://sts.windows.net/57cc008d-XXXX-XXXX-XXXX-XXXXXXXXXXXX/",
"iat": 1628443017,
"nbf": 1628443017,
"exp": 1628446917,
"aio": "E2ZgYOg4qv7qZsTRKv5v+XXXXXXXXXXX",
"appid": "f87cc6ac-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"appidacr": "1",
"idp": "https://sts.windows.net/57cc008d-XXXX-XXXX-XXXX-XXXXXXXXXXXX/",
"oid": "39b2e6b8-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"rh": "0.AVsAjQDMV3y6h0is_ZMInHBWQKzGfPhOtBZEj3l003jzIFFbAAA.",
"sub": "39b2e6b8-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"tid": "57cc008d-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"uti": "U7pMFAzw_XXXXXXXXXXXXX",
"ver": "1.0"
}.[Signature]
Now, the access token is used as a bearer token when Function2 is called. Function2 gets the token from the Authorization header and tries to validate it, like this:
ConfigurationManager<OpenIdConnectConfiguration> configurationManager = new ("https://login.microsoftonline.com/57cc008d-ba7c-4887-acfd-93089c705640/v2.0/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration openIdConnectConfiguration = await configurationManager.GetConfigurationAsync();
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = true,
ValidIssuers = new[]
{
openIdConnectConfiguration.Issuer
},
ValidateIssuerSigningKey = true,
IssuerSigningKeys = openIdConnectConfiguration.SigningKeys,
RequireExpirationTime = true,
ValidateLifetime = true,
RequireSignedTokens = true,
};
JwtSecurityTokenHandler securityTokenHandler = new();
if (!securityTokenHandler.CanReadToken(cleanedBearerToken))
throw new ArgumentException("Unable to read the token. It is malformed.");
try
{
ClaimsPrincipal claimsPrincipal = securityTokenHandler.ValidateToken(cleanedBearerToken, validationParameters, out SecurityToken _);
return claimsPrincipal;
}
catch (Exception unhandledException)
{
throw new AuthenticationException("The token could not be validated.", unhandledException);
}
Aside from the issuer, the validation works. With this setup, the issuer can't be validated. The token indicates that the issuer is from sts.windows.net. However, the OpenID configuration states that the issuer must be login.microsoft.com.
To make it work, I have fall back to something like this which is less ideal since I have to ignore a parameter returned by the openid-configuration endpoint (which must know better that I):
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = true,
ValidIssuers = new[]
{
// --> Force the use of the following issuer!!
"https://sts.windows.net/57cc008d-ba7c-4887-acfd-93089c705640/"
},
ValidateIssuerSigningKey = true,
IssuerSigningKeys = openIdConnectConfiguration.SigningKeys,
RequireExpirationTime = true,
ValidateLifetime = true,
RequireSignedTokens = true,
};
Question
Is the override of issuer a good practice? If not, what can I do to get consistent issuer from Azure Active Directory and avoid specifying the issuer myself?
1 answer
Accepted answer · original discussion
Aug 31
The issuer value depends on the Access token version. If you view the Sample Tokens in jwt.ms, you will see the issuer for Access token V1 is https://sts.windows.net/... and for Access token V2 it is https://login.microsoft.com/...
Also, if you check OIDC metadata endpoint v1 (https://login.microsoftonline.com/common/.well-known/openid-configuration), issuer will be sts.windows.net and for OIDC metadata endpoint v2 (https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration), issuer will be login.microsoft.com.
Update
In registered application's manifest, look for the value of accessTokenAcceptedVersion:
Change it to 2 and the issuer will become login.microsoft.com instead of sts.windows.net.
0 question comments
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.