Closed jrmcdona closed 3 years ago
@jrmcdona : Did you look at this one: https://github.com/Azure-Samples/ms-identity-javascript-angular-spa-aspnetcore-webapi
Other protected web API samples are referenced here: https://docs.microsoft.com/en-us/azure/active-directory/develop/sample-v2-code#web-apis. You might want to look at https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore-v2/tree/master/1.%20Desktop%20app%20calls%20Web%20API
@jmprieur those are ,NET Core. I am looking for .NET 4.7 or so. I have a working project with .NET Core but right now I have a first party app and I am trying to move to MSAL so I can get rid of RPS. But I have yet to find a clear example.
@jrmcdona : indeed, I'm not sure we have a good .NET Framework example for web APIs. Good point; Note that for the token cache serialization, we just released yesterday a version of MIcrosoft.Identity.Web which provides some implementation for .NET Framework: See https://github.com/AzureAD/microsoft-identity-web/wiki/asp-net#token-cache-serialization-for-msalnet
@jrmcdona : did you look at this sample: https://github.com/Azure-Samples/ms-identity-aspnet-webapi-onbehalfof ?
@jmprieur that sample uses WindowsAzureActiveDirectoryBearerAuthenticationOptions and I was told to use UseJwtBearerAuthentication. So I have been trying to make it work with Jwt. Do you know for sure which I should use?
Mine is a protected Web API and not a on behalf of downstream API.
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
@jrmcdona : the todolist service is a protected API (which moreover calls a downstream API). But I just found this one: https://github.com/Azure-Samples/active-directory-dotnet-webapi-getting-started. the code is ASP.NET Core, but the readme seems to explain how to proceed for .NET Fw: https://github.com/Azure-Samples/active-directory-dotnet-webapi-getting-started#about-the-basic-backend-service
@kalyankrishna1 : do we have a better sample of protected web API for ASP.NET FW?
@kalyankrishna1 @jmprieur Since I am trying to use MSAL.NET should I need ne using client applications to validate the token?
https://identitydocs.azurewebsites.net/static/v2/msal-net-initializing-client-applications.html
@jrmcdona : no the tokens need to be validated with Middleware. It does not need to use MSAL.NET until you want to acquire a token for a downstream API. I forgot to ask.: did you see https://docs.microsoft.com/azure/active-directory/develop/scenario-protected-web-api-overview ?
I am a facing similar issue for configuring Azure Ad/Protecting our custom Web API. I have angularjs in the front end and ASP.Net 4.8 in the backend. Could you please provide a working example? I tried different combinations from the Azure sample directory like JavaScript samples as AngularJS MSAL libary is discontinue and I also tried the samples suggested above. But none of them seems to be working.
How can I protect my web api with Azure AD configuration in ASP.Net 4.8?
@shridharkurhade - there is a sample dedicated to ASP.NET classic - https://learn.microsoft.com/en-us/azure/active-directory/develop/sample-v2-code#web-applications - does this not work for you?
Hi @bgavrilMS ,
The classic ASP.Net works when I have the whole application in ASP.Net e.g. if I use open id configuration and have Sign-in , sign-out calls in ASP.Net application.
But I want to trigger sign-in, sign-out and refresh token to Azure AD from AngularJS code and my web API code is in ASP.Net.
Could you please suggest an example for this combination? How I can configure Azure AD configuration in my ASP.Net with additional claims and retrieve it in AngularJS.
There are examples of ASP.Net core and Angular (not AngularJS), which I can find. In these examples, I can see that MSAL.Net and MSAL Angular used for configuration but didn't find any working example of Classic ASP.Net and AngujarJS.
@kalyankrishna1 is it something you can help with?
@bgavrilMS @kalyankrishna1 this is something we also are looking to do as part of our migration to modernising our massive legacy web app.
Edit: I've figured it out. Will post the solution here later today or tomorrow to help anyone for the future
@bgavrilMS @jmprieur The solution is as follows:
There's two aspects I wasn't entirely certain of which I point out below and still need to research. The code works though so its 99% of the job done.
Startup.Auth.cs
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
var authority = $"https://login.microsoftonline.com/{Config.TenantId}/v2.0";
var discoveryDoc = $"{authority}/.well-known/openid-configuration";
var keyResolver = new OpenIdV2ConnectSigningKeyResolver(discoveryDoc);
var tokenParams = new TokenValidationParameters
{
AuthenticationType = OAuthDefaults.AuthenticationType,
ValidAudiences = new[] { Config.ApiClientIdId, $"api://{Config.ApiClientIdId}" },
ValidateIssuer = true,
ValidIssuer = authority,
IssuerSigningKeyResolver = (token, securityToken, kid, parameters) => keyResolver.GetSigningKey(kid)
};
ep.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
IssuerSecurityKeyProviders = new[] { new OpenIdConnectV2SecurityKeyProvider(discoveryDoc) },
TokenValidationParameters = tokenParams
});
Not entirely sure if authenticationmode.Active is necessary.
Then in the above you'll see that I had to implement both the OpenIdConnectV2SecurityKeyProvider
and OpenIdV2ConnectSigningKeyResolver
.
Here are the implementations of each:
OpenIdV2ConnectSigningKeyResolver
Not entirely sure if I should use a ReaderWriterLockSlim
similar to the Provider
. (i.e. is it a critical section??)
//This is necessary because when using .NET Framework UseJwtBearer doesn't know what V2.0 endpoint to call to validate signing keys.
public class OpenIdV2ConnectSigningKeyResolver
{
private readonly OpenIdConnectConfiguration openIdConfig;
public OpenIdV2ConnectSigningKeyResolver(string metadataEndpoint)
{
var cm = new ConfigurationManager<OpenIdConnectConfiguration>(metadataEndpoint, new OpenIdConnectConfigurationRetriever());
var configRequest = cm.GetConfigurationAsync().ConfigureAwait(false); //Always use ConfigureAwait(false) to avoid deadlocks when running async code unavoidably in sync method
this.openIdConfig = configRequest.GetAwaiter().GetResult();
}
public SecurityKey[] GetSigningKey(string kid)
{
return new[] { this.openIdConfig.JsonWebKeySet.GetSigningKeys().FirstOrDefault(t => t.KeyId == kid) };
}
}
`KeyProvider.cs`
//This class is necessary because the OAuthBearer Middleware does not leverage
// the OpenID Connect metadata endpoint exposed by the V2 STS by default.
public class OpenIdConnectV2SecurityKeyProvider : IIssuerSecurityKeyProvider
{
public ConfigurationManager<OpenIdConnectConfiguration> ConfigManager;
private string _issuer;
private IEnumerable<SecurityKey> _keys;
private readonly string _metadataEndpoint;
private readonly ReaderWriterLockSlim _synclock = new ReaderWriterLockSlim();
public OpenIdConnectSecurityKeyProvider(string metadataEndpoint)
{
_metadataEndpoint = metadataEndpoint;
ConfigManager = new ConfigurationManager<OpenIdConnectConfiguration>(metadataEndpoint, new OpenIdConnectConfigurationRetriever());
RetrieveMetadata();
}
/// <summary>
/// Gets the issuer the credentials are for.
/// </summary>
/// <value>
/// The issuer the credentials are for.
/// </value>
public string Issuer
{
get
{
RetrieveMetadata();
_synclock.EnterReadLock();
try
{
return _issuer;
}
finally
{
_synclock.ExitReadLock();
}
}
}
public IEnumerable<SecurityKey> SecurityKeys
{
get
{
RetrieveMetadata();
_synclock.EnterReadLock();
try
{
return _keys;
}
finally
{
_synclock.ExitReadLock();
}
}
}
private void RetrieveMetadata()
{
_synclock.EnterWriteLock();
try
{
OpenIdConnectConfiguration config = ConfigManager.GetConfigurationAsync().Result;
_issuer = config.Issuer;
_keys = config.SigningKeys;
}
finally
{
_synclock.ExitWriteLock();
}
}
}
Assuming you have split your Azure app registrations with an API app reg and a SPA app reg, you need to make sure that the API app reg has api: {requestedAccessTokenVersion: 2}
(new manifest) or accessTokenAcceptedVersion: 2
(old soon to be deprecated AAD Graph manifest) set in the manifest. One of ours had null
which made the app still try to use the V1 authority
I cannot seem to find a Protect Web API example using .NET.
I have an MSAL.JS 2 Angular app and I have a .NET Web API in which I need to validate the Bearer token.
Does this exsit?
Thanks