Is Authorization Policy requireAuthenticatedUser compatible with multiple forms of authentication (cookie and JWT) - asp.net-core

I have an ASP.NET Core 2.2 MVC Application with a web API. I'm using cookie auth for MVC pages and JWT Bearer auth for API. I followed the solution described How can i implement Cookie base authentication and jwt in asp.net core 2.2? which is based on https://wildermuth.com/2017/08/19/Two-AuthorizationSchemes-in-ASP-NET-Core-2
The trouble comes when I want to add an authorization policy to ensure the entire site is available to authenticated users
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
})
When I do this, the JWT Bearer authentication is ignored even though the atrribute [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] is present. Thus accessing these API controllers returns a redirect to the login page.
How could I enable the authorization policy and still maintain both forms of authentication? A workaround is to add [Authorize] attribute to all controllers

You can combine the jwt bearer schema and asp.net identity authentication schema when building AuthorizationPolicy :
var policy = new AuthorizationPolicyBuilder(new[] { JwtBearerDefaults.AuthenticationScheme, IdentityConstants.ApplicationScheme })
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
So that both authenticated by asp.net identity and JWT token authentication could access your protected actions .

It seems like it is a known issue in the .Net core. I am using .NET Core 3.1 and it is still not resolved.
Here is my workaround:
var principal = context.User;
if (!principal.Identity.IsAuthenticated)
{
return Task.FromResult(0); //user not logged in
}

Related

How to set up OpenIddict to rely on AzureAd without using Microsoft.AspNetCore.Identity.UI

