I'm trying to validate the IdToken I get from Azure AD on each request but I keep getting an error saying that there is no signature on the token. When I validate the Access Token it works, but I would rather use the Id Token as that contains the users claims. Is there anyway to make Azure send back the Id Token with the signature as well?
public JwtSecurityToken ValidateJwtToken(string jwtToken)
{
string stsDiscoveryEndpoint = $"{_authority}/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager =
new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false,
ValidateLifetime = false,
IssuerSigningKeys = config.SigningKeys
};
JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();
try
{
SecurityToken token;
tokendHandler.ValidateToken(jwtToken, validationParameters, out token);
return token as JwtSecurityToken;
}
catch (Exception ex)
{
loggingService.LogError("Could not validate azure ad token", nameof(AzureSecurityService), ex);
return null;
}
}
public async Task<string> GenerateToken(string code)
{
AuthenticationContext authenticationContext = new AuthenticationContext(_authority);
try
{
string baseUrl = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
AuthenticationResult result =
await authenticationContext.AcquireTokenByAuthorizationCodeAsync(code, new Uri(baseUrl), new ClientCredential(_clientId, _clientSecret));
return result.IdToken;
}
catch (AdalException adalex)
{
loggingService.LogError("Could not get authorization request url", nameof(AzureSecurityService), adalex);
return null;
}
catch (Exception ex)
{
loggingService.LogError("Could not get authorization request url", nameof(AzureSecurityService), ex);
return null;
}
}
public async Task<string> GetAuthUrl()
{
AuthenticationContext authenticationContext = new AuthenticationContext(_authority);
// Config for OAuth client credentials
try
{
string baseUrl = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority);
var authUri =
await authenticationContext.GetAuthorizationRequestUrlAsync("00000000-0000-0000-0000-000000000000", _clientId, new Uri(baseUrl), UserIdentifier.AnyUser, null);
return authUri.ToString();
}
catch (AdalException adalex)
{
loggingService.LogError("Could not get authorization request url", nameof(AzureSecurityService), adalex);
return null;
}
catch (Exception ex)
{
loggingService.LogError("Could not get authorization request url", nameof(AzureSecurityService), ex);
return null;
}
}
UPDATE
So for whatever reason even though there was no scope being provided I was getting back claims. What I have done now is just add query parameters to the auth url for scope and request both openid and profile:
var authUri =
await authenticationContext.GetAuthorizationRequestUrlAsync("00000000-0000-0000-0000-000000000000", _clientId, new Uri(GetBaseUrl()), UserIdentifier.AnyUser, "scope=openid profile");
My question is now why does the default scope not return an id_token with a signature?
You should have forgotten to put required scope value in your authorization request,
From OpenID Connect specification
Scope
REQUIRED. OpenID Connect requests MUST contain the openid scope value. If the openid scope value is not present, the behavior is entirely unspecified. Other scope values MAY be present. Scope values used that are not understood by an implementation SHOULD be ignored. See Sections 5.4 and 11 for additional scope values defined by this specification.
So you must specify openid in the authorization request.
And Azure does return an id token for OAuth 2.0 token response (when openid scope is not present).
From Azure AD OAuth2.0 documentation,
id_token
An unsigned JSON Web Token (JWT). The app can base64Url decode the segments of this token to request information about the user who signed in. The app can cache the values and display them, but it should not rely on them for any authorization or security boundaries.
As the documentation say, it's just there to display the end user. One must not use this id token for authenticate/authorize.
On the other hand, for a proper OpenID Connect token response, Auzre sends you a signed id token,
From documentation
id_token
The id_token that the app requested. You can use the id_token to verify the user's identity and begin a session with the user.
Validate the id_token section of the same documentation explains how to validate the token
Hope this solved your issue.