How to manually set value of HttpContext.User.Identity.IsAuthenticated - c#

I am creating an Asp.NET MVC 5 application. For this project, I am trying to implement a custom authentication mechanism (i don't want to use Forms Authentication / OWIN etc. external providers)
I created a custom authorize attribute as follows:
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class myAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!HttpContext.Current.Request.IsAuthenticated)
{
httpContext.Response.Redirect("~/Account/Login");
}
return base.AuthorizeCore(httpContext);
}
}
And in my Login action, I am trying to change the value of
HttpContext.User.Identity.IsAuthenticated
But it is read only and I am unable to change the value. Can I change its value manually or am I making a logical mistake.

You can achieve this by manually settings HttpContext.User:
var identity = new ClaimsIdentity("Custom");
HttpContext.User = new ClaimsPrincipal(identity);
It's important to set a custom authenticationType. In the example above, I just used the string "Custom", but it can be anything you want.
With that, HttpContext.User.Identity.IsAuthenticated will be true.
For something more complicated, you can add claims like this:
var identity = new ClaimsIdentity(new List<Claim>
{
new Claim("UserId", "123", ClaimValueTypes.Integer32)
}, "Custom");
HttpContext.User = new ClaimsPrincipal(identity);
This results in:
HttpContext.User.Identity.IsAuthenticated == true;
int.Parse(((ClaimsIdentity)HttpContext.User.Identity).ValueFromType("UserId")) == 123;

My answer may no suit you perfectly but it may help.
In my ASP.NET Core MVC application, administrators need to impersonate other users.
It is an intranet application and obviously users are authenticated by Windows authentication.
It is done thanks to an ajax request to this controller action :
public async Task<JsonResult> UserImpersonation(IdentityExtension userIdentity)
IdentityExtension is a custom class of which you can observe the signature below :
public class IdentityExtension : IIdentity
{
public IdentityExtension()
{ }
public IdentityExtension(string name)
{
this.Name = name;
}
public string AuthenticationType => "Kerberos";
public bool IsAuthenticated => true;
public string Name { get; set; }
}
UserImpersonation method returns the success state of ReplaceUser method, wich updates HttpContext.User this way :
this.HttpContext.User = identity as ClaimsPrincipal;
return true;
identity being an instance of IdentityExtension.
I hope my solution can be adapted to your use case !

We can see, from the source code:
/// <summary>
/// Gets a value that indicates if the user has been authenticated.
/// </summary>
public virtual bool IsAuthenticated
{
get { return !string.IsNullOrEmpty(_authenticationType); }
}
so, it means when we have a null _authenticationType, IsAuthenticated will always be false;
otherwise, it will be true.

If you want the HttpContext to persist and be able to fetch claims afterward, do the following. I'll be using Cookie Authentication as the authenticationType.
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
identity.AddClaim(new Claim(ClaimTypes.Name, TempData["Username"].ToString()));
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties { IsPersistent = true });

you can't use FormsAuthentication.SetAuthCookie?
https://learn.microsoft.com/en-us/dotnet/api/system.web.security.formsauthentication.setauthcookie?view=netframework-4.7.2

Related

Allow "semi anonymous" authentication

