How to authenticate AAD token from PPE environment using OWIN? - owin

I am using below code to authenticate AAD access token.
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigSettings.MicrosoftAadTenant,
TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false,
ValidateIssuer = false
}
});
The code works fine until I switched to use a token generated from PPE environment, whose Iss is "https://sts.windows-ppe.net/...". I pasted error message below. Do you know how to fix it?
Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationMiddleware
Error: 0 : Authentication failed
System.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException:
IDX10500: Signature validation failed. Unable to resolve
SecurityKeyIdentifier: 'SecurityKeyIdentifier
(
IsReadOnly = False,
Count = 2,
Clause[0] = X509ThumbprintKeyIdentifierClause(Hash = 0x871BE0E2BDD307841D01C8151AE2717D2DB9F376),
Clause[1] = System.IdentityModel.Tokens.NamedKeySecurityKeyIdentifierClause
) ',
Thanks,

Ok, I find out why. Looking at Microsoft Owin source code at git repo, the login url is hard coded. There is no way to config it. I fix the issue by writing my own extension method.

Related

Microsoft.IdentityModel.LoggingExtensions.IdentityLoggerAdapter Error Logs in AzureAD Auth Implementation

We have integrated AzureAD for our API Authorization and API Auth is working fine but seeing error logs for Microsoft.IdentityModel.LoggingExtensions.IdentityLoggerAdapter with below messages,
IDX40003: Neither tid nor tenantId claim is present in the token obtained from Microsoft identity platform.
IDX10516: Signature validation failed. Unable to match key: kid: '*'. Number of keys in TokenValidationParameters: '16'. Number of keys in Configuration: '0'. Exceptions caught: '[PII of type 'System.Text.StringBuilder' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. token: '[PII of type 'System.IdentityModel.Tokens.Jwt.JwtSecurityToken' is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. Valid Lifetime: 'False'. Valid Issuer: 'False'**
Our APIs are being consumed by UI APP as well as via Swagger/Postman.
What should be root cause to it?
The error IDX10516: Signature validation failed. Unable to match key: kid occurs when the Kid in your decoded token which validates token signature is not valid.
So one needs to load keys from the openid provider.
code:
var openidConfiguration = new ConfigurationManager<OpenIdConnectConfiguration>(
$"https://login.microsoftonline.com/{tenantid}/v2.0/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever(),
new HttpDocumentRetriever());
var config = await openidConfiguration.GetConfigurationAsync();
var parameteres = new TokenValidationParameters()
{
RequireAudience = true,
RequireExpirationTime = true,
ValidateAudience = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidAudiences = new[] { "<my client id or appId uri or resource identitifer>" },
ValidIssuers = new[] { $"https://sts.windows.net/{tenantid}/" },
IssuerSigningKeys = config.SigningKeys
};
AppId Uri , you can check from the portal : api://
Add the following to check for more clear descriptive exception in ConfigureServices method in Startup.cs :IdentityModelEventSource.ShowPII = true
Example:
public void ConfigureServices(IServiceCollection services)
{
...
IdentityModelEventSource.ShowPII = true;
...
}
Reference : Azure AD - Why can't I validate JWT token issued by Azure AD for my Web API? Getting "IDX10516: Signature validation failed" error - Stack Overflow

Azure AD B2C - CORS Policy Issue for Multi App Services

I've 2 projects: Project1 is main app (localhost:4016) Project2 is child app (localhost:4055) and sharing authorization token cookies between 2 projects.
It's working fine locally but not working while hosted on Azure. Getting following error:
Access to XMLHttpRequest at 'https://devplatform.b2clogin.com/devplatform.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/authorize?client_id=XXXX&redirect_uri=https://myapps.example.com/home/index ...' (redirected from 'https://www.example.com/api/home/getlist') from origin 'https://www.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I'm using following OpenIdConnect for Azure AD B2C Authentication in multiple projects.
public void ConfigureAuth(IAppBuilder app) {
// Required for Azure webapps, as by default they force TLS 1.2 and this project attempts 1.0
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions() {
//locally it'll keep redirecting infinitely
CookieDomain = ".example.com" //only required when we host it to keep persist cookies
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
// Generate the metadata address using the tenant and policy information
MetadataAddress = String.Format(Globals.WellKnownMetadata, Globals.Tenant, Globals.DefaultPolicy),
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = Globals.ClientId,
RedirectUri = Globals.RedirectUri,
PostLogoutRedirectUri = Globals.RedirectUri,
// Specify the callbacks for each type of notifications
Notifications = new OpenIdConnectAuthenticationNotifications {
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
},
// Specify the claim type that specifies the Name property.
TokenValidationParameters = new TokenValidationParameters {
NameClaimType = "name",
ValidateIssuer = false
},
// Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
Scope = $"openid profile offline_access {Globals.ReadTasksScope} {Globals.WriteTasksScope}"
}
);
}
If I do redirection on authentication failed event, get the nonce error as follows:
IDX21323: RequireNonce is ‘[PII is hidden by default. Set the
‘ShowPII’ flag in IdentityModelEventSource.cs to true to reveal it.]’.
OpenIdConnectProtocolValidationContext.Nonce was null,
OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null. The
nonce cannot be validated. If you don’t need to check the nonce, set
OpenIdConnectProtocolValidator.RequireNonce to ‘false’. Note if a
‘nonce’ is found it will be evaluated.
I can disable it by following statement:
ProtocolValidator = new OpenIdConnectProtocolValidator() { RequireNonce = false }
But it introduced following error:
IDX21329: RequireState is '[PII is hidden]' but the
OpenIdConnectProtocolValidationContext.State is null. State cannot be
validated.
I tried to disable that by following statement but still same issue.
ProtocolValidator = new OpenIdConnectProtocolValidator() { RequireNonce = false, RequireState = false }

