How to authenticate both Blazor web app and api? - authentication

I am using .NET 6 RC1 with Blazor Server for my Server and a separate Blazor Webassembly project for my client. Both authenticate with Azure Ad B2C. The authentication aspect is working fine.
The server hosts both a BlazorHub for the server side (admin) pages as well as a separate hub for the client to be able to authenticate and call into.
The client connects to the hub mentioned above, as well as perhaps calls any potential API endpoints.
The authentication setup I have is as follows
var builder = WebApplication.CreateBuilder(args);
var services = builder.Services;
var configuration = builder.Configuration;
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(configuration, AzureB2C);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(configuration, AzureB2C);
The problem here is that if I have both of these defined, the Client (localhost:4201) works and can connect to the protected Hub on the server.
If I attempt to connect to the server endpoint (localhost:5001) I simply receive a browser 401 page, with nothing in the console and Visual Studio output except for a crbug/1173575, non-JS module files deprecated. warning and additionally in the browser console I receive an Uncaught pause_on_uncaught error.
If I comment out the section for AddMicrosoftIdentityWebApp, the client can still connect fine as before, and the Server fails to authenticate as described above.
If I instead comment out the section for AddMicrosoftIdentityWebApi, the server can authenticate and works as expected, but now the client fails to authenticate with the Hub because the JWT token is not being validated.
How do I mix both of these successfully so I can both authenticate in the Server and the Client?
I have found this on the Microsoft Identity Web github page which basically is what I am trying to accomplish, except the code they provided doesn't work for me the same as I have in my example.

I seem to have resolved my issue by doing the following.
Change the order of my Authentication methods.
AddMicrosoftIdentityWebApi is first.
AddMicrosoftIdentityWebApp is second.
Specify for my Hub that it will use the JwtBearerDefaults.AuthenticationScheme
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapHub<GameHub>("/hubs/gamehub")
.RequireAuthorization(new AuthorizeAttribute
{
AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme,
Policy = PlayerPolicy
});
endpoints.MapFallbackToPage("/_Host");
});
Now both authorize just fine!
I'd love to hear if anybody knows the solution for this keeping the original order of them.

Related

Trying to use token acquisition with ADFS authenticated app

I am stuck on a problem that i cannot think my way out of and have searched everywhere online for answers to no avail.
Here is the problem:
I usually embed PowerBI reports in asp.net application. I follow the Microsoft tutorial. Where we registered an azureAD app as service principal. And we use the Microsoft.Identity.Web library to authenticate our users as well as authenticate as the app's service principal for accessing PowerBI reports.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
builder.Services.AddScoped(typeof(PowerBiServiceApi));
The problem now is that I am building an app that doesn’t authenticate with the Microsoft.Identity.Web but will actually be authenticating users using individual accounts/ ADFS and federation service.
The problem is that I am unable to do token acquisition by authenticating as the app's service principal.
.AddOpenIdConnect(options =>
{
options.ClientId = "xxxxx-xxxxx-xxx-xxx-xxxxxx";
options.Authority = "https://xxxxxxxxxxx";
options.SignedOutRedirectUri = "https://localhost:xxxx/";
options.Events = new OpenIdConnectEvents
{
OnRemoteFailure = OnAuthenticationFailed,
};
})
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
However when I am unable to run this app and embed successfully.
My main question is:
is it possible to authenticate my users with one authentication provider (ADFS federation service via openID or wsFederation )
whilst also doing using Microsoft.Identity to do token acquisition
Thus far I've had success with using OpenIDConnect directly with ADFS and MSAL library to get tokens (also from ADFS) for downstream api's. For entries in ADFS using a client secret (i.e. server apps) I would use the IConfidentialClientApplicationBuilder, whereas native apps would use IPublicClientApplicaationBuilder.
This means I never need nor can use this code:
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")) .EnableTokenAcquisitionToCallDownstreamApi() .AddInMemoryTokenCaches();

Authenticate on a on-premise webservice

I am having trouble with authentication in my asp-net web service. This service has an angular gui and some mvc api.
This service will be deployed on multiple servers inside my organisation windows or linux (or even docker). This seems to prevent me from using basic active directory authentication. Important part is I don't know before deployment the target ip:port of deployment.
I am trying to add an azure active directory based authentication.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
}
Everything works fine until I get into the Reply Url part of the authent process. It seems I can't find a way to avoid using a reply url.
In dev environement I have to add localhost as a valid reply url to my app and everything works fine. But what about future deployment? It seems very painfull to maintain a complete and up to date list of all the alive servers.
Is there a way to do the complete auth process client side and then go back to back end only when the final token has been received? I then suppose that the backend would have to validate the token against the authority has it was not implied in the process but that's fine.
What am I missing here?

Identity Server 4 User management API