I have a case where I need some controller methods to be accessible either by an authenticated user, or if the request contains a sort of "acccess token" in the url.
For example:
Either an authenticated user could make a call to:
https://example.com/some/resource
Or a non authenticated user could make the same call, but add some kind of token to the url (or as a header):
https://example.com/some/resource?token=123abc
The token does not have to be super secret, only something hard to guess.
[AllowSpecialToken]
[HttpGet]
[Route("some/resource")]
public async Task<string> GetSomeResource()
{
return "some resource";
}
What I'm struggling with is how to write the AllowSpecialTokenAttribute. And how to get that to run before the authentication (using OpenIddict) we have in place now.
Is this a stupid use case? Should I find another solution?
To give some context: We have a SPA that calls our API. Some pages of the SPA can be shared with others (non user) just by sending a link. That link will contain the token. The content of those pages are not critical security wise, but they shouldn't be completely open.
You need to make your own authentication attribute. I've done something like that in the past, here is my stub at it:
public class TokenAuthenticationAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
// this will read `token` parameter from your URL
ValueProviderResult valueProvided = filterContext.Controller.ValueProvider.GetValue("token");
if (valueProvided == null)
{
filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
return;
}
var providedToken = valueProvided.AttemptedValue;
var storedToken = "12345"; // <-- get your token value from DB or something
if (storedToken != providedToken)
{
filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
return;
}
}
}
Then decorate your action with the attribute:
[TokenAuthentication]
[HttpGet]
[Route("some/resource")]
public async Task<string> GetSomeResource()
{
return "some resource";
}
And get your URI looking like https:\\www.example.com\api\some\resource?token=12345
You could try the below and see if it works for you.
Caveat: I have absolutely no idea if this is the "correct" way to do this. I just know it is a way that appears to work. Please test and downvote if you find problems. I still have an open question on another authentication handler I have written, but with no replies, so use with caution. It may be worth contacting blowdart (search users) at MS if you are going to pursue this use case.
Middleware Class
public class TokenCodeAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public const string DefaultSchemeName = "TokenAuthScheme";
public TokenCodeAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
AuthenticateResult result = await this.Context.AuthenticateAsync();
if (result.Succeeded)
{
//User has supplied details
return AuthenticateResult.Success(result.Ticket);
}
else if (Context.Request.Query["token"] == "123abc") //TODO: Change hard-coded token
{
//User has supplied token
string username = "Test"; //Get/set username here
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, username, ClaimValueTypes.String, Options.ClaimsIssuer),
new Claim(ClaimTypes.Name, username, ClaimValueTypes.String, Options.ClaimsIssuer)
};
ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(claims, Scheme.Name));
AuthenticationTicket ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
return AuthenticateResult.Fail("Unauthorized");
}
}
Configure Services in Startup
services.AddAuthentication()
.AddScheme<AuthenticationSchemeOptions, TokenCodeAuthHandler>(
TokenCodeAuthHandler.DefaultSchemeName,
(o) => { });
Attribute Usage
Use on controller actions as follows:
Note - I couldn't seem to override controller level authorize attributes).
[Authorize(AuthenticationSchemes = TokenCodeAuthHandler.DefaultSchemeName)]
[HttpGet]
[Route("some/resource")]
public async Task<string> GetSomeResource()
{
return "some resource";
}

.NET Web Api 2 UserPrincipal.IsAuthenticated always false when using dependency injection (Unity)

I have a custom authorization attribute which verifies token inside header and sets user-principal.
//when token is validated
var userIdentityBase = new UserIdentityBase(token); <-- Inherited GenericIdentity
IPrincipal principal = new GenericPrincipal(userIdentityBase, null);
actionContext.Request.GetRequestContext().Principal = principal;
HttpContext.Current.User = principal;
Thread.CurrentPrincipal = principal;
This works fine when I check for Identity inside controller
But when i inject IPrincipal to my service classes, it does not work anymore. IsAuthenticated is false.
Unity setup code:
container.RegisterType<IPrincipal>(new InjectionFactory(u => HttpContext.Current.User));
When injected, it does not work (both screenshots are taken withing same request):
Any suggestions?
I've solved this by creating own interface and implementation, which will extract user identity at runtime. Works like a charm for my case
Interface
public interface IIdentityProvider
{
IPrincipal GetPrincipal();
}
Implementation:
public class IdentityProvider : IIdentityProvider
{
public IPrincipal GetPrincipal()
{
return HttpContext.Current.User;
}
}
unity registration:
container.RegisterType<IIdentityProvider, IdentityProvider>();
And usage:
IIdentityProvider _identityProvider;
public BookingRecordService(IIdentityProvider identityProvider)
{
_identityProvider = identityProvider;
var isAuth = _identityProvider.GetPrincipal().Identity.IsAuthenticated;
}
RequestContext.Principal.Identity.IsAuthenticated

Unable to access custom property in a custom principal

