OpenID Connect obtain claims in MVC Session - asp.net-mvc

I need to store a unique identifier for a user in an SQL database. My application uses OpenID Connect via OWIN to authenticate users against Azure AD and is based on the MVC 5 Framework. I can access the ObjectId of the user via the http://schemas.microsoft.com/identity/claims/objectidentifier claim. The only way I know how to obtain the claims is with a delegate added to the "Notifications" property of "OpenIdConnectAuthenticationOptions". As this is part of the application startup the delegate has no way of passing this information on to a session.
I have seen answers to questions with similar goals that recommend using the Graph API to match the User.Identity.Name with the ObjectId. I would rather avoid doing this as my application is resource hungry enough as it is.
Here is what I have so far. I just need to get the claim into a Session, or access the claims from a Session. (This doesn't always work, for example if the user is already logged in, but it's the best I have so far)
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = (context) =>
{
IEnumerable<Claim> claims = context.AuthenticationTicket.Identity.Claims;
Claim objectId = claims.First(claim => claim.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier");
// ???
return Task.FromResult(0);
}
},

I think I've answered my own question. This works nicely.
ClaimsIdentity identity = User.Identity as ClaimsIdentity;
Guid objectId = new Guid(identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value);
Guid tenantId = new Guid(identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value);

Related

How do you set the current user in an ASP.NET MVC 5 application using ASP.NET Identity?

I am having difficulty figuring out how to set the current user in an ASP.NET MVC 5 application that uses ASP.NET Identity.
We use a Web Service that encapsulates authentication. A function calls that service and gets back a JWT token that can be "unwrapped" to retrieve the ClaimsIdentity. It's my understanding that all you should need to do, at that point, is call HttpContext.GetOwinContext().Authentication.SignIn() and pass the ClaimsIdentity to establish the current user and get that user to persist across requests.
However, this doesn't seem to be the case. If I query this.User in the controller or the view afterwards, it's an anonymous user (this.User.Identity.IsAuthenticated evaluates to false). Also, refreshing the page or redirecting makes it obvious that there's no current user (again, this.User.Identity.IsAuthenticated evaluates to false).
When I inspect the identity I've retrieved from AccountHelper, all the data is there. It's fully populated with a name, an ID, and a full suite of claims.
So what is the right way to establish the current user and get it to persist across requests?
// This call authenticates a user with the provided credentials against our user store
// and returns a bearer token and a ClaimsIdentity.
var response = new AccountHelper().AuthenticateUser(credentials);
if (response.IsAuthenticated) // This is true
{
// This comes back with a fully populated identity, as expected.
var claimsIdentity = response.ClaimsIdentity;
// This call has no effect, whatsoever. It doesn't set the current user.
HttpContext.GetOwinContext()
.Authentication
.SignIn(new AuthenticationProperties { IsPersistent = true }, claimsIdentity);
First of all, did you miss .Current in your code sample?
It should be
HttpContext.Current.GetOwinContext()
.Authentication
.SignIn(...);
Secondly, I'm assuming you've setup cookie authentication in your app?
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
...
});
Here setting the authentication type is important! And when you generate the claims identity and before you pass it into the .SignIn() method, the claims identity needs to have the same authentication type so that they can talk!
I would use .CreateIdentityAsync() method from the UserManager class to create the identity instead, because you can pass the authentication type in as one of the parameters:
// Create an application user from your claim identity?
var appUser = new AppUser { ... };
// And pass the user into manager to create the identity with the same authentication
// type you used when you setup the cookie authentication
var claimsIdentity = _userManager.CreateIdentityAsync(appUser,
CookieAuthenticationDefaults.AuthenticationType);
I am using this way to impersonate users for admins that have developer role so that we can test the app.

Windows Authentication use groupsid claims instead on role claims in IsInRole function

I'm building MVC 5 webapp with Windows Authentication.
Since I have external authorization store, so I want to add some custom claims authentication. I handle the PostAuthenticationRequest event
void Application_PostAuthenticateRequest()
{
if (Request.IsAuthenticated)
{
var id = ClaimsPrincipal.Current.Identities.First();
...
//query authorization store
id.AddClaim(new Claim(ClaimTypes.Role, "SomeRole"));
}
}
Then in controller, I check the IsInRole("SomeRole") , but I found it always return false even the identity have the SomeRole role claim
Then I found out that in Windows Authentication, the IsInRole() is using groupsid claims as role
Instead of add my custom claim to groupsid claim, how can I set the IsInRole function use the standard Role claims?
Look like RoleClaimType is the way to go, but it is readonly I cannot set it.
I don't think you can change the behavior of the WindowsPrincipal.IsInRole method.
What you can do is create a new ClaimsPrincipal from a new ClaimsIdentity, using the claims from the WindowsPrincipal. One of the ClaimsIdentity constructors lets you specify the nameType and roleType.
Having set the new ClaimsPrincipal, your IsInRole calls should work on the specified roleType.

asp.net identity with domain controller

