I'm using Microsoft.Owin.Security.Jwt nuget package to secure my webapi. It's setting the HttpContext.Current.User as expected when I'm passing the token to the webapi as a header into my requests.
As the business logic is encapsulated, I could not find the moment that HttpContext.Current.User is being setted.
Instead of searching for the right claim using identity.Claims all the time, I would like to extract the information that I need, and set a custom class as HttpContext.Current.User. Is it possible using Microsoft.Owin.Security.Jwt?
Not quite sure what you mean. The HttpContext.Current.User is set automatically the moment your token is valid (matching the secret and not expired).
What you could do is write some extension methods to retrieve other values. For example:
public static class IdentityHelpers
{
public static string GetUserId(this IIdentity identity)
{
ClaimsIdentity claimsIdentity = identity as ClaimsIdentity;
if (claimsIdentity.Claims.FirstOrDefault(x => x.Type == "UserId") != null) {
return claimsIdentity.Claims.FirstOrDefault(x => x.Type == "UserId").Value;
}
return "0";
}
public static string GetUserName(this IIdentity identity)
{
return identity.Name;
}
}
You will then be able to call for example:
HttpContext.Current.user.GetUserName();
or
HttpContext.Current.User.GetUserId();
wherever you include that class.
Related
I have looking into code of a web application where token is generated and injected into url instead of auth cookie for each request token is passed with url to accessed secured action. The web application is using token instead of auth cookie. Token life is one day.
This is a sample url
http://localhost:48000/ACX/Default/Login?token=8kzRLdW8lQVIS0MrtlqdZJbmz9p22l33u1wspGOmLgCgEy2MG5XZ0JG1ovVZGiNX7KpAfBVn3
of that web application where token is passing through url.
This code is generating the token which would valid up to 24 hours:
public IActionResult Login([FromBody]LoginModel user)
{
if (user == null)
{
return BadRequest("Invalid request");
}
if (user.UserName == "johncitizen" && user.Password == "abc#123")
{
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("KeyForSignInSecret#1234"));
var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
var tokeOptions = new JwtSecurityToken(
issuer: "http://localhost:2000",
audience: "http://localhost:2000",
claims: new List<Claim>(),
expires: DateTime.Now.AddMinutes(1440), // valid till 24 hours
signingCredentials: signinCredentials
);
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
return Ok(new { Token = tokenString });
}
else
{
return Unauthorized();
}
}
My question is: when token is passed through the URL, then any other person can get the URL and impersonate the user. I guess passing token through URL is not secure.
What can we do as a result token would be secure passing through URL? I want to change flow bit in such a way that if another user copy and paste the same URL, then he will not be able to access protected resource. So how to achieve and secure long life token?
Please guide me with approach in details. Thanks
You can try to use IDataProtectionProvider protect your data.
Configure in your program.cs:
builder.Services.AddDataProtection();
Or you can also specify algorithms (using Microsoft.AspNetCore.DataProtection) used for encryption and validation, like this:
builder.Services.AddDataProtection()
.UseCryptographicAlgorithms(new AuthenticatedEncryptionSettings()
{
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_GCM,
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
});
Then inject IDataProtectionProvider into your controller, Create your own key And create Create encryption and decryption methods:
public class HomeController : Controller
{
//Inject IDataProtectionProvider into your controller
private readonly IDataProtectionProvider _dataProtectionProvider;
//create your own key
private const string Key = "my-very-long-key-of-no-exact-size";
public HomeController(ILogger<HomeController> logger, IDataProtectionProvider dataProtectionProvider)
{
_logger = logger;
_dataProtectionProvider = dataProtectionProvider;
}
public IActionResult Login([FromBody]LoginModel user)
{
//...............
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
var keytoken = Encrypt(tokenString);
return Ok(new { Token = keytoken });
//...............
}
//encryption methods
public string Encrypt(string input)
{
var protector = _dataProtectionProvider.CreateProtector(Key);
return protector.Protect(input);
}
//decryption methods
public string Decrypt(string output)
{
var protector = _dataProtectionProvider.CreateProtector(Key);
return protector.Unprotect(output);
}
}
Now, Token has been encrypted.
Even if someone else gets your toke, Because they don't have the key, So they can't decrypt the token.
I'm moving my steps to OpenIDDict and I made my application based on Velusia example.
Everything works fine but I have a question: My access token doesn't include roles.
There's a way to automate the retrieving of .NET Core identity user roles and append them to the User property as Claim before accessing the action in my controller?
The purpose of all is being able to use (for example)
User.IsInRole("MyRole");
Thanks to everyone!
Reading this post gets me in the right direction: Is there a way to dynamically load claims in OpenIddict?
public class MyClaimTransformation : IClaimsTransformation
{
private readonly UserManager<UserInfo> _userManager;
public MyClaimTransformation(UserManager<UserInfo> userManager)
{
_userManager = userManager;
}
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
ClaimsIdentity claimsIdentity = new ClaimsIdentity();
//claimsIdentity.RoleClaimType = OpenIddict.Abstractions.OpenIddictConstants.Claims.Role;
//claimsIdentity.NameClaimType = OpenIddict.Abstractions.OpenIddictConstants.Claims.Name;
var claimType = ClaimTypes.Role;
if (principal.Identity != null && principal.Identity.IsAuthenticated)
{
//Do I already have roles in the claim?
var roleClaimsAvailable = principal.Claims.Any(x => x.Type == claimType);
if (!roleClaimsAvailable)
{
//Roles not found, adding:
var userProfile = await _userManager.GetUserAsync(principal);
if (userProfile != null)
{
var roles = await _userManager.GetRolesAsync(userProfile);
foreach (var role in roles)
{
claimsIdentity.AddClaim(new Claim(claimType, role));
}
principal.AddIdentity(claimsIdentity);
}
}
}
return principal;
}
}
Than we need to register in the Startup.cs as a service:
//Adding roles on access token incoming
builder.Services.AddTransient<IClaimsTransformation, MyClaimTransformation>();
I have an web front end calling an ASP Web Api 2 backend. Authentication is managed with ASP Identity. For some of the controllers I'm creating I need to know the user making the call. I don't want to have to create some weird model to pass in including the user's identity (which I don't even store in the client).
All calls to the API are authorized using a bearer token, my thought is the controller should be able to determine the user context based on this but I do not know how to implement. I have searched but I don't know what I'm searching for exactly and haven't found anything relevant. I'm going for something like...
public async Task<IHttpActionResult> Post(ApplicationIdentity identity, WalkthroughModel data)
Update
I found the below which looked very promising... but the value is always null! My controller inherits from ApiController and has an Authorize header.
var userid = User.Identity.GetUserId();
Update 2
I have also tried all of the solutions in Get the current user, within an ApiController action, without passing the userID as a parameter but none work. No matter what I am getting an Identity that is valid and auth'd, but has a null UserID
Update 3
Here's where I'm at now.
[Authorize]
[Route("Email")]
public async Task<IHttpActionResult> Get()
{
var testa = User.Identity.GetType();
var testb = User.Identity.GetUserId();
var testc = User.Identity.AuthenticationType;
var testd = User.Identity.IsAuthenticated;
return Ok();
}
testa = Name: ClaimsIdentity,
testb = null,
testc = Bearer,
testd = true
The user is obviously authenticated but I am unable to retrieve their userID.
Update 4
I found an answer, but I'm really unhappy with it...
ClaimsIdentity identity = (ClaimsIdentity)User.Identity;
string username = identity.Claims.First().Value;
That gets me the username without any db calls but it seems very janky and a pain to support in the future. Would love if anyone had a better answer.
What if I need to change what claims are issued down the road? Plus any time I actually need the user's id I have to make a db call to convert username to ID
A common approach is to create a base class for your ApiControllers and take advantage of the ApplicationUserManager to retrieve the information you need. With this approach, you can keep the logic for accessing the user's information in one location and reuse it across your controllers.
public class BaseApiController : ApiController
{
private ApplicationUser _member;
public ApplicationUserManager UserManager
{
get { return HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
}
public string UserIdentityId
{
get
{
var user = UserManager.FindByName(User.Identity.Name);
return user.Id;
}
}
public ApplicationUser UserRecord
{
get
{
if (_member != null)
{
return _member ;
}
_member = UserManager.FindByEmail(Thread.CurrentPrincipal.Identity.Name);
return _member ;
}
set { _member = value; }
}
}
I use a custom user authentication (I dont use AspIdentity because my existing user table fields was far different from IdentityUser properties) and create ClaimsIdentity passing my table UserID and UserName to validate my bearer token on API calls.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
User user;
try
{
var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
_service = scope.Resolve<IUserService>();
user = await _service.FindUserAsync(context.UserName);
if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt))
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
catch (Exception ex)
{
context.SetError("invalid_grant", ex.Message);
return;
}
var properties = new Dictionary<string, string>()
{
{ ClaimTypes.NameIdentifier, user.UserID.ToString() },
{ ClaimTypes.Name, context.UserName }
};
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
properties.ToList().ForEach(c => identity.AddClaim(new Claim(c.Key, c.Value)));
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties(properties));
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(identity);
}
And how I use the ClaimsIdentity to retrieve my User table details on User ApiController Details call.
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("Details")]
public async Task<IHttpActionResult> Details()
{
var user = await _service.GetAsync(RequestContext.Principal.Identity.GetUserId<int>());
var basicDetails = Mapper.Map<User, BasicUserModel>(user);
return Ok(basicDetails);
}
Notice the
ClaimTypes.NameIdentifier = GetUserId() and ClaimTypes.Name = GetUserName()
I have a ASP.NET Web API (.NET 4) application which has a few controllers. We will run several instances of the Web API application on IIS with one difference. Only certain controllers will be available under certain IIS instances. What I was thinking is to disable/unload the controllers that are not applicable to an instance when the instance starts up.
Anyone got some information that could guide me in the right direction on this?
You can put your own custom IHttpControllerActivator in by decorating the DefaultHttpControllerActivator. Inside just check for a setting and only create the controller if allowed.
When you return null from the Create method the user will receive 404 Not Found message.
My example shows a value in App Settings (App.Config or Web.Config) being checked but obviously this could any other environment aware condition.
public class YourCustomControllerActivator : IHttpControllerActivator
{
private readonly IHttpControllerActivator _default = new DefaultHttpControllerActivator();
public YourCustomControllerActivator()
{
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
if (ConfigurationManager.AppSettings["MySetting"] == "Off")
{
//Or get clever and look for attributes on the controller in controllerDescriptor.GetCustomAttributes<>();
//Or use the contoller name controllerDescriptor.ControllerName
//This example uses the type
if (controllerType == typeof (MyController) ||
controllerType == typeof (EtcController))
{
return null;
}
}
return _default.Create(request, controllerDescriptor, controllerType);
}
}
You can switch your activator in like so:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new YourCustomControllerActivator());
Update
It has been a while since I looked at this question but if I was to tackle it today I would alter the approach slightly and use a custom IHttpControllerSelector. This is called before the activator and makes for a slightly more efficient place to enable and disable controllers... (although the other approach does work). You should be able to decorate or inherit from DefaultHttpControllerSelector.
Rather than unloading the controllers, I think I'd create a custom Authorize attribute that looked at the instance information in deciding to grant authorization.
You would add the following to each controller at the class level, or you could also add this to individual controller actions:
[ControllerAuthorize (AuthorizedUserSources = new[] { "IISInstance1","IISInstance2","..." })]
Here's the code for the Attribute:
public class ControllerAuthorize : AuthorizeAttribute
{
public ControllerAuthorize()
{
UnauthorizedAccessMessage = "You do not have the required access to view this content.";
}
//Property to allow array instead of single string.
private string[] _authorizedSources;
public string UnauthorizedAccessMessage { get; set; }
public string[] AuthorizedSources
{
get { return _authorizedSources ?? new string[0]; }
set { _authorizedSources = value; }
}
// return true if the IIS instance ID matches any of the AllowedSources.
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
//If no sources are supplied then return true, assuming none means any.
if (!AuthorizedSources.Any())
return true;
return AuthorizedSources.Any(ut => ut == httpContext.ApplicationInstance.Request.ServerVariables["INSTANCE_ID"]);
}
The IHttpControllerActivator implementation doesn't disable the routes defined using attribute routing , if you want to switch on/off a controller and have a default catch all route controller. Switching off using IHttpControllerActivator disables the controller but when the route is requested it doesn't hit the catch all route controller -it simply tries to hit the controller that was removed and returns no controller registered.
I have been asked to create a site where the user isn't required to provide a email to login because of privacy issues. In the past I have simple said this isn't advisable but in this case the client has stringently requested it. My initial thoughts are to potentially create administrators with a email whom could create generic logins (username and a password) and pass them to members of there group on site. Then at least I have a point of contact for login resets and such.
Has anyone had any experience with such situations where they have needed to create logins without the use of a email address? Could you direct me towards any relevant materials or tutorials that may be of use. I'm using MVC3 to develop this project.
I hope I understand your question right and you want to implement a login using username and password instead of email adress and password.
In that case you would have to implement your own custom membership provider and a custom roleprovider if needed.
You want to check the following page for more information:
Custom Membership Provider # Codeproject
EDIT
Fyi you dont need to implement every function - just implement the ones you need.
Custom membership provider from some of my older mvc3 projects. Removed most of the not-implemented functions for shorter code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
namespace Domain.Models
{
public class PlatformMembershipProvider : MembershipProvider
{
public SalesModelContainer ******** = new SalesModelContainer();
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
var user = ********.UserSet.Single(s => s.Email == username);
if (user.Password == oldPassword)
{
user.Password = newPassword;
return true;
}
else
{
return false;
}
}
public override string GetUserNameByEmail(string email)
{
var user = ********.UserSet.Single(s => s.Email == email);
return user.CompanyName;
}
public override void UpdateUser(System.Web.Security.MembershipUser user)
{
throw new NotImplementedException();
}
//TODO: use MD5 for password encryption
public override bool ValidateUser(string username, string password)
{
bool returnValue;
var user = ********.UserSet.SingleOrDefault(s => (s.Email == username) && (s.Password == password));
if (user != null)
returnValue = true;
else
returnValue = false;
return returnValue;
}
}
}