I'm using windows authentication and custom roles. I've extended the WindowsPrincipal because I want to include additional information about the user based on a User class I've added. When I run the application, it sees the CustomPrincipal assigned to the built-in User, but not the additional "user" property I've added. I'm sure I'm doing something really dumb, but this is my first run into the C# ASP world and could really appreciate some help. Here is my custom principal and global.asax
Custom principal:
public class CustomPrincipal : WindowsPrincipal
{
List<string> _roles;
private User thisUser = new User();
public CustomPrincipal(WindowsIdentity identity)
: base(identity)
{
_roles = new List<string>(Roles.GetRolesForUser());
user = thisUser.getDarUser(identity.Name);
}
public User user { get; private set; }
public override bool IsInRole(string role)
{
if (base.IsInRole(role) || _roles.Contains(role) || _roles.Contains("Admin") || _roles.Contains("Dev"))
{
return true;
}
else
{
return false;
}
}
}
And Global.asax:
protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
if (User.Identity.IsAuthenticated)
{
IIdentity thisId = User.Identity;
WindowsIdentity wi = (WindowsIdentity)thisId;
CustomPrincipal cp = new CustomPrincipal(wi);
HttpContext.Current.User = cp;
Thread.CurrentPrincipal = cp;
}
}
Thanks again for any direction.
Looking into it a little more I found the fail was in how I was trying to access the principal in my views. Thanks to ASP.NET MVC - Set custom IIdentity or IPrincipal, while this was not related to my Windows Auth type project, it did lead me to the correct usage of my principal.
What I was doing WRONG:
#User.user.myproperty
Change to:
#((User as CustomPrinicpal).user.myproperty
Hopefully this helps another newb not make the same bonehead mistake

DB-First authentication confusion with ASP.NET Web API 2 + EF6

I need to create a Web API C# application for an existing MySQL database. I've managed to use Entity Framework 6 to bind every database table to a RESTful API (that allows CRUD operations).
I want to implement a login/registration system (so that I can implement roles and permissions in the future, and restrict certain API requests).
The MySQL database I have to use has a table for users (called user) that has the following self-explanatory columns:
id
email
username
password_hash
It seems that the de-facto standard for authentication is ASP.Net Identity. I have spent the last hour trying to figure out how to make Identity work with an existing DB-First Entity Framework setup.
If I try to construct ApplicationUser instances storing user instances (entities from the MySQL database) to retrieve user data, I get the following error:
The entity type ApplicationUser is not part of the model for the current context.
I assume I need to store Identity data in my MySQL database, but couldn't find any resource on how to do that. I've tried completely removing the ApplicationUser class and making my user entity class derive from IdentityUser, but calling UserManager.CreateAsync resulted in LINQ to Entities conversion errors.
How do I setup authentication in a Web API 2 application, having an existing user entity?
You say:
I want to implement a login/registration system (so that I can
implement roles and permissions in the future, and restrict certain
API requests).
How do I setup authentication in a Web API 2 application, having an
existing user entity?
It definitely means that you DO NOT need ASP.NET Identity. ASP.NET Identity is a technology to handle all users stuffs. It actually does not "make" the authentication mechanism. ASP.NET Identity uses OWIN Authentication mechanism, which is another thing.
What you are looking for is not "how to use ASP.NET Identity with my existing Users table", but "How to configure OWIN Authentication using my existing Users table"
To use OWIN Auth follow these steps:
Install the packages:
Owin
Microsoft.AspNet.Cors
Microsoft.AspNet.WebApi.Client
Microsoft.AspNet.WebApi.Core
Microsoft.AspNet.WebApi.Owin
Microsoft.AspNet.WebApi.WebHost
Microsoft.Owin
Microsoft.Owin.Cors
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Security
Microsoft.Owin.Security.OAuth
Create Startup.cs file inside the root folder (example):
make sure that [assembly: OwinStartup] is correctly configured
[assembly: OwinStartup(typeof(YourProject.Startup))]
namespace YourProject
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
//other configurations
ConfigureOAuth(app);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/security/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
Provider = new AuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
try
{
//retrieve your user from database. ex:
var user = await userService.Authenticate(context.UserName, context.Password);
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));
//roles example
var rolesTechnicalNamesUser = new List<string>();
if (user.Roles != null)
{
rolesTechnicalNamesUser = user.Roles.Select(x => x.TechnicalName).ToList();
foreach (var role in user.Roles)
identity.AddClaim(new Claim(ClaimTypes.Role, role.TechnicalName));
}
var principal = new GenericPrincipal(identity, rolesTechnicalNamesUser.ToArray());
Thread.CurrentPrincipal = principal;
context.Validated(identity);
}
catch (Exception ex)
{
context.SetError("invalid_grant", "message");
}
}
}
}
Use the [Authorize] attribute to authorize the actions.
Call api/security/token with GrantType, UserName, and Password to get the bearer token. Like this:
"grant_type=password&username=" + username + "&password=" password;
Send the token within the HttpHeader Authorization as Bearer "YOURTOKENHERE". Like this:
headers: { 'Authorization': 'Bearer ' + token }
Hope it helps!
Since your DB schema are not compatible with default UserStore You must implement your own UserStore and UserPasswordStore classes then inject them to UserManager. Consider this simple example:
First write your custom user class and implement IUser interface:
class User:IUser<int>
{
public int ID {get;set;}
public string Username{get;set;}
public string Password_hash {get;set;}
// some other properties
}
Now author your custom UserStore and IUserPasswordStore class like this:
public class MyUserStore : IUserStore<User>, IUserPasswordStore<User>
{
private readonly MyDbContext _context;
public MyUserStore(MyDbContext context)
{
_context=context;
}
public Task CreateAsync(AppUser user)
{
// implement your desired logic such as
// _context.Users.Add(user);
}
public Task DeleteAsync(AppUser user)
{
// implement your desired logic
}
public Task<AppUser> FindByIdAsync(string userId)
{
// implement your desired logic
}
public Task<AppUser> FindByNameAsync(string userName)
{
// implement your desired logic
}
public Task UpdateAsync(AppUser user)
{
// implement your desired logic
}
public void Dispose()
{
// implement your desired logic
}
// Following 3 methods are needed for IUserPasswordStore
public Task<string> GetPasswordHashAsync(AppUser user)
{
// something like this:
return Task.FromResult(user.Password_hash);
}
public Task<bool> HasPasswordAsync(AppUser user)
{
return Task.FromResult(user.Password_hash != null);
}
public Task SetPasswordHashAsync(AppUser user, string passwordHash)
{
user.Password_hash = passwordHash;
return Task.FromResult(0);
}
}
Now you have very own user store simply inject it to the user manager:
public class ApplicationUserManager: UserManager<User, int>
{
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new MyUserStore(context.Get<MyDbContext>()));
// rest of code
}
}
Also please note you must directly inherit your DB Context class from DbContext not IdentityDbContext since you have implemented own user store.