I've managed to get a solution working with a single page application project (ReactJS), an API running on ASP.net Core project and an IdentityServer 4 project.
I want to be able to call an API on the IdentityServer 4 project from the single page application project.
I created a simple controller class in the IdentityServer 4 application, with the authorize attribute. If I call it via Postman, however, I get the HTML for the login page back.
This happens after I already logged in on the API, and I use that same token.
How am I supposed to log in to identity server to make calls to it to manage users?
As stated in the comments, you should definitely add more information to your question. Your controller is part of the identity server's mvc application? Are you using AspnetCore.Identity?
If so, your controller is protected by AspnetCore.Identities's cookie authentication scheme. You need to send the cookie to access the controller. This has nothing to do with identity server as you are on the local MVC application, it's just plain vanilla MVC.
Postman has problems sending cookies, you need the interceptor chrome extension. You also need to login though postman.
This will probably work if the SPA is hosted by the same MVC application. If not you will need to configure your mvc applications to validate access tokens (not just issue them), like this:
// Adds IdentityServer
app.UseIdentityServer();
// Accept access tokens from identity server
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
AuthenticationScheme = "Bearer"
ApiName = "api1"
});
This way you can request access tokens for your local api through identity server, (You also need to configure an api scope for 'api1').
To validate them in your api-controller add this to your controller:
[Authorize(ActiveAuthenticationSchemes = "Bearer")]
[HttpGet]
public string Bearer()
{
return "Access Token Accepted";
}

How to set up cookie based authentication with NancyFx and IdentityServer3 (non-API website)

We have an environment with the following:
Standalone IdentityServer3 instance (issues reference tokens, not jwt)
ASP.NET WebAPI resource server
.NET client applications that authenticate against IdSvr (via resource owner flow)
...and now we'd like to start adding an OWIN-hosted web app that will use NancyFx to serve server-rendered pages as well as a couple AngularJS SPAs. This Nancy website will NOT host any APIs, but may consume data from our existing API. I'd like to add authentication in the OWIN pipeline to help secure our Angular applications from being sent down to users who don't have access.
This would be in contrast to sending down the SPA code, and having Angular determine if the user should see anything. In that case we've already exposed the javascript code base, and this we want to avoid.
I'm trying to understand how I should configure this Nancy site to authenticate users against IdentityServer using the implicit flow. I have implemented this authentication scheme in standalone SPAs before (where all authentication was handled by AngularJS code and tokens were stored in HTML5 local storage), but I'm a bit lost on how to properly tackle this within the OWIN pipeline.
I'm thinking that the OWIN cookie authentication middle-ware is the answer, but does that mean the following?
I need to redirect the user to IdentityServer (using the proper url arguments for implicit flow)?
IdentityServer will redirect the user back to my site on a successful login, so is that where I hook into the OWIN Authorization manager to set the appropriate cookie?
...or am I thinking about this all wrong?
For reference, I've read through the following posts, and they're very helpful but I'm not quite seeing the big picture with OWIN. I'm going to experiment with the UseOpenIdConnectAuthentication middle-ware next, but I would appreciate any guidance SO might have here.
http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/
https://github.com/IdentityServer/IdentityServer3/issues/487
Fundamentally, implementing OpenID Connect authentication in a Nancy app hosted via OWIN is really not different from implementing it in any MVC/Katana app (the Thinktecture team has a sample for this scenario: https://github.com/IdentityServer/IdentityServer3.Samples/tree/master/source/Clients/MVC%20OWIN%20Client)
You basically need 3 things: the cookie middleware, the OpenID Connect middleware and the Nancy middleware:
public class Startup {
public void Configuration(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
// Set the address of your OpenID Connect server:
Authority = "http://localhost:54541/"
// Set your client identifier here:
ClientId = "myClient",
// Set the redirect_uri and post_logout_redirect_uri
// corresponding to your application:
RedirectUri = "http://localhost:56765/oidc",
PostLogoutRedirectUri = "http://localhost:56765/"
});
app.UseNancy(options => options.PerformPassThrough = context => context.Response.StatusCode == HttpStatusCode.NotFound);
}
}
If you're looking for a functional demo, you can take a look at https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Nancy/Nancy.Client (note: it doesn't use IdentityServer3 for the OIDC server part but it shouldn't make any difference for the client app).

What are the correct authentication settings for an on-premise ADFS flow?

