Asp.net Identity 2.0 custom login method - c#

I'm developing ASP.NET 5 application using Identity 2.0. I have two types of users:
Normal - they authenticate using standard login method.
Temporary - they should login based on provided token.
I do not want to store temporary users, except from information required to authenticate user (some username and token). If the user provides username and valid password he should be logged in.
I'm not sure how to achieve this.

You could use Identity in both scenarios simultaneously as well. For first scenario use Identity just like you have done before without any change but for second scenario you a slight modify in login method.
public ActionResoult TempLogin(string username, string password)
{
// imaging you have own temp user manager, completely independent from identity
if(_tempUserManager.IsValid(username,password))
{
// user is valid, going to authenticate user for my App
var ident = new ClaimsIdentity(
new[]
{
// adding following 2 claim just for supporting default antiforgery provider
new Claim(ClaimTypes.NameIdentifier, username),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
// an optional claim you could omit this
new Claim(ClaimTypes.Name, username),
// you could even add some role
new Claim(ClaimTypes.Role, "TempUser"),
new Claim(ClaimTypes.Role, "AnotherRole"),
// and so on
},
DefaultAuthenticationTypes.ApplicationCookie);
// Identity is sign in user based on claim don't matter
// how you generated it Identity
HttpContext.GetOwinContext().Authentication.SignIn(
new AuthenticationProperties { IsPersistent = false }, ident);
// auth is succeed,
return RedirectToAction("MyAction");
}
ModelState.AddModelError("", "We could not authorize you :(");
return View();
}
Since we injected our logic to Identity, we don't need to do extra thing at all.
[Authorize]
public ActionResult MySecretAction()
{
// all authorized users could use this method don't matter how has been authenticated
// we have access current user principal by calling also
// HttpContext.User
}
[Authorize(Roles="TempUser")]
public ActionResult MySecretAction()
{
// just temp users have accesses to this method
}

You'll need to extend the ASP.NET Identity Libraries, using your custom logic and/or storage.
Here you can find an example in my Github account with some useful links that I used to read when I was trying to understand the ASP.NET Identity stuff: https://github.com/hernandgr/AspNetIdentityDemo
Hope it helps!

Related

ASP.NET Core. How can i create an auth cookie without an identity user account?

My app creates an Guid Event token
The customer visits the url of the event like so.
https://example.com/event/a3b2e538-e87c-4444-a655-5171f263ac70
The customer is then redirected to a login form to provide the event password.
If the password maches the event token customer will be able to see the event.
Can i acomblish this without an Identity user account? (I do not wish to follow the classical user identity login approach, in this case).
Maybe something like creating an auth cookie and use it, in the upcoming requests.
[HttpPost]
[Route("validate/{code}")]
public IActionResult Cookie(string code)
{
var user = true; //Validate event token against event password supplied by user
if (user == true)
{
var ident = _userManager.CreateAuthCookie(user, DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties { IsPersistent = false }, ident);
return Redirect("Wherever");
}
ModelState.AddModelError("", "Invalid login attempt");
return View();
}
I guess, I understand your point. You want to create a custom Owin Authentication Framework. The same thing can be achieved using JWT token. It has advantages over OWIN Authentication.
But if you want to design something like this using OWIN, Please take care of these:
First and most important thing, the parameter passed in the url should not have any password or sensitive value. The URL token should be encoded (for encoding u can refer https://www.nuget.org/packages/Microsoft.Owin.Security/) and that encoded token can carry this information. While encoding, you can encode someID (not email address), some sourcecd (that tells the token is from the valid source), some time limit and how many times that token will be valid.
Now, when you get the call to your API end point, you can decode that token, validate and then extract that someID, and make a call to your DB to understand, if the user is valid.
Important: Encoding and decoding algorithm plays a very important role here. So, your encoding and decoding token should not be exposed anywhere.
If he is a valid user, then create a claimsIdentity object.
ClaimsIdentity identity = new ClaimsIdentity();
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, identity.FindFirst("sub").Value));
identity.AddClaim(new Claim("Name", <<add your claims like this>>));
identity = new ClaimsIdentity(identity.Claims, "ApplicationCookie");
AuthenticationProperties properties = new AuthenticationProperties();
AuthenticationManager.SignIn(properties, identity);
Setup Authentication class object in your controller or to a seperate file:
using Microsoft.Owin.Security;
private IAuthenticationManager authenticationManager;
public IAuthenticationManager AuthenticationManager
{
get
{
if (authenticationManager == null)
authenticationManager = HttpContext.GetOwinContext().Authentication;
return authenticationManager;
}
set { authenticationManager = value; }
}
You should also have OWIN layer configured in the middlelayer. You can configure OWIN middle layer based on .Net framework or .Net Core

How do I build a custom role based authorization in ASP Core 2?