I need to achieve to authenticate users with their domain user/password, if they're are in the domain controller, but the application should be available for other users as well, who should be authenticated with their own username/password; this should be stored in the application database, and their username/password to be checked against the DB.
So far i started with new asp.net template in vs2015, choosing Individual User Accounts.
I'm able to authenticate users agains domain controller, but if that is succeeded I'm unable to store the user to HttpContext.User property.
In SignInManager i call PasswordSignIn and return Success or Failure depending on AD check.
public SignInStatus PasswordSignIn(string userName, string password, bool isPersistent, bool shouldLockout) {
if(AuthenticateAD(userName, password)) {
//
// to create identity/principal and assign to HttpContext.User
//
return SignInStatus.Success;
}
else {
return SignInStatus.Failure;
}
}
public bool AuthenticateAD(string username, string password) {
using(var context = new PrincipalContext(ContextType.Domain, "domainname")) {
return context.ValidateCredentials(username, password);
}
}
thanks for any hint!
The only way this really works is if you create proxy users in your application for users in AD. Essentially, you just set up a script that populates new users/updates existing users based on the data in AD on a schedule (nightly, etc. based on your needs). Then, you're dealing with just one type of user whether they're part of the domain or external. The only change you need to make is to selectively authenticate via AD or via the standard password authentication. Either way, the same user principal is in play.
You can use ADFS and allow users to choose where to authenticate. It is quite trivial to implement using default template. Just like usual login mechanics with Sign-in via google and local account.
I think this is most correct way of doing things, because domain users may end up with Kerberos/Ntlm, if they want, and it lowers complexity of your system.
Here is a WS-Fed example: Using Claims in your Web App is Easier with the new OWIN Security Components
For other stuff you can create app with default template. This app will have external authentication stuff as example.

MVC 5 Identity (v2) Authentication Without Creating an Application User

Is there a way to authenticate a session without creating an ApplicationUser in MVC 5 identity?
For various reasons, I ended up using a two layered authentication system. I parse a "user" object from my custom db into session, and in various places all over the site, the existence of this object is how the logged-in status of a user is determined.
I use Identity user stuff (e.g. claims, logins, etc.) at various places of the site. But at this one specific instance, I need to log in an anonymous Identity user and parse whatever user object is requested to the session. So how can I create an anonymously authenticated session with Identity V2?
In Identity you don't need to have user object to authenticate. You could create some claims on the fly and use them to authenticate. Consider this simple example:
[HttpPost]
public ActionResult AnonymousLogin()
{
var ident = new ClaimsIdentity(
new[]
{
// adding following 2 claim just for supporting default antiforgery provider
new Claim(ClaimTypes.NameIdentifier, "AnonymousUserID"),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"),
new Claim(ClaimTypes.Name, "AnonymousUserID"),
},
DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.GetOwinContext().Authentication.SignIn(
new AuthenticationProperties { IsPersistent = false }, ident);
return RedirectToAction("MyAction"); // auth succeed
}
Now you have authenticated an anonymous user just like a real user:
[Authorize]
public ActionResult MyAction()
{
// all authorized users could use this method don't matter how have been authenticated
// you have access current user principal
var username=HttpContext.User.Identity.Name;
}

Extending Azure ACS Claims for MVC Sessions

I am developing an MVC 4 Application to be hosted in Azure and want to use their ACS service for authentication. Once the user is authenticated I will use the resulting claim details to correlate to my local records. Subsequent to that, I would like to extend the claimset to include additional claims that represent local authorizations which my application would use for authorization decisions. I assume I need to replace the Principle but I'm not sure where/when to do this in MVC and want to avoid breaking any of the authentication plumbing which would normally be used throughout the life of the session. Can anyone shed some light on this?
The extensibility point in WIF for enriching the claimset is the ClaimsAuthenticationManager
From the docs:
The claims authentication manager provides an extensibility point in
the RP processing pipeline that you can use to filter, modify, or
inject new claims into the set of claims presented by an
IClaimsPrincipal before the RP application is called.
You can also add rules in ACS to enrich the token with the claims you need.
In addition to what #Eugenio Pace has said, it's worth noting that you can just add and remove claims to and from the IClaimsPrincipal:
public static void UpdateClaims(IClaimsIdentity identity)
{
identity.Claims.Remove(identity.Claims.SingleOrDefault(x => x.ClaimType == ClaimTypes.Name));
identity.Claims.Remove(identity.Claims.SingleOrDefault(x => x.ClaimType == ClaimTypes.Email));
identity.Claims.Add(new Claim(ClaimTypes.Name, "Steve Smith"));
identity.Claims.Add(new Claim(ClaimTypes.Email, "steve#smith.com"));
}
UpdateClaims(User.Identity as IClaimsIdentity);
Claims added can either be one of the types enumerated in ClaimTypes, or a custom string of your own devising. You can add multiple claims of type ClaimTypes.Role - I'm not sure about the other types.
From the ClaimsCollection docs:
Represents a collection of claims associated with a single subject.
Adding a Claim to a ClaimCollection implicitly associates that Claim
with the subject associated with the collection by calling the
SetSubject method.
Removing a Claim from a ClaimCollection implicitly removes this
association by also calling the SetSubject method.
http://msdn.microsoft.com/en-us/library/microsoft.identitymodel.claims.claimcollection.aspx
Update
For .Net 4.5, the identity class and the method for updating claims have changed, as well as the namespace:
using System.IdentityModel;
using System.Security.Claims;
public static void UpdateClaims(Member member, ClaimsIdentity identity)
{
identity.RemoveClaim(identity.Claims.SingleOrDefault(x => x.Type == ClaimTypes.Name));
identity.RemoveClaim(identity.Claims.SingleOrDefault(x => x.Type == ClaimTypes.Email));
identity.AddClaim(new Claim(ClaimTypes.Name, "Steve Smith"));
identity.AddClaim(new Claim(ClaimTypes.Email, "steve#smith.com"));
}
UpdateClaims(User.Identity as ClaimsIdentity);
http://msdn.microsoft.com/en-us/library/system.security.claims.claimsidentity.aspx

Resources