I've been reading Vittorio Bertocci's blog to try to get up to speed on using ADFS to manage authentication and claims in either an MVC app or WebApi service. It looks like it's getting to be very approachable.
I am now trying to build out a POC using ADFS to do common claims resolution for internal sites/services in our enterprise. Our users would be on the internal network along with our endpoints. Right now we use Windows Integrated auth by default and each site does the work of looking up a user’s name, email, and other AD details and inspecting the claims principal for roles via IsInRole. The claims we get with integrated auth includes just a SamIdentifier and a bunch of group SIDs. I’d like ADFS to do that work for us but still give our users a challenge-free experience. Long term, we will likely add support for non-domain-joined devices on some sites/services, so that is another motivation to explore ADFS.
So I've set up a simple sample app in VS2013 using Organizational Accounts (On Premise) that will dump out a current user's claims, configured the metadata endpoint and audience uri, communicated that info along with the claims I'd like mapped to my ADFS admin (2012, btw), and deployed my site to a development server. So my host is still IIS, though I hope to use Owin middleware to set up authentication rather than web.config (WIF-style).
Given that IIS is my host, how do I configure authentication for my site: anonymous? And my web.config should specify "None" for the authentication mode and deny="?" for authorization, correct?
The other question I have that Vittorio didn't get into in his post about on-premise adfs was the nature of the bearer token and whether or not we need to explicitly configure the middleware to use cookies. My startup config looks like this right now:
public void ConfigureAuth(IAppBuilder app)
{
app.UseActiveDirectoryFederationServicesBearerAuthentication(
new ActiveDirectoryFederationServicesBearerAuthenticationOptions
{
MetadataEndpoint = ConfigurationManager.AppSettings["ida:AdfsMetadataEndpoint"],
TokenValidationParameters = new TokenValidationParameters() { ValidAudience = ConfigurationManager.AppSettings["ida:Audience"] }
});
}
It looks like this middleware is expecting JWT tokens (given that there is a JwtSecurityTokenHandler on the class). Is there any configuration we need to do on the ADFS side to issue JWT tokens? My understanding is that I'll receive a SAML token by default.
And should we expect to use the CookieAuthentication middleware to manage the token or will the browser just keep including it for the life of the session?
Thanks, all!
UPDATE:
So based on Vittorio's help below and some further research, I now have a simple website with just one page protected with an [Authorize] attribute. My startup class's ConfigureAuth method now looks like this:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseActiveDirectoryFederationServicesBearerAuthentication(
new ActiveDirectoryFederationServicesBearerAuthenticationOptions
{
MetadataEndpoint = ConfigurationManager.AppSettings["ida:AdfsMetadataEndpoint"],
TokenValidationParameters = new TokenValidationParameters() { ValidAudience = ConfigurationManager.AppSettings["ida:Audience"] }
});
}
We've added my website as a relying party trust in ADFS and created a half dozen claims rules. Everything seems correct so far, but I'm still struggling. I hit the protected "claims" page and get a 401 response with a WWW-Authenticate:Bearer header. So far, so good.
But that's it. How does the browser know where to get authenticated and receive a token? If I was proving out the separate client scenario, my client would be configured with the location of the token authority, but in this simple website scenario, I'm clearly missing something.
UPDATE 2:
I wonder if the implementation for on-premise ADFS just isn't ready yet? Or perhaps the documentation just isn't there yet - or both...
I pulled out all the Owin packages and reverted to using the WSFederationAuthenticationModule and SessionAuthenticationModule, along with all the web.config settings in system.identityModel and system-identityModel.services that have been around a while. Basically, I made the solution look like the one you get from VS2013 when you selected Organizational Accounts --> On Premise. Everything works beautifully and I have all my configured claims coming from ADFS. I see the initial 302 redirect to ADFS, the challenge-response, and ultimately have a SAML token serialized into a secure session cookie. On the website, I echo back the claims like so:
var user = User as ClaimsPrincipal;
ViewBag.Claims = user.Claims;
return View();
This is why I suspect the middleware is incomplete: when you use that new template in VS2013, the wizard goes to the federation metadata endpoint you specify and builds out all the web.config settings by reading that xml and, in addition, sets some intelligent defaults. That's sort of what I expected to happen in the Owin middleware - it should have everything it needs to know since I pass in the same metadata endpoint. I was hoping that "magic" would replace using the FAM/SAM modules and all the accompanying config.
1) If you are configuring a web UX app, that is something meant to be consumed through browser redirects, you want to use http://www.cloudidentity.com/blog/2014/04/29/use-the-owin-security-components-in-asp-net-to-implement-web-sign-on-with-adfs/. You'll see that the cookie middleware does come into play in that case.
2) If you are configuring a web API, as in something that is consumed by a rich client or another server, or in general anything that is not a browser roundtripping, see http://www.cloudidentity.com/blog/2013/10/25/securing-a-web-api-with-adfs-on-ws2012-r2-got-even-easier/. In that case you do not need cookies, given that there is no session -every single call must carry the token.
HTH
V.
As Vittorio said you need to differentiate if you create a web page with web api or web api only. Follow his blog posts they are great!!
If you host a webapi only project in an IIS you need to set the authentication to "forms authentication".
This works also if your web api is covered behind a web application proxy. make sure that you configure your endpoint (published web application) not to preauthenticate. the value for "preauthenticate" should be "pass through".
bg Andrej