How to add custom data to claim during login process? - asp.net-mvc

I was reading article from here
This way we can add claim during login
var user = userManager.Find(userName, password);
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = persistCookie }, identity);
This way reading back the value stored in clam
var prinicpal = (ClaimsPrincipal)Thread.CurrentPrincipal;
var email = prinicpal.Claims.Where(c => c.Type == ClaimTypes.Email).Select(c => c.Value).SingleOrDefault();
Now I have few questions
How could I add my custom data to claim. Suppose user role names.
Suppose the things I want to add that is not available in ClaimTypes then how could I add my custom data to claim?
How to read back my custom data stored in claim?
My action is decorated with authorized attribute where role name is specified like below one:
..
public class HomeController : Controller
{
[Authorize(Roles = "Admin, HrAdmin")]
public ActionResult PayRoll()
{
return View();
}
}
Do I need to go for custom authentication to extract roles from claim to set in GenericPrincipal?
Last question: When we go for role based authorization then roles are stored in authorization cookie? Do I need to write code to store roles in authorization cookie or ASP.net engine does it for us?
Same way claims are store in authorization cookie generated by owin cookie?

If you are using Identity than identity have its own method which can handle roles and everything you just have to login with this line.
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
you will have to add role manager in Identity Config File
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
: base(roleStore)
{ }
public static ApplicationRoleManager Create(
IdentityFactoryOptions<ApplicationRoleManager> options,
IOwinContext context)
{
var manager = new ApplicationRoleManager(
new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()));
return manager;
}
}
and register in Startup.Auth.cs
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
and you won't have to give roles to the authentication manually. you just have to write
[Authorize(Roles = "Admin, HrAdmin")]
if you want to add that manually without identity given method than use this below
private void IdentityLogin(UserInfo UserInfo)
{
// Waleed: added the role in the claim
var identity = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Name, UserInfo.Email),
new Claim(ClaimTypes.Sid, UserInfo.UserID),
new Claim(ClaimTypes.Role, UserInfo.Roles)
}, DefaultAuthenticationTypes.ApplicationCookie);
var claimsPrincipal = new ClaimsPrincipal(identity);
// Set current principal
Thread.CurrentPrincipal = claimsPrincipal;
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignIn(identity);
}
Claims are of two types one are in your session and other are stored in db. Session Claims are above in IdentityLogin method and db claims can be written as
UserManager.AddClaim(userId,new Claim())

Related

Using custom RoleClaimType on OAuthBearer middleware