Our roles model is different so we can't use the stock Microsoft identity database model and all UX that goes with it, more's the pity.
All I want to do is
use OpenIdDict
have AzureAd do authentication
put my own claims into the claims principal so they go into the identity token when OpenIdDict creates it
I'm not interested in IdentityServer for assorted reasons.
I worked through a tutorial and had no trouble building all this using cookie based authn handled in an AccountController but I cannot figure out how to switch over to Azure and could really use some help.
Startup looks like this
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
// .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
// {
// options.LoginPath = "/account/login";
// });
services.AddAuthentication()
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAd"));
// from package `Microsoft.Identity.Web`
services.AddDbContext<DbContext>(options =>
{
// Configure the context to use an in-memory store.
options.UseInMemoryDatabase(nameof(DbContext));
// Register the entity sets needed by OpenIddict.
options.UseOpenIddict();
});
services.AddHostedService<TestData>();
var openiddictBuilder = services.AddOpenIddict();
// Register the OpenIddict core components.
openiddictBuilder.AddCore(options =>
{
// Configure OpenIddict to use the EF Core stores/models.
options.UseEntityFrameworkCore()
.UseDbContext<DbContext>();
});
// Register the OpenIddict server components.
openiddictBuilder.AddServer(options =>
{
options
.AllowAuthorizationCodeFlow().RequireProofKeyForCodeExchange()
.AllowClientCredentialsFlow()
.AllowRefreshTokenFlow()
.SetAuthorizationEndpointUris("/connect/authorize")
.SetTokenEndpointUris("/connect/token")
// Encryption and signing of tokens
.AddEphemeralEncryptionKey()
.AddEphemeralSigningKey()
.DisableAccessTokenEncryption()
// Register scopes (permissions)
.RegisterScopes("api")
// Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
.UseAspNetCore()
.EnableTokenEndpointPassthrough()
.EnableAuthorizationEndpointPassthrough()
;
});
}
There's an AuthorizeController with an Authorize method that looks like this
[HttpGet("~/connect/authorize")]
[HttpPost("~/connect/authorize")]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> Authorize()
{
var request = HttpContext.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Retrieve the user principal stored in the authentication cookie.
// var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
// If the user principal can't be extracted, redirect the user to the login page.
if (!result.Succeeded)
{
var authprops = new AuthenticationProperties
{
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(
Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList())
};
return Challenge(
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
properties: authprops);
}
// Create a new claims principal
var claims = new List<Claim>
{
// 'subject' claim which is required
new Claim(OpenIddictConstants.Claims.Subject, result.Principal.Identity.Name),
new Claim(OpenIddictConstants.Claims.Role,"admin").SetDestinations(
OpenIddictConstants.Destinations.IdentityToken),
new Claim(OpenIddictConstants.Claims.Role,"gerbil wrangler").SetDestinations(
OpenIddictConstants.Destinations.IdentityToken)
};
var claimsIdentity = new ClaimsIdentity(claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
// Set requested scopes (this is not done automatically)
claimsPrincipal.SetScopes(request.GetScopes());
// Signing in with the OpenIdDict authentiction scheme causes OpenIdDict
// to issue a code which can be exchanged for an access token
return SignIn(claimsPrincipal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
}
As I understand the theory of operation, OpenIddict proxies authentication and then issues a new token. That implies that the AzureAd redirect_uri ought to be set to an endpoint provided by OpenIddict, probably something like signin-openiddict and assuming that I'm right about all that, the client talking to OpenIddict will in turn provide a completely distinct and unrelated redirect_uri. But I haven't found any documentation covering this yet, so feel free to educate me.
In OpenIddict (and IdentityServer) the login and token generation are separated.
Those 2 parts are generally:
The user logs in using arbitrary methods and the authentication cookie is created.
The token endpoint reads the authentication cookie and creates tokens.
To use Azure Ad Authentication, you need to alter the first part to generate an authentication cookie using Azure Ad. To achieve this you'll need to implement the ExternalLogin and ExternalLoginCallback as seen in this example: https://github.com/openiddict/openiddict-core/blob/cda55862bcba67bf3de4ba08cf512ee9e2269cf5/samples/Mvc.Server/Controllers/AccountController.cs#L141
Instead of using the SignInManager, you need to create the authentication cookie yourself in the ExternalLoginCallback method. This can be done by using the HttpContext.SignInAsync method.
The second part (token generation) is left unchanged and should work without modification.

Identity server 4: get access toke on asp.net webform .NET 4.5

I am using Identity serve4 for user authentication and authorization and one of my clients is asp.net webform written on .NET 4.5. When the user tried to access the protected webform I am redirecting the user to identity server for authentication. But after authentication, there is a logic which based on currently logged userid, and for getting the current user login info I have to call some token endpoint which requires the token? I inspected the webform after login and I noticed there are some auth cookies. Now my question is how do I get token from this? On asp.net core we get an access token using the httpcontext below method but how do we get the same on asp.net webform using .NET 4.5?
var access_token = await HttpContext.GetTokenAsync("access_token")
The easiest way would be to save the access token in the cookie after authentication. Change the code on client to be like this:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ResponseType = "id_token token",
Scope = "openid profile api1",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
n.AuthenticationTicket.Identity.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));
return Task.FromResult(0);
},
}
});
And then you can retrieve the access_token from current user's claims, like this:
var accessToken = user.FindFirst("access_token").Value;
I explained it with details here: https://nahidfa.com/posts/identityserver4-and-asp-.net-mvc/#call-an-authorized-api-endpoint-using-access-token
Edit:
To set Identity.Name add this code:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
...
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = "name"
}, // This is to set Identity.Name
});

Web site Authentication against Web API

