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!
Related
So I’m using firebase for authentication on my WebAPI app. It gets a JWT token and then authenticates it and then puts the claims in the HttpContext.User.
My only problem is I just want to use Firebase Authentication for authentication, not for authorization. I want to use ASP.NET Identity for this.
so right now, when someone connects to my server, I’ll check if they don’t have an account, if not I will create one for them.
var name = _contextAccessor.HttpContext.User.Claims.First(c => c.Type == "user_id").Value;
ApplicationUser user = await _userManager.FindByNameAsync(name);
if (user == null)
{
user = new ApplicationUser(name);
await _userManager.CreateAsync(user);
}
So this works, and now there is a record in the ASPNetUsers against the user and later on, I can give it Claims and Roles against whatever business rules I’d like.
However, my question is, previously, when I’ve used ASP.NET Identity I’ve been able to leverage all of the built-in features like the Authorize attribute to do my authorization.
So if both authentication and authorization is done using ASP.NET Identity, I can write
[Authorize(Roles = "Administrator")]
Obviously, that won’t work with external authentication because HttpContext.User is the Firebase Authenticated user, not the corresponding ASP.NET Identity user that has the Administrator role.
Is there a way to customize the existing Authorize attribute to configure it to somehow convert my firebase token into an ASP.NET Identity so that it would recognize the roles and claims it has or if I wanted to do all this through middleware, am I going to need to write my own authorize attribute?
Ok so I've finally figured it out. It seems there's a couple of events you can intercept during the Jwt authentication process. In particular, there is an OnTokenValidated event.
services.AddJwtBearer(options =>
{
...
options.Events = new JwtBearerEvents
{
OnTokenValidated = async ctx =>
{
// 1. grabs the user id from firebase
var name = ctx.Principal.Claims.First(c => c.Type == "user_id").Value;
// Get userManager out of DI
var _userManager = ctx.HttpContext.RequestServices.GetRequiredService<UserManager<ApplicationUser>>();
// 2. retrieves the roles that the user has
ApplicationUser user = await _userManager.FindByNameAsync(name);
var userRoles = await _userManager.GetRolesAsync(user);
//3. adds the role as a new claim
ClaimsIdentity identity = ctx.Principal.Identity as ClaimsIdentity;
if (identity != null)
{
foreach (var role in userRoles)
{
identity.AddClaim(new System.Security.Claims.Claim(ClaimTypes.Role, role));
}
}
}
};
});
So what the code above is saying
once the token is authenticated, take the userId from the
external provider
Go into ASP.NET Identity and find the user and
the roles for that user [remember in my original question, I insert a user
into ASP.NET Identity table when they first log in, I'm just grabbing that user]
Insert the roles of that user back into the ClaimsIdentity
The result is that when the Authorise attribute runs, it'll include the AspNET Identity Roles of the user in the check and I can do something like below and it'll check the role.
[Authorize(Roles = "Administrator")]
Yes you can use the authorize attribute with some modification,
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme,Roles = "Administrator")]
But if you want to make it a default scheme, then refer to this answer
Here is a summary, Add it to he Startup class,
services.AddAuthentication(cfg =>
{
cfg.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
cfg.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
I think this should make your Authorization work:
You have to make ClaimsPrincipal the base class of your ApplicationUser class.
(ClaimsPrincipal implements the IPrincipal interface)
Then inside your ApplicationUser class implement/override the IsInRole method that comes with the ClaimsPrincipal (base) class. IsInRole official documentation. The role argument of the method will contain the value of the Roles property on the AuthorizeAtribute that you specify.
Set the HttpContext.User using your instance of type ApplicationUser. (explanation: Setting the property User on Httpcontext allows you to specify (=customize) the user of the current request within asp.net request processing)
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 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.
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));
I'm writing a simple chat application using ASP.NET MVC 5 and SignalR. The application doesn't require any complicated authentication logic. User simply enters their login and enters the system (if there was no such user in the db before, it's created).
My intent was to use Session to hold the logged in user and their information (id from the database and login/username) and write a global filter to check if user is authenticated on each request. I've got some problems with SignalR though. It's not possible to access the Session from the SignalR Hub, while I need it to find out the login of the user who sent the message.
As fas as I found out, it's possible to work with the User.Identity using SignalR's Context. However, in my case Uder.Identity is completely empty. Presumably because I've created the app as 'no authentication' and the mechanism that User.Identity uses to get user data is not aware of my manipulation with session.
The question is, is it possible to elegantly intergate User.Identity into my application and make it aware of the Session? Creating ASP.NET MVC project with individual user accounts creates a mess with stuff like
public AccountController() :
this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
and that's what I don't want to have in my application by any means, since I want to write it as clean as possible and not use any solutions I am not familiar with. I also don't need any external login providers, cookies, etc.
I was thinking about implementing some in-memory storage on my own. However, I would still have to clean this store up at some point of time. I though of cleaning it up when the Session_End event is fired. However, this event will only be fired if there is data in Session which I don't want to have since it would be quite awkward to have standalone in-memory storage and rely on Session events to clean it up and, moreover, to set some data in Session just to make sure Session_End will fire.
Here's the solution I came up with. It's still not as clear as I would like it to be and it uses cookies, so any additions are welcome.
First of all, I had to install Microsoft.AspNet.Identity.Owin package and all its dependencies.
Then I registered my auth as follows:
private void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
}
This method is then called in Configuration method of Startup.cs file.
In order to work with the authentication, an instance of IAuthenticationManager is required. I inject it into my controller and use Ninject to resolve the dependency
kernel.Bind<IAuthenticationManager>().ToMethod(_ => HttpContext.Current.GetOwinContext().Authentication).InRequestScope();
Here's the Login method of Account controller which user is redirected to when auth is required (thanks to LoginPath in ConfigureAuth method):
[HttpPost]
public ActionResult Login(LoginViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = authenticationService.AuthenticateUser(model.Login);
IdentitySignIn(user.Id, user.Login);
return RedirectToAction("Index", "Home");
}
AuthenticationService is my own class which communicates with the database and performs the login to create or return a user.
IdentitySignIn is declared as follows:
private void IdentitySignIn(int userId, string userLogin)
{
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.PrimarySid, userId.ToString()));
claims.Add(new Claim(ClaimTypes.Name, userLogin));
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(new AuthenticationProperties()
{
ExpiresUtc = DateTime.UtcNow.AddDays(200),
IsPersistent = true
}, identity);
}
This method creates a cookie with appropriate info. There is one thing, though. When I check the cookie expiration date, it's not the current date plus 200 days, which is kinda awkward.
SignOut method is quite simple:
public void IdentitySignout()
{
authenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
}
So, User.Identity is now accessible in the SignalR hub with the Identity.Name property.
To do: it would be also nice to get access to the Id property via something like User.Identity.Id. As far as I know, it requires implementing custom Principal.
I am also still thinking of implementing some sort of session of my own using cookies to store the session id on client side, though it will definitely take more time than using Identity.
Addition:
in order to get user id, one might use the extension method of IdentityExtensions:
(Inside the Hub)
Context.User.Identity.GetUserId()
In order for this to work, the Claim with the value of user's id should have the type ClaimTypes.NameIdentifier.
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, userId.ToString()));
claims.Add(new Claim(ClaimTypes.Name, userLogin));
Update 2:
Here are some additional links on the subject that greatly helped me. I do not include links to MS guides since they are quite easy to find.
http://leastprivilege.com/2015/07/21/the-state-of-security-in-asp-net-5-and-mvc-6-claims-authentication/
http://weblog.west-wind.com/posts/2015/Apr/29/Adding-minimal-OWIN-Identity-Authentication-to-an-Existing-ASPNET-MVC-Application