azure acive directory validation error

Im having an issue with a web app in azure. I use active directory to control access which works well, but one of my users gets this error message when they login in
idx10214 audience validation failed did not match validationparameters.validaudience or validationparameters null
does anyone know what this means ? Is there a workaround ?
heres how I setup openid to authorize users, how would i include the audience settings here ? What should the audience value be ?
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = AuthorityCHP,
PostLogoutRedirectUri = postLogoutRedirectUri,
RedirectUri = loURL,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential credential = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(AuthorityCHP, new ADALTokenCache(signedInUserID));
var newuri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(code, newuri, credential, graphResourceId);
return Task.FromResult(0);
}
}
});
Ive added some additional code in the hope it might work, but after i deployed it, i got the user affecetd to try logging in again and its still the same. Im not consuming a webapi its just a straightforwrsd webapp with a standard login page, Im really stumped, why is it only one particular user thats affecetd by this ? Can anynoe help ?
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = clientId
},
Tenant = tenantId,
AuthenticationType = "OAuth2Bearer"
});
That's mean you were calling the API using the incorrect token. When we call a web API which protected by Azure AD, it will verify the signature of the token and claims in the token.
The audience is used to which resource the token is able to access. We should acquire the token based on the resource. For example, if we protect the web API with code below, we should use the audience config in the below to acquire the token.
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
});
//app.UsePasswordAuthentication();
}
update
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
// In a real application you would use IssuerValidator for additional checks, like making sure the user's organization has signed up for your app.
// IssuerValidator = (issuer, token, tvp) =>
// {
// //if(MyCustomTenantValidation(issuer))
// return issuer;
// //else
// // throw new SecurityTokenInvalidIssuerException("Invalid issuer");
// },
},

Integrate Windows Azure Active Directory Bearer Authentication at run time