I have a database that has a user table, access table and a join table assigning a user to multiple access. The site will verify a user by matching the Identity Username from AD with an username in the Users table to verify they can see the site (Intranet). The access table is used to specify which pages they are allow to visit.
In ASP Core 2 how can I use Authorization to perform the same check at Startup to verify they are in the Users table and then take it a step further and use Roles to allow the user access to specific web pages.
I've gone through the documentation but I can't figure out which way to go as the examples use a login that is not necessary in my case using AD.
I have a users table and don't use AD roles because we have a admin for exchange and I do not have access to that.
Thanks in advance
Authorize attribute is the what you are looking for. For example,
[Authorize(Roles = "Admin, User")]
If you are using OAuth for authentication, you will create a ClaimsIdentity while authenticating. Based on the claim, the Authorize attribute will work out of the box. For example,
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(oAuthIdentity);
}
You can refer to this post, where I have explained a similar scenario in a bit more detail.

IsInRole return false even if there is role in claims

I am working on claim base authentication and it is working fine.
Now I want to add role autorization.
I have role claim for user (eg. "Admin")
When the IsInRole() method is called, there is a check made to see if
the current user has that role. In claims-aware applications, the role
is expressed by a role claim type that should be available in the
token. The role claim type is expressed using the following URI:
http://schemas.microsoft.com/ws/2008/06/identity/claims/role
//Include all claims
//claims is List<Claim> with all claims
var id = new ClaimsIdentity(claims, "Cookies");
Request.GetOwinContext().Authentication.SignIn(id);
If i check if user is in role I will get false. Although I have Role claim with "Admin" value
User.IsInRole("Admin");
Also authorize attrubute on my api will not work
[Authorize (Roles = "Admin")]
I probably misih logic how to make roles visible to User. Probably is not enough to just have Roles in list of claims?
If your service is using Windows authentication, then the IPrincipal.Identity you receive will be of type WindowsPrincipal. It's a little misleading, but the ClaimType that WindowsPrincipal.IsInRole() looks for is not ClaimTypes.Role as you might reasonably expect, but ClaimTypes.GroupSid.
However, you should not assume the actual ClaimType that the current Identity uses for specifying roles because different types of identity use different values. Instead you should reference the ClaimsIdentity.RoleClaimType property.
We have implemented a IAuthenticationFilter along the following lines:
public Task AuthenticateAsync(HttpAuthenticationContext context, cancellationToken)
{
var principal = context.Principal;
if(principal.Identity is ClaimsIdentity && principal.Identity.IsAuthenticated)
{
var ci = (ClaimsIdentity)principal.Identity;
// get the user's additional roles from somewhere and add Claims
ci.AddClaim(new Claim(ci.RoleClaimType, "MyRole"));
}
}
This allows us to use the standard AuthorizeAttribute mechanism in our ASP.Net Controllers. e.g.
[Authorize(Roles="MyRole")]
public IHttpActionResult Get()
{
//authenticated and authorised code here
}
See ClaimsIdentity.RoleClaimType on MSDN for further clarification.
Please note: adding user-defined roles to the WindowsPrincipal can cause problems. It seems that the current implementation of .Net Framework 4.5 (as of April 2017) will sometimes throw an exception when checking roles, expecting the details of the role to be available from Active Directory. See this question for an alternative approach.
Probably, the ClaimType of the claim is just "role".
You should create the claim using Microsoft Schema:
manager.AddClaim(dn1.Id, claim: new Claim(ClaimTypes.Role.ToString(), "ADMINISTRATOR"));
Then User.IsInRole("Admin"); and [Authorize (Roles = "Admin")]will work properly.
This because Microsoft Identity uses the schema:
http://schemas.microsoft.com/ws/2008/06/identity/claims/role
When for role checking.
I suggest you to check ASPNETIdentity database to have a complete view of how che claim are inserted.
I'm pretty sure that the ClaimType of AspNetUserClaims is not like the Microsoft Schema.
Regards
TL;DR Case Sensitivity, Perhaps?
I found that the check used by default in...
[Authorize(Roles = "RoleA,RoleB")]
...was case sensitive.
I created roles in mixed case, and used AspNetCore's Identity manager, with an non-EF memory implementation for testing.
UserManager.IsInRole("RoleA") returned true, but when checked via the ClaimsPrincipal, HttpContext.User.IsInRole("RoleA") returned false. I dumped the claims out to text and could see that there were role claims for the correct MS schema...
ClaimType:[http://schemas.microsoft.com/ws/2008/06/identity/claims/role], ClaimValue:[ROLEA], Issuer:[TokenServer]
ClaimType:[http://schemas.microsoft.com/ws/2008/06/identity/claims/role], ClaimValue:[ROLEB], Issuer:[TokenServer]
...but the claim value (the role) was upper case.
To fix the problem, I just had to change the attribute to...
[Authorize(Roles = "ROLEA,ROLEB")]
... and it worked.
So, if you are having a problem getting roles authorization to work in AspNetCore, try to read the claims, and match the claims exactly. You can read the claims by accessing the HttpContext.User.Claims object...
foreach (var claim in HttpContext.User.Claims)
Console.WriteLine($"ClaimType:[{claim.Type}], ClaimValue:[{claim.Value}], Issuer:[{claim.Issuer}]");
It could of course be that I somehow donkey-coded the roles to upper case, or somewhere used the NormalisedRole, but you might have done the same thing...
Note that
HttpContext.User.Identity.RoleClaimType: "role"
may be different to
ClaimTypes.Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
So when generating the claims identity you may need to add claims using "role" as the key instead of the ClaimTypes constants. ClaimsIdentity.IsInRole(String) uses the claim key as defined by ClaimsIdentity.RoleClaimType.
My factory code looks like this ...
var identity = await base.GenerateClaimsAsync(user);
var roles = await UserManager.GetRolesAsync(user);
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
identity.AddClaim(new Claim("role", role));
}
return identity;
The first add is really superfluous, but makes me feel like I am actually adding the right claim.
You did not mention which Authentication approach you are using, but if you are using JWT Authentication, then you need to add the roles to the ClaimsIdentity when generating the token, as detailed in this post: ASP.NET Core JWT mapping role claims to ClaimsIdentity

ASP.NET MVC Identity with existing user table

I am creating a new web application that needs to authenticate users against an existing user table that exists from another web application. User registration, forgotten password, etc are handled in that application. All I need in my new application is login.
I wondered if it was possible to overwrite some Identity class to point to that table to authenticate the user so I can use the existing Identity functionality like the [Authorize] attribute on Controllers and to redirect back to the login page, etc.
I got the same situation like yours when trying to upgrade my legacy system to OWIN authentication, I also had my own User table and authentication workflow which's totally different with ASP.NET Identity offers.
Firstly I had tried to customize ASP.NET Identity, but it was not sorted out that way. My thought is Identity was painful and much more complicated to customize for legacy app since it has lots of abstract levels.
Eventually I have come up with the solution to strip out ASP.NET Identity and manage claim identity by myself. It's incredibly simple, my below simple demo code is how to login with OWIN without ASP.NET Identity, hope that helps:
private void OwinSignIn(User user, bool isPersistence = false)
{
var claims = new[] {
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.Email, user.Email)
};
var identity = new ClaimsIdentity(claims, DefaultApplicationTypes.ApplicationCookie);
var roles = _roleService.GetByUserId(user.Id).ToList();
if (roles.Any())
{
var roleClaims = roles.Select(r => new Claim(ClaimTypes.Role, r.Name));
identity.AddClaims(roleClaims);
}
var context = Request.GetOwinContext();
var authManager = context.Authentication;
authManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistence }, identity);
}
[HttpPost]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
return View();
var user = _userService.GetByEmail(model.Email);
if (user != null && (user.Password == model.Password))
{
OwinSignIn(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
ModelState.AddModelError("", "Invalid email or password");
return View();
}
You can have the Identity in a separate database without problems, as long as it has the identity format. Point the Usermanager/Rolemanager to your other database using the connection string.
If the existing authentication is not an identity setup, you won't be able to use the identity framework to connect to your other database out of the box. The identity framework expects a certain format. You can rewrite the managers to understand your user format in the database as long as you fulfill the minimum requirements as stated in the comments below.
You can always write your own OWIN behaviour though. See #Cuong Le's example

Mvc OAuth2 how to store data for the current Autentication in a OAuthAuthorizationServerProvider

im sorry for my bad English, im french.
I will try to explain my question the best i can.
i have a OAuthAuthorizationServerProvider wish work fine.
This is to allow other application to connect with my Asp.Net Identity 2.0 Authentication Server.
I wish to store data for the current authentication. If the user is connected twice, they will not necessary have the same stored data. I don't think Session is the right thing for this becose i dont use cookie. I use Bearer, an access_token and a refresh_token.
I can simply store the refresh_token in a table, then refer it on each request but i don't like to store sensible data like that, especially if the framework provide a way to do what i want.
I need to store the data relative to each external authentication, not to the user. Something like Claims but only for the current authentication session.
tanks to point me on the right path.
In your OAuthAuthorizationServerProvider, you will have overridden the GrantResourceOwnerCredentials method. This is where you will have validated the user, and it's the place where you can add additional claims for the user.
Here is an example that validates the user against ASPNet Identity, and adds an additional claim to the identity that is returned.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
var mgr = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await mgr.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
var usrIdentity = await mgr.CreateIdentityAsync(user, context.Options.AuthenticationType);
foreach (var c in usrIdentity.Claims)
{
identity.AddClaim(c);
}
//
// Add additional claims to your identity
//
identity.AddClaim(new Claim("your_custom_claim", "your_custom_claim_value"));
context.Validated(identity);
}
That said, in your comments you seem to be using Cookie and Token in the same sentence, and possibly confusing the two. Check out this blog post which should give you a good example.
Also check out the ASP.NET Identity Recommended Resources page too.

Categories