The OAuth server issues role claims using different claim type from System.Security.Claims.ClaimTypes.Role:
var adminRole = new Claim("CustomRole", "Admin");
context.Ticket.Identity.AddClaim(adminRole);
How can I tell the OAuthBearerAuthentication middleware to use my custom role claim type so it gets the Authorize attribute to work:
//Startup
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions ...
[Authorize(Roles = "Admin")]
public IHttpActionResult SecureAction()
Make sure that your Identity is set to an instance of System.Security.Claims.ClaimsIdentity:
The ClaimsIdentity.RoleClaimType property is used to specify the claim that represents the role, and it's used when evaluating this identity for the ClaimsPrincipal.IsInRole(String) method.
You can easily clone your original identity from the original one (passing the original claims) and specifying a different roleType name using the constructor:
ClaimsIdentity(IIdentity, IEnumerable<Claim>, String, String, String)
In OnValidateIdentity function of OAuthBearerAuthenticationProvider, we can rebindClaimsIdentitywith appropriateRolaClaimTypeandNameClaimType`:
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new OAuthBearerAuthenticationProvider
{
OnValidateIdentity = context =>
{
var claimsIdentity = new ClaimsIdentity(
context.Ticket.Identity.Claims,
OAuthDefaults.AuthenticationType,
CustomClaimTypes.Name,
CustomClaimTypes.Role);
context.Validated(claimsIdentity);
return Task.FromResult(0);
}
}
});

How to store bearer tokens when MVC and Web API are in different projects

Situation:
I have a Web API 2 project which acts as an Authorization server (/token endpoint) and a resource server. I am using the template that comes out of box with ASP.Net Web API minus any MVC reference.
The Start.Auth is configured as below:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
{
AppId = ConfigurationManager.AppSettings["Test_Facebook_AppId"],
AppSecret = ConfigurationManager.AppSettings["Test_Facebook_AppSecret"],
//SendAppSecretProof = true,
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
return Task.FromResult(0);
}
}
};
facebookAuthenticationOptions.Scope.Add("email user_about_me user_location");
app.UseFacebookAuthentication(facebookAuthenticationOptions);
}
The MVC 5 Client (different Project) uses the Web API app for authorization and data. Below is the code to retrieve the Bearer token in case of Username/Password store:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
model.ExternalProviders = await GetExternalLogins(returnUrl);
return View(model);
}
var client = Client.GetClient();
var response = await client.PostAsync("Token",
new StringContent(string.Format("grant_type=password&username={0}&password={1}", model.Email, model.Password), Encoding.UTF8));
if (response.IsSuccessStatusCode)
{
return RedirectToLocal(returnUrl);
}
return View();
}
Problem
I could retrieve the Bearer token and then add it to the Authorization Header for subsequent calls. I think that would be ok in case of an Angular App or a SPA. But I think there should be something in MVC that handles it for me, like automatically store it in a cookie and send the cookie on subsequent requests. I have searched around quite a lot and there are posts which hint towards this (Registering Web API 2 external logins from multiple API clients with OWIN Identity) but I haven't been able to figure out what to do after I get a token.
Do I need to add something in the MVC app Startup.Auth?
Ideally, I need the functionality which the AccountController in ASP.Net Template (MVC + Web API) gives out of box (Logins, Register, External logins, forget password etc etc...) but with the MVC and Web API in different projects.
Is there a template or a git repo which has this boiler plate code?
Thanks in advance!
Update
Incorporating #FrancisDucharme suggestions, below is the code for GrantResourceOwnerCredentials().
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);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
//Add a response cookie...
context.Response.Cookies.Append("Token", context.Options.AccessTokenFormat.Protect(ticket));
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
But I can't still seem to get that Cookie or figure out what to do next.
Restating Questions:
What would be the correct way to authenticate, authorize and call Web API methods (Auth and Resource server) from an MVC client?
Is there boilerplate code or template for AccountController which does the basic plumbing (Login, register - internal/external, forgot password etc.)?
You could have your Startup class return a response cookie that the client will then return on all subsequent requests, here's an example. I would do it in GrantResourceOwnerCredentials.
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//your authentication logic here, if it fails, do this...
//context.SetError("invalid_grant", "The user name or password is incorrect.");
//return;
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
AuthenticationTicket ticket = new AuthenticationTicket(identity);
//Add a response cookie...
context.Response.Cookies.Append("Token", context.Options.AccessTokenFormat.Protect(ticket));
context.Validated(ticket);
}
The Startup class:
public partial class Startup
{
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public Startup()
{
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
}
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
//I use CORS in my projects....
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
WebApiConfig.Register(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true, //I have this here for testing purpose, production should always only accept HTTPS encrypted traffic.
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new AuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
That assumes the client has cookies enabled, of course.
Then, modify your MVC headers to add the Authorization header to all requests as such.
In the ActionFilterAttribute, fetch your cookie value (Token) and add the header.
Instead of storing in session, I have added it to the the DefaultRequestHeaders as shown below so I don't need to add this in every call I make to Web API.
public async Task AuthenticateUser(string username, string password)
{
var data = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", username),
new KeyValuePair<string, string>("password", password)
});
using (HttpResponseMessage response = await APIClient.PostAsync("/Token", data))
{
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsAsync<AuthenticatedUser>();
APIClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.Access_Token);
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}

How to do programmatic sign in using aspnet Identity Framework v2?

I'm cobbling together snippets of code from blogs and different places to try to get this to work. Normally, I'd refer to the reference documentation, but I can't find it here or anywhere else. It's just videos and demos for specific use cases that include user management or facebook or twitter.
I have a proprietary authentication service that I'm using. User accounts are not managed inside my application. So I need to be able to sign in a user that's completely constructed at run time.
Here's what I'm trying now in my MVC app.
using System.Security.Claims;
public class HomeController : Controller {
public ActionResult Scratch() {
var claims = new Claim[] {
new Claim(ClaimTypes.Name, "somename"),
new Claim(ClaimTypes.NameIdentifier, "someidentifier"),
new Claim("foo", "bar"),
};
var identity = new ClaimsIdentity(claims);
var authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.SignIn(identity);
return Content(
$"authentication manager type: {authenticationManager.GetType()} \n"
+ $"authenticated: {HttpContext.User.Identity.IsAuthenticated} \n"
+ $"user name: {HttpContext.User.Identity.Name} \n",
"text/plain");
}
}
The output is
authentication manager type: Microsoft.Owin.Security.AuthenticationManager
authenticated: False
user name:
Questions:
Why does the output show that the user has not been authenticated? What more do I have to do to get this user authenticated?
Where is the documentation for this framework?
Update
Startup.cs
public partial class Startup {
public void Configuration(IAppBuilder app) {
ConfigureAuth(app);
ConfigureAnalyticContext(app);
}
}
Startup.Auth.cs:
(there is actually much more, but all the rest has been commented out, in search of finding a minimal configuration that works)
public partial class Startup {
public void ConfigureAuth(IAppBuilder app) {
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
}
AnalyticContext.Auth.cs
(this is my Entity Framework context, I doubt it's related to this problem)
public partial class Startup {
public void ConfigureAnalyticContext(IAppBuilder app) {
app.CreatePerOwinContext(() => CentoAnalyticsContext.Create());
}
}
Well, it seems that you are not using ASP.NET Identity. ASP.NET Identity is new membership system of asp.net, which automatically creates database tables for storing users, encrypting password, etc.
What you are trying to do is to use the new authentication system provided by OWIN, which replaces the old FormsAuthentication style.
To make it work, you have to create the cookie authentication. Like this:
public static class AuthConfig
{
public const string DefaultAuthType = "DefaultAppCookie";
public const string LoginPath = "/System/SignIn";
public static void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthType,
LoginPath = new PathString(LoginPath)
});
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier; //or whatever
}
}
In the login action:
var claims = new Claim[] {
new Claim(ClaimTypes.Name, "somename"),
new Claim(ClaimTypes.NameIdentifier, "someidentifier"),
new Claim("foo", "bar"),
};
ClaimsIdentity identity = new ClaimsIdentity(claims, AuthConfig.DefaultAuthType);
IAuthenticationManager authManager = Request.GetOwinContext().Authentication;
authManager.SignIn(new AuthenticationProperties() { IsPersistent = true }, identity);
I think that should be enough to make it work in your app. A few days ago I answered a similar question MVC Authentication - Easiest Way, take a look, it might be helpful.
I recently have added Active Directory authentication, constructed ClaimsPrincipal myself and signed-in the same way you do.
And you are indeed missing .UseCookieAuthentication in your ConfigureAuth(IAppBuilder app)
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "MyAuthenticationName", // <-- this must match the AuthenticatioType name when you do sign-out
LoginPath = new PathString("/MyLoginPath"),
CookieName = "MyCookieName",
CookieHttpOnly = true,
});
}
And you don't need UseExternalSignInCookie.
Request.IsAuthenticated will be false with in the same request flow.
I think you still need to update the current security principal if you need to check IsAuthenticated for the request as authenticationManager.SignIn only validates the user against data store and sets the OWIN cookie which when sent back in subsequent request sets the security principal , usually a redirect takes care of this as in most cases there will be redirection in home page or something. If you still need to check with in the same request you can do something like below depending on your requirement
var claims = new Claim[] {
new Claim(ClaimTypes.Name, "somename"),
new Claim(ClaimTypes.NameIdentifier, "someidentifier"),
new Claim("foo", "bar"),
};
var identity = new ClaimsIdentity(claims,DefaultAuthenticationTypes.ApplicationCookie,
ClaimTypes.Name, ClaimTypes.Role);
var principal = new ClaimsPrincipal(identity);
System.Threading.Thread.CurrentPrincipal = principal;
if (System.Web.HttpContext.Current != null)
System.Web.HttpContext.Current.User = principal;
Hope this helps.

Claims GetUserId is null

I hope you're all great, this time I'm here to ask an issue that I can't figure it out and it's about Identity, the thing is I want to get de User Id from the Claims principal or whereever it is, right now the only thing that I have is
var principal = ClaimsPrincipal.Current;
var id1 = principal.Claims.FirstOrDefault(c => c.ValueType == ClaimTypes.NameIdentifier);
but when I try to get the UserId and I go to the information inside the claims I can't find the value even if I saved it in the AuthorizationTicket at login.
I'm working with MVC template and Web api service My service is hosted in IIS and with it I manage the authentication with an accountcontroller via Token
this is my AuthenticationProperties
public static AuthenticationProperties CreateProperties(string userName, string userid)
{
IDictionary<string, string> data = new Dictionary<string, string> { { "userName", userName }, { "userId", userid } };
return new AuthenticationProperties(data);
}
and my GrantResourceOwnerCredentiales
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
//var userManager = DependencyResolver.Current.GetService<ApplicationUserManager>();
AppJobSeeker 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);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager, CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName, user.Id);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);...
And I have a method which creates a AuthenticationTicket and it receive the UserName and the UserId as well
private void CreateTicket(SignInResult result, SignInModel model, string returnUrl)
{
//Let's keep the user authenticated in the MVC webapp.
//By using the AccessToken, we can use User.Identity.Name in the MVC controllers to make API calls.
FormsAuthentication.SetAuthCookie(result.AccessToken, model.RememberMe);
//Create an AuthenticationTicket to generate a cookie used to authenticate against Web API.
//But before we can do that, we need a ClaimsIdentity that can be authenticated in Web API.
var claims = new[]
{
new Claim(ClaimTypes.Name, result.UserName), //Name is the default name claim type, and UserName is the one known also in Web API.
new Claim(ClaimTypes.NameIdentifier, result.UserId), //If you want to use User.Identity.GetUserId in Web API, you need a NameIdentifier claim.
};
//Generate a new ClaimsIdentity, using the DefaultAuthenticationTypes.ApplicationCookie authenticationType.
//This also matches what we've set up in Web API.
var authTicket = new AuthenticationTicket(new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie), new AuthenticationProperties
{
ExpiresUtc = result.Expires,
IsPersistent = model.RememberMe,
IssuedUtc = result.Issued,
RedirectUri = returnUrl
});
...
Everithing looks fine when I do the login but when a I go to another controller I can't retreive the UserId
Have you added the [Authorize] attribute to your controller?
[Authorize]
public class AuthorizeController : ApiController
{
public Task<IHttpActionResult> GetUserId()
{
return Ok(HttpContext.Current.User.Identity.GetUserId());
}
}

MVC 3 Authentication / Authorization: Roles missing

We use MVC 3. The default user management is not usable for us as our account info is stored in our own data-store and access goes via our own repository classes.
I'm trying to assign a principal add roles to the HttpContext.User and give out an authorization cookie.
Based on a code snipped I found I tried something like this:
if (UserIsOk(name, password))
{
HttpContext.User =
new GenericPrincipal(
new GenericIdentity(name, "Forms"),
new string[] { "Admin" }
);
FormsAuthentication.SetAuthCookie(name, false);
return Redirect(returnUrl);
}
When the next request is done, the user is authenticated, but he is not in the "Admin" role.
What am I missing?
I think you should implement FormsAuthenticationTicket.
More info here : http://msdn.microsoft.com/en-us/library/aa289844(v=vs.71).aspx
In Mvc it is quite similar.
I have a class called UserSession that is injected into LoginController and that I use in LogOn action :
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Index(LoginInput loginInput, string returnUrl)
{
if (ModelState.IsValid)
{
return (ActionResult)_userSession.LogIn(userToLog, loginInput.RememberMe, CheckForLocalUrl(returnUrl), "~/Home");
}
}
Here's my UserSession LogIn implementation (notice I put the "Admin" role hard coded for the example, but you could pass it as argument) :
public object LogIn(User user, bool isPersistent, string returnUrl, string redirectDefault)
{
var authTicket = new FormsAuthenticationTicket(1, user.Username, DateTime.Now, DateTime.Now.AddYears(1), isPersistent, "Admin", FormsAuthentication.FormsCookiePath);
string hash = FormsAuthentication.Encrypt(authTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash);
if (authTicket.IsPersistent) authCookie.Expires = authTicket.Expiration;
HttpContext.Current.Response.Cookies.Add(authCookie);
if (!String.IsNullOrEmpty(returnUrl))
return new RedirectResult(HttpContext.Current.Server.UrlDecode(returnUrl));
return new RedirectResult(redirectDefault);
}
Then in the base controller I've overriden OnAuthorization method to get the cookie :
if (filterContext.HttpContext.Current.User != null)
{
if (filterContext.HttpContext.Current.User.Identity.IsAuthenticated)
{
if( filterContext.HttpContext.Current.User.Identity is FormsIdentity )
{
FormsIdentity id = filterContext.HttpContext.Current.User.Identity as FormsIdentity;
FormsAuthenticationTicket ticket = id.Ticket;
string roles = ticket.UserData;
filterContext.HttpContext.Current.User = new GenericPrincipal(id, roles);
}
}
}
I hope this helps. Let me know.
You sure, that roles are enabled, and there is such role?
If not, do following:
In Visual Studio:
Project -> ASP.NET Configuration
Then choose Security, enable roles. Create role "Admin".
Then try your approach

Resources