I have spent considerable time looking into getting my WebApi application to use the Owin authentication model as I wish to take advantage of all of the social media login methods. My immediate problem is understanding what it is that I'm doing wrong with regards to authenticating a calling user with a Bearer token set.
My Startup.Auth has been trimmed back to only the following:
OAuthManager.OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthManager.OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AuthorizeEndpointPath = new PathString("/oauth/externallogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
AccessTokenFormat = new MyJwtFormat(),
Provider = new MyOAuthServerProvider(),
};
app.UseOAuthAuthorizationServer(OAuthManager.OAuthServerOptions);
app.UseJwtBearerAuthentication(new MyJwtBearerOptions());
MyOAuthServerProvider looks like this:
public class OAuthServerProvider : OAuthAuthorizationServerProvider
{
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var result = base.GrantResourceOwnerCredentials(context);
return result;
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult(true);
}
}
Question 1 is the above code all that I require when calling the service with a Bearer token set in the Authorization header? Is this the correct place for this Jwt to be placed? The token should be of the form 'Bearer ey.....' etc I believe.
Question 2 should the caller be placing the Bearer token in an 'Authorization' header on the call or some other location?
Question 3 why with the above code in place is my OAuthServerProvider never getting hit on either method? What is it that makes the Owin authentication system say 'Hold on a minute, we've got an Authorization header so I should process that' what is it that my client should be doing differently?
Is it possible that my WebApiConfig is upsetting the Owin OAuth perhaps? I have set the following:
config.SuppressDefaultHostAuthentication();
Although I'm not sure I understand what that means...
I'm trying to test the above using an integration test which performs the following actions:
Creates a user
Logs in as that user (correctly receiving a Bearer token)
Makes an authenticated call with the Bearer token set on the 'Authorization' header (authentication is not set on the Current Principal, HttpContext or OwinContext)
Please ask me questions for clarification, this is costing me a lot of time! Many thanks for any help.
As it turns out, my WebApi configuration was upsetting the authentication process. I had neglected to include the very important line:
config.Filters.Add(new HostAuthenticationFilter(OAuthBearerOptions.AuthenticationType));
After which authentication sprung into action in the OWIN world. This problem occurred mainly because I attempted to retrofit OAuth to a pre-existing WebApi app that hadn't been set up using templates that would have done the above for me.
So just a reminder then that in your WebApi configuration include both lines:
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthManager.OAuthBearerOptions.AuthenticationType));
Related
I'm using multiple JWT Bearer authentication as documented by Microsoft
It all works fine, up until the point where I put it into an application that has controller methods that need optional authentication (a property in appsettings.json controls whether the request needs to be authenticated or not)
I'm doing the optional authentication using my own attribute that implements IAsyncAuthorizationFilter
public class OptionalAuthorizationAttribute: Attribute, IAsyncAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
SetUnauthorized(context);
}
public Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
SetUnauthorized(context);
return Task.CompletedTask;
}
private void SetUnauthorized(AuthorizationFilterContext context)
{
var configService = (IConfiguration)context.HttpContext.RequestServices.GetService(typeof(IConfiguration));
if (configService != null)
{
var enableAuth = configService.GetValue<bool>("AppSettings:EnableAuthentication");
if (enableAuth)
{
if (user?.Identity?.IsAuthenticated != true)
{
context.Result = new UnauthorizedResult();
}
}
}
else
{
context.Result = new UnauthorizedResult();
}
}
}
When I use a single authentication scheme, this works like a charm. When I use two schemes, it works for the default scheme ('Bearer', but not my second scheme). Looking at the logs, I see this
2021-07-19 14:50:41.616 +02:00 [Information]
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Failed
to validate the token.
Microsoft.IdentityModel.Tokens.SecurityTokenUnableToValidateException:
IDX10516: Signature validation failed. Unable to match key:
Now, as I understand that's normal when the first jwt scheme cannot validate the token.. the second can. But, in my OptionalAuthorizationAttribute context.HttpContext.User.Identity is now unauthenticated, so the request is still rejected. I'm guessing that the User.Identity is being populated by what is gotten from the token after it goes through the default scheme (which cannot validate the token so it's an empty ClaimsIdentity and unauthorized.
Any chance I can get the Identity from the second authentication scheme instead? (and make this generic in case I ever need to add additional auth schemes).
Things to work just fine if I swap out my [OptionalAuthorization] tag on the controller and replace it with [Authorize] so I know that my setup is complete.. it's just that my attribute gets triggered before the token has been validated using the second scheme.
Here's a controller showing how I'm using my OptionalAuthorizationAttribute
[Route("[controller]")]
[ApiController]
public class WebDirectoryController:ControllerBase
{
[HttpGet]
[OptionalAuthorization]
[Route("Sources")]
public IActionResult GetPlugins()
{
... implementation
}
}
I have been digging into this topic and after reading through this wanted to respond with my perspective of how to handle authenticating with multiple JWT tokens.
I assume the code you are sharing is a class that defines a custom auth policy. Through my research I see official documentation using AuthorizationPolicyBuilder: Use multiple authentication schemes
I dont see why you cant just determine if you want to add the optional auth policy using the configuration value at startup following the microsoft doc linked above.
The Microsoft Docs just have this description:
Defines whether the bearer token should be stored in the AuthenticationProperties after a successful authorization.
I wondered if saving the JWT allows you to revoke it somehow, but every place I read about JWTs says they are irrevocable. What would you do with a JWT being stored in the AuthenticationProperties?
Storing the JWT in the AuthenticationProperties allows you to retrieve it from elsewhere within your application.
For example, use GetTokenAsync inside of an action, like this:
public async Task<IActionResult> SomeAction()
{
// using Microsoft.AspNetCore.Authentication;
var accessToken = await HttpContext.GetTokenAsync("access_token");
// ...
}
This is useful if, for example, you want to forward the JWT in an outgoing request.
I have the solution with projects: IdentityServer4, ApiServer, MvcClient. I use Hybrid flow. Auth works very well but I can't get the role in MvcClient.
In the MvсСlient app, after authorization, I get access_token. The token contains the necessary claims. But the MVC application cannot access to the user role.
That is, it is assumed that I will call the external API from the MVC application. But I also need the MVC application to be able to use the user role.
Attribute [Authorize] works fine but [Authorize(Roles =
"admin")] doesn't work!
Source code here: gitlab
Unfortunately, I have not found a better solution than to intercept the Access Token event. Then I parsed it and manually added claims to the cookie.
options.Events = new OpenIdConnectEvents
{
OnTokenResponseReceived = xxx =>
{
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
JwtSecurityToken jwt = handler.ReadJwtToken(xxx.TokenEndpointResponse.AccessToken);
var claimsIdentity = (ClaimsIdentity) xxx.Principal.Identity;
claimsIdentity.AddClaims(jwt.Claims);
return Task.FromResult(0);
}
};
I will be very grateful to you! If you look at the source code of the project (it has been updated to asp.net core 2.1) and offer the best option!
I planned to use ASP.NET Identity 2.0 in an ASP.NET MVC application for authentication and authorization.
Referring the below link
JSON Web Token in ASP.NET Web API 2 using Owin
I was able to create a access token(JWT) for the valid user i.e., When user Logs in to the application I will validate the user with name and password then I will issue a JSON web token for that valid user.
Now, I read in some articles that we need to pass the bearer token in headers for every request to validate the user for authentication. In MVC we will provide Authorize attribute for the methods that needs to be protected as shown below…
public class UserController : BaseHRAppController
{
[Authorize]
public ActionResult Index()
{
return View();
}
}
How to tell my MVC application to use JWT for validating the user?
I want to make my MVC application validate the user using JWT whenever the user tries to access the method with authorize attribute. Since I will use AJAX calls in many pages to access method present in MVC controller, I don't think it's good to pass a token on every AJAX request. I need help to accomplish authentication and authorization in an efficient way using ASP.NET Identity in an MVC applicaton.
Currently, I don't know how to use this JWT token for authentication and authorization in an MVC application.
In order for MVC to understand anything about your JWT you basically have to tell it :-) . First, install the Jwt package from nuget:
Install-Package Microsoft.Owin.Security.Jwt
Then open up your Startup.cs file and add a new funtion that will tell MVC how to consume JWT. At basics your Startup will look something like:
using System.Configuration;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.Jwt;
using Owin;
[assembly: OwinStartupAttribute(typeof(TOMS.Frontend.Startup))]
namespace TOMS.Frontend
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
ConfigureOAuthTokenConsumption(app);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = ConfigurationManager.AppSettings["Issuer"];
var audienceId = ConfigurationManager.AppSettings["AudienceId"];
var audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
}
}
You will notice that I am placing the issuer, audienceId and audienceSecret in my Web.config file. (Those values should match the ones on your Resource Server). Also, you might want to ensure you have an updated System.IdentityModel.Tokens.Jwt running:
Update-package System.IdentityModel.Tokens.Jwt
With those set, you may decorate your controller Action with the [Authorize] attribute and play ball.
Play ball of course meaning fire your request from your javascript to your protected controller action:
//assuming you placed the token in a sessionStorage variable called tokenKey when it came back from your Authorization Server
var token = sessionStorage.getItem(tokenKey);
var headers = {};
if (token) {
headers.Authorization = 'Bearer ' + token;
}
$.ajax({
type: 'GET',
url: 'CONTROLLER/ACTION',
headers: headers
}).done(function (data) {
self.result(data);
}).fail(showError);
UPDATE
By The way, if you wish to add the values in your web.config file in order to retrieve them as I did above; simply add them under the AppSettings:
<configuration>
<appSettings>
<add key="Issuer" value="YOUR_ISSUER" />
<add key="AudienceId" value="YOUR_AUDIENCEID" />
<add key="AudienceSecret" value="YOUR_AUDIENCESECRET" />
</appSettings>
</configuration>
...of course, replacing the "values" with your own
I don't know if you solved this, but I was having a similar issue and decided to store the token using FormsAuthentication which I was able to encrypt the token, and on each request the cookie was passed back and then I could decrypt it to get the JWT and then pull out the roles/claims from and then use those roles to create and Identity Principal that would allow me to decorate my controller methods with [Authorize(Role="blah,blah")].
Here is some sample code below.
Once you get the JSON web token back from the api after login, you can use something like:
var returnedToken = (TokenResponse)result.ReturnedObject;
var ticket = new FormsAuthenticationTicket(1, model.Email, DateTime.Now, ConvertUnitToDateTime(returnedToken.expires_in), true, returnedToken.access_token);
string encryptedTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
cookie.HttpOnly = true;
Response.Cookies.Add(cookie)
I have some of my own created classes and methods in there, but it will give you the general idea that you store the JWT access token as well as the expiration date in your FormsAuthentication cookie.
Then the cookie is passed with each request and in your Global.asax file you can have a method to authenticate the request:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
//Extract the forms authentication cookie
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JwtSecurityToken jwTok = TokenHelper.GetJWTokenFromCookie(authCookie);
// Create the IIdentity instance
IIdentity id = new FormsIdentity(authTicket);
// Create the IPrinciple instance
IPrincipal principal = new GenericPrincipal(id, TokenHelper.GetRolesFromToken(jwTok).ToArray());
// Set the context user
Context.User = principal;
}
}
So that method you would decrypt the cookie to get the JWT access token which you can then decode using the System.IdentityModel.Tokens.Jwt library from Microsoft and then take those roles and ID and generate the principal and identity for the user which creates your user with the roles.
Then those roles can be validated against the [Authorize] attribute.
In my webapi application created from template in VS2013 I have added custom OAuthBearerAuthenticationProvider class in Startup.Auth.cs file:
public class CustomBearerAuthenticationProvider : OAuthBearerAuthenticationProvider
{
public override Task ValidateIdentity(OAuthValidateIdentityContext context)
{
UserManager<ApplicationUser> userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = userManager.FindById(context.Ticket.Identity.GetUserId());
var claims = context.Ticket.Identity.Claims;
if (claims.FirstOrDefault(claim => claim.Type == "AspNet.Identity.SecurityStamp") == null
|| claims.Any(claim => claim.Type == "AspNet.Identity.SecurityStamp"
&& !claim.Value.Equals(user.SecurityStamp)))
{
context.Rejected();
}
return Task.FromResult<object>(null);
}
}
Also I have added the variable:
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
And in the ConfigureAuth(IAppBuilder app) method I have added the following lines of code to use the custom OAuthBearerAuthenticationProvider class:
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthBearerOptions.AccessTokenFormat = OAuthOptions.AccessTokenFormat;
OAuthBearerOptions.AccessTokenProvider = OAuthOptions.AccessTokenProvider;
OAuthBearerOptions.AuthenticationMode = OAuthOptions.AuthenticationMode;
OAuthBearerOptions.AuthenticationType = OAuthOptions.AuthenticationType;
OAuthBearerOptions.Description = OAuthOptions.Description;
OAuthBearerOptions.Provider = new CustomBearerAuthenticationProvider();
OAuthBearerOptions.SystemClock = OAuthOptions.SystemClock;
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
All those changes I have made to implement my own custom logic of bearer token verification. For some reason the SecurityStamp verification is not implemented in Webapi application created from template in VS 2013. I thought this should have been done by default.
To verify the SecurityStamp validation concept I have changed SecurityStamp in the database and after that called some webapi method from client using old bearer token, i.e. containing old SecurityStamp claim. Please note my webapi controller is annotated with [Authorize] attribute. After that ValidateIdentity(OAuthValidateIdentityContext context) method has been called and context.Rejected() line has been executed and I was expecting the webapi method should not be called after that and the 401 Unauthorized response should be send back to the client.
But nothing of this happened. Webapi method did get called and the client did successfully get sensitive data from server whereas should not, because the old bearer token the client sent to the server for authentication and authorization must not be valid after password change.
I thought if context.Rejected() have been called in ValidateIdentity method any [Authorize] decorated webapi method should not be called and the
client should receive something like 401 Unauthorized response.
Am I misunderstanding the whole thing? If I am could anyone explain how it works, please? Why after context.Rejected() has been called the [Authorize] annotated controller's webapi method gets called and successfully returns sensitive data? Why the 401 Unauthorized response has not been sent instead? How to achieve the goal which is the 401 Unauthorized response to be sent back to the client when SecurityStamp claim is not the same as in the database currently?
Finally I was able to find the explanation of how the things works. It is the Hongye Sun's comment to his answer to the stackoverflow question:
How do you reject a Katana Bearer token's identity
I cite it here:
"UseOAuthBearerTokens will register Bearer authentication middleware and authorization server middleware into the pipeline. If you call both methods, you will register two Bearer auth middlewares. You need to call UseOAuthAuthorizationServer to register authorization server only."
So I replaced this line of code:
app.UseOAuthBearerTokens(OAuthOptions);
To this one:
app.UseOAuthAuthorizationServer(OAuthOptions);
And things started to work as they should. I. e. the [Authorize] annotated controller's webapi method not called and the 401 Unauthorized response sent back after context.Rejected() has been called.