I'm trying to write a custom authentication manager that will handle user login and logout and many other stuff in my ASP .Net Core 2.x app, but i'm stuck in the first place.
i have tried the way suggested in this Microsoft Article but when i try to implement Sign-in, it shows the HttpContext does not contain a definition for SignOutAsync error. i have all the references as suggested in the article :
public async void SignIn(HttpContext httpContext, UserDbModel user, bool isPersistent = false)
{
ClaimsIdentity identity = new ClaimsIdentity(this.GetUserClaims(user), CookieAuthenticationDefaults.AuthenticationScheme);
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme);
}
below code works but it is obsolete :
await HttpContext.Authentication.SignOutAsync(...)
references in class :
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Authentication;
What's missing here ? maybe these extension-methods are removed in new versions, if so ... how do i implement the authentication in it's correct way ?
HttpContext should be a reference to a field in your Controller and that you are not referring to a/the type HttpContext. If that is not the case then that is the cause of your problem, change your code to use the field/variable and not the type.
So if the field name is httpContext then use that as calling an extension methods is done by referring to the method on an instance and not a type as the instance is also passed in as the first parameter of the extension method.
await httpContext.SignInAsync(IdentityConstants.ApplicationScheme);
AuthenticationHttpContextExtensions.SignOutAsync(HttpContext, "Cookies");
AuthenticationHttpContextExtensions
.SignOutAsync(HttpContext, CookieAuthenticationDefaults.AuthenticationScheme);
try this in a controller
insted of
await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme);
should use
await httpContext.SignInAsync(IdentityConstants.ApplicationScheme,ClaimsPrincipal.Current);
or could use
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.SignInAsync(IdentityConstants.ApplicationScheme,ClaimsPrincipal.Current);
Related
I'm trying to hook up azure functions with EasyAuth as it is part of the app service platform. I have configured well and its working fine out-of-the-box when authenticating with azure active directory.
But since I want to add authorization as well, I decided to do this with app_roles using this guide.
Still I cannot use this in my code. Here's my only function in the function app:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Security.Claims;
namespace delme_azf
{
public static class meh
{
[FunctionName("meh")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log, ClaimsPrincipal claims)
{
string name = req.Query["name"];
req.Headers.TryGetValue("X-MS-TOKEN-AAD-ID-TOKEN", out var jwt);
if (claims.IsInRole("shizzl"))
{
return new OkObjectResult($"shiiiiiiiiiiiiiiiiiiiit {name} {jwt}");
}
else
{
return new OkObjectResult($"moepppppppp === {jwt} ===");
}
}
}
}
As you can see, I injected the ClaimsPrincipal, as is advertised here. And according to this I should be able to check for the existence of a role with claims.IsInRole("shizzl") but this ALWAYS return false (shizzl is the name of my role). But as you can also see, I am returning the jwt in the response as well, if I put this in https://jwt.ms the role is there!
Any guess as to why it's not working via the ClaimsPrincipal approach? Any help is really appreciated.
you are correct that "roles" it is in the claims, however.
according to the documentation, isinrole does not work as you expect. it looks for role types as per the claimidentity
Each ClaimsIdentity has its own definition of the claim type that
represents a role. This claim type can be accessed and set through the
ClaimsIdentity.RoleClaimType property.
by default the roleclaimtype is something like "ClaimsType.Role" where as the claims type in the jwt is "roles" it doesn't match so it doesn't consider it a role claim when you call isinrole
you can try to set the claimsidentity roleclaimtype or you can search for the claim using a different method like claims.hasclaim("roles","shizzl") . that will work too?
Hopefully this helps you.
I am new to asp.net identity (ver. 2) and am about to start implementing it one of our MVC projects using the Claims based authorization mechanism instead of role based. I having been going through this link where what I understand from what is written is that I need to inherit Microsoft.AspNet.Identity.UserManager and create a CustomUserManager class and override its methods and then implement Microsoft.AspNet.Identity.EntityFramework.IUserStore to be consumed by my CustomUserManager class at the very least. There are other interfaces that I think were designed to be implemented for certain specific conditions like in my case the IUserClaimStore since I want to go with Claims based authorization. The reason mentioned is that I can change the store at a later date incase I want to change my persistence mechanism.
My questions are:
Since I am never going to change the persistence mechanism, is it really required that I implement all those classes and interfaces?
Going through the sample code the most important methods seem to be the following two code blocks:
Identity Creation and save to session (code in DoLogin Method)
// over simplified user object creation
UserPoco userObject= MyDAL.GetUserDatabyLoginDetails(username,password);
//identity created
var identity = CustomImplementationOfCreateIdentity(userObject, DefaultAuthenticationTypes.ApplicationCookie);
//saved to session
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
CheckAccess
public class AppClaimsAuthManager: ClaimsAuthorizationManager
{
public override bool CheckAccess(AuthorizationContext context)
{
var resource = context.Resource.First().Value;
var action = context.Action.First().Value;
//bool retVal = context.Principal.HasClaim("MyAction", "SampleResource");
bool retVal = context.Principal.HasClaim(action, resource);
bool baseRetVal= base.CheckAccess(context);
return retVal;
}
}
which is then used in controller methods like so
[ClaimsPrincipalPermission(SecurityAction.Demand, Operation = "delete", Resource = "SomeResource")]
public ActionResult ClaimsBasedActionMethod()
{
return View();
}
Does it really matter how my user object is created via the CustomManager and CustomUserstore class implementations? Once the user name and password is verified and claims fetched from DB and my userObject created, I should be good to go right? I want this data to be fetched my service layer using enterprise library which I don't want to clog up with all identity framework related references.
Thoughts?
I am building a REST API with ASP.NET WebAPI. Everything worked fine, but then I came up with the great idea to use interfaces in all my method calls. After I have changed all the methods i noticed that after setting the parameter in my Controller methods as interfaces, my API calls does not work. I am using OWIN Self host and Unity dependency injection. Here is my relevant code:
Resolving my Interface:
IUnityContainer container = new UnityContainer();
container.RegisterType<IMyInterface, MyInterfaceImpl>(new HierarchicalLifetimeManager());
HttpConfiguration config = new HttpConfiguration();
config.DependencyResolver = new UnityDependencyResolver(container);
My Controller (the part where i get the error)
[Route("test")]
[HttpGet]
public HttpResponseMessage GetSomeData([FromUri]IMyInterface searchObject)
{
return this._searchService.SearchForData(searchObject);
}
When calling this method i get the error that an interface cannot be created. I unterstand that, but the problem is fixing it. I looked at ASP.NET Web API Operation with interfaces instead concrete class and also at https://brettedotnet.wordpress.com/2014/07/16/web-api-and-interface-parameters/ and at ASP.NET Web API Operation with interfaces instead concrete class, but none of the suggestions worked in my case (always getting the error that an interface cannot be created).
I was wondering if someone has a working example on something like this(on github or elsewhere) just to check what I am doing wrong (or even an idea what else I could try would be nice)
Thank you
Because you are passing data from the querystring a different approach is required here. In my blog post that you referenced I did not include that scenario. Since querystrings are handled via Model Binders you need to create a custom model binder.
In my situation I opted for creating a IoCModelBinder as seen below.
public class IocModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
var targetObject = ServiceLocator.Current.GetInstance(bindingContext.ModelType);
var valueProvider = GlobalConfiguration.Configuration.Services.GetValueProviderFactories().First(item => item is QueryStringValueProviderFactory).GetValueProvider(actionContext);
foreach (var property in targetObject.GetType().GetProperties())
{
var valueAsString = valueProvider.GetValue(property.Name);
var value = valueAsString == null ? null : valueAsString.ConvertTo(property.PropertyType);
if (value == null)
continue;
property.SetValue(targetObject, value, null);
}
bindingContext.Model = targetObject;
return true;
}
}
And in use
/// <summary>
/// Searches by the criteria specified.
/// </summary>
/// <param name="searchCriteriaDto">The search criteria dto.</param>
/// <returns></returns>
[HttpGet]
public HttpResponseMessage Search([ModelBinder(typeof(IocModelBinder))]IApplicationSearchCriteriaDto searchCriteriaDto)
{
}
Hope this helps.
Brette
Perhaps this helps:
Parameter Binding in ASP.NET Web API
How to bind to custom objects in action signatures in MVC/WebAPI
You can't use an Formatter because you data are coming from the URI. I think you can go with the Modelbinder approch from the links.
Edit: This question is outdated
The Identity Framework was a moving target at the moment I asked this. The authors changed quite a few things and they have decoupled several others, making everything easier.
Have a look at the Asp.NET Identity Sample project on github.
I'm creating a small application that requires user management. Registration is not allowed, instead there is a super user that will create and modify login information.
I'm using the new ASP.NET Identity membership system, and sure enough, creating users and adding roles is easy and intuitive.
Now, my question: How to obtain a list of users using the AuthenticationIdentityManager class that is used by the generated AccountController class? I couldn't find a way to access the user list from my controller.
(By the way, the new name "Identity" may sound awesome to some people but it is a pain to search for.)
Edit: If I try to do this
ApplicationDbContext UsersContext = new ApplicationDbContext();
UsersContext.Users.ToList(); // Exception
I get an exception Invalid column name 'Discriminator'. The definition of ApplicationDbContext is generated automatically by the new application wizard:
using Microsoft.AspNet.Identity.EntityFramework;
namespace Cobranzas.Models
{
public class ApplicationUser : User
{
}
public class ApplicationDbContext : IdentityDbContextWithCustomUser<ApplicationUser>
{
}
}
So my guess is that Discriminator column is for telling apart ApplicationUser from User. However, it does not exists in my database (which was created automatically by the application.)
I found out that I wasn't using the derived ApplicationUser object for anything, so I just went ahead and changed all uses of it for plain old User. Then I just changed ApplicationDbContext definition for the following:
public class ApplicationDbContext : IdentityDbContext<
User, UserClaim, UserSecret, UserLogin,
Role, UserRole, Token, UserManagement>
{
}
And now I can access the user list:
UsersContext = new ApplicationDbContext();
...
UsersContext.Users.ToList();
However, I think this will come back and haunt me in the future (I'll probably need to add more fields to User) so probably I'll have to use the same approach as in this question:
Get all role names in ASP.NET MVC5 Identity system
Edit: Since I got the need to add a new property, I had to revert my changes. So I went ahead and did a line by line comparison with the ASP.NET Identity Sample Project, and found out that the generated project had the following line:
IdentityManager = new AuthenticationIdentityManager(new IdentityStore());
while the Sample application had included the database context in the constructor. So I added it in my constructor, recreated the database and the problem went away.
IdentityManager = new AuthenticationIdentityManager(new IdentityStore(new ApplicationDbContext()));
Create ASP .NET MVC5 project by default
Create ASP .NET Identity tables properly and change connection string as well.
To get users just do the following test
A. Go to AccountController
B. Create any dummy method and put there
var context = new ApplicationDbContext();
var allUsers = context.Users.ToList();
For RTM, you will have to drop down to your DbContext or whatever your specific store implementation has to enumerate all users. In the next release, we will most likely be adding an optional IQueryable Users/Roles method on the Manager classes that stores can implement to expose IQueryables for both users and stores.
using System.Linq;
using System.Data;
using System.Data.Entity;
var db = new ApplicationDbContext();
var Users = db.Users.Include(u => u.Roles);
If we can use the following type of Constructor in Identity AccountController.
public AccountController(ApplicationUserManager userManager,
ISecureDataFormat<AuthenticationTicket> accessTokenFormat)
{
UserManager = userManager;
AccessTokenFormat = accessTokenFormat;
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
then we can directly used UserManager object to get user list like
var userList= UserManager.Users.ToList();
You can do this by explicitly setting right types:
var userStore = new UserStore<IdentityUser>();
var userManager = new UserManager<IdentityUser>(userStore);
IQueryable<IdentityUser> usersQuery = userManager.Users;
List<IdentityUser> users = usersQuery.ToList();
Imoports:
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Linq;
using System.Collections.Generic;
For basic authentication I have implemented a custom HttpMessageHandler based on the example shown in Darin Dimitrov's answer here: https://stackoverflow.com/a/11536349/270591
The code creates an instance principal of type GenericPrincipal with user name and roles and then sets this principal to the current principal of the thread:
Thread.CurrentPrincipal = principal;
Later in a ApiController method the principal can be read by accessing the controllers User property:
public class ValuesController : ApiController
{
public void Post(TestModel model)
{
var user = User; // this should be the principal set in the handler
//...
}
}
This seemed to work fine until I recently added a custom MediaTypeFormatter that uses the Task library like so:
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream,
HttpContent content, IFormatterLogger formatterLogger)
{
var task = Task.Factory.StartNew(() =>
{
// some formatting happens and finally a TestModel is returned,
// simulated here by just an empty model
return (object)new TestModel();
});
return task;
}
(I have this approach to start a task with Task.Factory.StartNew in ReadFromStreamAsync from some sample code. Is it wrong and maybe the only reason for the problem?)
Now, "sometimes" - and for me it appears to be random - the User principal in the controller method isn't the principal anymore I've set in the MessageHandler, i.e. user name, Authenticated flag and roles are all lost. The reason seems to be that the custom MediaTypeFormatter causes a change of the thread between MessageHandler and controller method. I've confirmed this by comparing the values of Thread.CurrentThread.ManagedThreadId in the MessageHandler and in the controller method. "Sometimes" they are different and then the principal is "lost".
I've looked now for an alternative to setting Thread.CurrentPrincipal to somehow transfer the principal safely from the custom MessageHandler to the controller method and in this blog post request properties are used:
request.Properties.Add(HttpPropertyKeys.UserPrincipalKey,
new GenericPrincipal(identity, new string[0]));
I wanted to test that but it seems that the HttpPropertyKeys class (which is in namespace System.Web.Http.Hosting) doesn't have a UserPrincipalKey property anymore in the recent WebApi versions (release candidate and final release from last week as well).
My question is: How can I change the last code snippet above so that is works with the current WebAPI version? Or generally: How can I set the user principal in a custom MessageHandler and access it reliably in a controller method?
Edit
It is mentioned here that "HttpPropertyKeys.UserPrincipalKey ... resolves to “MS_UserPrincipal”", so I tried to use:
request.Properties.Add("MS_UserPrincipal",
new GenericPrincipal(identity, new string[0]));
But it doesn't work as I expected: The ApiController.User property does not contain the principal added to the Properties collection above.
The problem of losing the principal on a new thread is mentioned here:
http://leastprivilege.com/2012/06/25/important-setting-the-client-principal-in-asp-net-web-api/
Important: Setting the Client Principal in ASP.NET Web API
Due to some unfortunate mechanisms buried deep in ASP.NET, setting
Thread.CurrentPrincipal in Web API web hosting is not enough.
When hosting in ASP.NET, Thread.CurrentPrincipal might get overridden
with HttpContext.Current.User when creating new threads. This means
you have to set the principal on both the thread and the HTTP context.
And here: http://aspnetwebstack.codeplex.com/workitem/264
Today, you will need to set both of the following for user principal
if you use a custom message handler to perform authentication in the
web hosted scenario.
IPrincipal principal = new GenericPrincipal(
new GenericIdentity("myuser"), new string[] { "myrole" });
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
I have added the last line HttpContext.Current.User = principal (needs using System.Web;) to the message handler and the User property in the ApiController does always have the correct principal now, even if the thread has changed due to the task in the MediaTypeFormatter.
Edit
Just to emphasize it: Setting the current user's principal of the HttpContext is only necessary when the WebApi is hosted in ASP.NET/IIS. For self-hosting it is not necessary (and not possible because HttpContext is an ASP.NET construct and doesn't exist when self hosted).
To avoid the context switch try using a TaskCompletionSource<object> instead of manually starting another task in your custom MediaTypeFormatter:
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
var tcs = new TaskCompletionSource<object>();
// some formatting happens and finally a TestModel is returned,
// simulated here by just an empty model
var testModel = new TestModel();
tcs.SetResult(testModel);
return tcs.Task;
}
Using your custom MessageHandler you could add the MS_UserPrincipal property by calling the HttpRequestMessageExtensionMethods.SetUserPrincipal extension method defined in System.ServiceModel.Channels:
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var user = new GenericPrincipal(new GenericIdentity("UserID"), null);
request.SetUserPrincipal(user);
return base.SendAsync(request, cancellationToken);
}
Note that this only adds this property to the Request's Properties collection, it doesn't change the User attached to the ApiController.