I have the following scenario with net core 3. A web site with a login page. This login page sends the user and password to a Web API that response with a JWT token if the credentials are correct.
How can I set now my web user as authenticated? how can I set the claims of the web user with the claims I recieve from the API token?
Is it neccessary to add any service on the startup of something similar?
Could you provide me with any basic sample of how to do it or any documentation?
Thank you
You can use cookie authentication :
In the Startup.ConfigureServices method, create the Authentication Middleware services with the AddAuthentication and AddCookie methods:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Account/Login";
});
And enable middleware in Configure :
app.UseAuthentication();
app.UseAuthorization();
And in the action which user post credential to , you can send a http request to web api with credential , web api will validate the credential and return back jwt token , your web application then decode token and sign in user like :
var stream = "[token]";
var handler = new JwtSecurityTokenHandler();
var tokenS = handler.ReadToken(stream) as JwtSecurityToken;
var claimsIdentity = new ClaimsIdentity(
tokenS.Claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
RedirectUri = "/Home/Privacy",
};
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
Depending on your front end solution, you need to figure out how to decode the JWT that you received to retrieve the values that you need.
Here are a couple of things, again depending on what you are using on the front end
C#
https://developer.okta.com/blog/2019/06/26/decode-jwt-in-csharp-for-authorization
NPM Package for SPA
https://www.npmjs.com/package/jwt-decode
Here is another good resource for JWT
https://jwt.io/
You can take the JWT you received to view the values that are in it

SPA (Aurelia) + ASP.NET Core WebAPI + Google Authentication

My SPA application (using Aurelia) calls my ASP.NET Core 2 Web API. I need to authenticate users with Google OIDC provider and also secure the Web API with the same method.
Currently I'm able to authenticate user on the client (SPA) side and retrieve id token and access token. With each API call I send the access token in the header.
Now I'm not sure how to handle the server side to validate the token and grant or deny the access to the API. I followed official docs how to add external login providers, but it seem to work only for server-side MVC applications.
Is there any easy way how to do this?
I think for instance IdentityServer4 can support this scenario, but it seems to me too complex for what I need to do. I don't need my own identity/authorization server after all.
Update:
Based on Miroslav Popovic answer, my configuration for ASP.NET Core 2.0 looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(o =>
{
o.Authority = "https://accounts.google.com";
o.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "accounts.google.com",
ValidAudience = "xxxxxxxxxxxxx.apps.googleusercontent.com",
ValidateAudience = true,
ValidateIssuer = true
};
});
services.AddMvc();
}
And in Configure() I call app.UseAuthentication().
When using this setup I get failure message No SecurityTokenValidator available for token.
Update 2:
I made it work. The server configuration is correct. The problem was I was sending access_token to the API instead of id_token.
Since you already have the access token, it shouldn't be too hard to use it to add authentication. You would need something along these lines (not tested):
// Inside Startup.cs, ConfigureServices method
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(
options =>
{
var tokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = "accounts.google.com",
ValidateAudience = false
};
options.MetadataAddress = "https://accounts.google.com/.well-known/openid-configuration";
options.TokenValidationParameters = tokenValidationParameters;
});
// Inside Startup.cs, Configure method
app.UseAuthentication(); // Before MVC middleware
app.UseMvc();
// And of course, on your controllers:
[Authorize]
public class MyApiController : Controller
This post from Paul Rowe might help some more, but note that it's written for ASP.NET Core 1.x and authentication APIs changed a bit in 2.0.
There is also a lot of info here on SO, like this question.

Using ASP.NET MVC 6 Web API Identity when I have a token from FB

I'm using FB SDK for iOS to login my users. My understanding is that I can get a granted token from SDK on the client side and use it to authenticate users in my Web API. What I can't understand is how to use SignInManager<ApplicationUser> to authenticate my users only with a token.
You need JWT Bearer authentication.
Facebook grants you an access_token in JSON Web Token (JWT) format. Include that as a bearer token in your Web API requests.
GET http://www.webapi.com/v1/people
Authorization: Bearer the_facebook_access_token_goes_here
Then use the ASP.NET Security JWT bearer middleware. It will read the token and populate the HttpContext.User.Identity.
The ASP.NET team has a sample here: https://github.com/aspnet/Security/tree/dev/samples/JwtBearerSample Here are the fundamentals of how to configure JWT Bearer authentication.
project.json
dependencies : {
"Microsoft.AspNetCore.Authentication.JwtBearer": "1.1.0-*",
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication();
}
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
// set these to the Facebook Authority and Audience
Authority = ""
Audience = ""
});
Decorate your Web API actions with the [Authorize] attribute.
That might be enough to get you started.