OWIN WebAPI 2 Bearer authentication custom identity

I want to allow two types of authentication on my site :
* Forms authentication: The user login using his/her details in the form. The authentication should be made using cookies.
* Bearer: When calling WebAPI's (for mobile), the authentication should be made only by using bearer tokens.
I've relayed on the SPA template and some questions in SO and did successful made it available.
The only problem I'm facing is the ClaimsIdentity: I wish to use custom identity class. However, I'm being able to do so only in forms authentication, not in bearer WebAPI requests.
My custom identity:
public class MyIdentity : ClaimsIdentity, IMyIdentity
{
#region IMyIdentity
private Account _account = null;
public Account Account
{
get
{
if (_account == null)
{
if (this.IsAuthenticated)
{
Guid claimedAccountId = Guid.Parse(this.FindFirst(ClaimTypes.NameIdentifier).Value);
var accountService = ServiceLocator.SharedInstance.GetInstance<IAccountService>();
_account = accountService.Where(
a => a.Id == claimedAccountId
).FirstOrDefault();
}
_account = _account ?? Membership.Account.GuestAccount;
}
return _account;
}
}
#endregion
}
In Global.asax, I've overridden the Application_OnPostAuthenticateRequest method in order to set the custom identity, and it does working good - but only in forms, not in WebAPI.
In addition, I do set in WebApiConfig.cs
config.SuppressDefaultHostAuthentication();
so it does make sense that MyIdentity being nulled and User.Identity resets back to ClaimsIdentity.
So to sum up my question - is there a way to define which Identity class will be used, so I can set MyIdentity instead of ClaimsIdentity?
For Web API, you could try hooking into the OWIN authentication pipeline, and implement your own Authentication Filter, and use it to change the current principal to your own:
public class MyAuthenticationFilter : ActionFilterAttribute, IAuthenticationFilter
{
public Task AuthenticateAsync(HttpAuthenticationContext context, System.Threading.CancellationToken cancellationToken)
{
if (context.Principal != null && context.Principal.Identity.IsAuthenticated)
{
CustomPrincipal myPrincipal = new CustomPrincipal();
// Do work to setup custom principal
context.Principal = myPrincipal;
}
return Task.FromResult(0);
}
And register the filter:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new MyAuthenticationFilter());
...

Categories