Currently, I am integrating Azure Active Directory into my .NET Web API using following code:
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = WebConfigurationManager.AppSettings["AzureClientId"],
Tenant = WebConfigurationManager.AppSettings["AzureTenant"]
}
});
The audience and tenant were set in web.config file.
I can get the token correctly and users can log in using their azure AD account.
However, I moved the audience and tenant to database to allow users to change the settings and disable/enable azure login by UI instead of changing the setting in web.config.
The above code was changed to:
var azureSetting = db.GetAzureSetting();
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = azureSetting.AzureClientId,
Tenant = azureSetting.AzureTenant
}
});
When the app first starts, there is no config in the database because users do not enter the config yet. Then users go to azure config screen, enter the correct Client Id, Tenant, Client Secret. But users can not log in using azure AD user.
Can anyone explain this case for me?
Is there any way to save azure config in db instead of web.config?
Your code in the post works when web API start and we can’t change configuration runtime , if you want to enable users change the audience and tenant dynamically, you could handle token validation yourself . You could use JwtSecurityTokenHandler to validate the token after your api app get the access token , code below is for your reference :
public JwtSecurityToken Validate(string token)
{
string stsDiscoveryEndpoint = "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration";
ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint);
OpenIdConnectConfiguration config = configManager.GetConfigurationAsync().Result;
TokenValidationParameters validationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidateIssuer = false,
ValidAudience = "https://testbasic1.onmicrosoft.com/TodoListService", //your value from database
IssuerSigningTokens = config.SigningTokens,
ValidateLifetime = false
};
JwtSecurityTokenHandler tokendHandler = new JwtSecurityTokenHandler();
SecurityToken jwt;
var result = tokendHandler.ValidateToken(token, validationParameters, out jwt);
return jwt as JwtSecurityToken;
}
This handler helps you verify the signature of the token to ensure the token was issued by Azure Active Directory,and also verify the claims in the token based on the business logic,in your scenario,you need to confirm audience and tenant.
In you web api app , you could register custom TokenValidationHandler in Global.asax :
GlobalConfiguration.Configuration.MessageHandlers.Add(new TokenValidationHandler());
You could click here for code sample , you could modify your code to check whether tokens comes from tenant-id which stores in database .

How do you validate JwtSecurityToken that has been signed using Azure's WEBSITE_AUTH_SIGNING_KEY?

I have setup 3rd party login providers (google and microsoft) on Azure for my web app. I am adding a custom login also (in case the user doesn't have one of those accounts). I have the token being generated as follows:
private JwtSecurityToken GenerateToken(User user)
{
var audience = ConfigurationManager.AppSettings["ValidAudience"];
var issuer = ConfigurationManager.AppSettings["ValidIssuer"];
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, user.AzureUserID) },
GetSigningKey(),
audience,
issuer,
TimeSpan.FromHours(1));
return token;
}
Where GetSigningKey() returns a string of the Azure environment variable WEBSITE_AUTH_SIGNING_KEY. The default authentication with my API controllers is working great, but when I need to refresh my token due to it expiring I am having trouble validating its signature. There are a lot of examples out there on how to validate JwtSecurityTokens, but I haven't been able to find any when you signed it using the WEBSITE_AUTH_SIGNING_KEY. Here is what I have tried to get working, without success:
var validationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ValidAudience"],
ValidIssuer = ConfigurationManager.AppSettings["ValidIssuer"],
IssuerSigningToken = <I think I need this, but not sure how to populate it...>,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true
};
SecurityToken validatedToken;
tokenHandler.ValidateToken(expiredAccessToken, validationParameters, out validatedToken);
Does anyone have any ideas on how to validate the token?
App Settings (including the signing key you mentioned) appear as environment variables, so you can either use the ConfigurationManager to read it or you can use the normal environment variable reading for ASP.NET
Try:
ConfigurationManager.AppSettings["WEBSITE_AUTH_SIGNING_KEY"]
Digging through the unit tests on GitHub for the azure-mobile-apps-net-server project, I found an AppServiceTokenHandler that does the trick:
// Validate that the token was generated by our server.
var validationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ValidAudience"],
ValidIssuer = ConfigurationManager.AppSettings["ValidIssuer"],
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidateLifetime = false // The token is valid for this test case whether or not it is expired.
};
ClaimsPrincipal claimsPrincipal;
if (AppServiceTokenHandler.TryValidateToken(validationParameters, expiredAccessToken, GetSigningKey(), out claimsPrincipal))
{
// Token is validated
}

Resources