ClaimsIdentity not working properly in WebApi - c#

I want to authorize user in WebApi using ClaimsIdentity. In my AccountController which inherits ApiController class I have my two methods to test user authentication. One is a proper method used to receive user's data from other app based on his AD name and authenticates him saving his data as a Claim. The other one is a test method which I call after the previous one to check if the user is authenticated and has claims set.
Unfortunately the login method doesn't seem to set his Identity correctly even though the cookie is generated. The second method than works as if the user wasn't even authenticated and doesn't have any claims.
I have tried some various combination of creating his Identity but nothing seems to work.
Maybe you can see what I am missing.
AccountController.cs
[HttpGet]
[Route("account/login/{userActDirName}/{realmId}")]
public async Task<IHttpActionResult> Login(string userActDirName, long realmId)
{
//getting user data
var user = await UserManager.FindAsync(userActDirName, "1");
if (user == null)
{
user = new ApplicationUser() { UserName = userActDirName };
IdentityResult result = await UserManager.CreateAsync(user, "1");
if (!result.Succeeded)
{
...
}
user = await UserManager.FindAsync(userActDirName, "1");
}
Authentication.SignOut();
ClaimsIdentity cookieIdentity = UserManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
cookieIdentity.AddClaim(new Claim(ClaimTypes.Name, userActDirName));
cookieIdentity.AddClaim(new Claim("User", JsonConvert.SerializeObject(userData)));
Authentication.SignIn(new AuthenticationProperties() { IsPersistent = false }, cookieIdentity);
}
private ApplicationUserManager _userManager;
private IAuthenticationManager Authentication
{
get { return HttpContext.Current.GetOwinContext().Authentication; }
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
IdentityConfig.cs
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = -1,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false,
};
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
Startup.cs
[assembly: OwinStartup(typeof(Api.Startup))]
namespace Api
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Name;
// 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()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
}

Since you used the string "User" while creating claim for complete user object as JSON, using the following code :
cookieIdentity.AddClaim(new Claim("User", JsonConvert.SerializeObject(userData)));
Therefore when checking if the user is authenticated or not, use the follwoing code to check if the above mentioned claim exists or not. It will also give you full JSON that you stored while adding "User" Claim.
Remember the type casting below is very important
also use the following namespace
using System.Security.Claims;
before using the following code
var user = "";
var claims =
((ClaimsIdentity)filterContext.RequestContext.Principal.Identity).Claims;
foreach (var c in claims)
{
if (c.Type == "User")
user = c.Value;
}
I have used this code in a custom "AuthorizationFilterAttribute". Therefore I have
filterContext object
you can get
RequestContext object
easily in any WebAPI-Method e.g.
this.RequestContext.Principal.Identity
therefore,
var claims =
((ClaimsIdentity)this.RequestContext.Principal.Identity).Claims;
will work in any web api controller.

Related

ASP.NET MVC - Refresh Auth Cookie on Anonymous Ajax Requests

I'm trying to refresh a session after an ajax request to a controller that has the [AllowAnonymous] attribute. For some reasons removing this attribute is not a possibility right now. The authentication is being made via OWIN (Microsoft.Owin v4.1.0).
Here is how the authentication is made:
public class Startup_Auth
{
public void Configuration(IAppBuilder app)
{
try
{
MyAuthenticationProvider provider = new MyAuthenticationProvider() { OnValidateIdentity = MyValidation };
app.SetDefaultSignInAsAuthenticationType("ExternalCookie");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ExternalCookie",
AuthenticationMode = AuthenticationMode.Active,
CookieName = "MyCookie",
CookieSecure = CookieSecureOption.Always,
LoginPath = new PathString(PATH),
ExpireTimeSpan = TimeSpan.FromMinutes(EXPIRATION),
Provider = provider,
TicketDataFormat = new MyTicketDataFormat()
});
}
...
}
private static Task MyValidation(CookieValidateIdentityContext context)
{
...
}
}
I have also tried by the controller's OnActionExecuting:
[AllowAnonymous]
public class MyController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// can't access cookies here
}
}
Any suggestions will be very welcome.
You need to create a claims identity and call SignIn on the AuthenticationManager (SignInManager.SignInAsync) this way, the Claims are updated:
// Get User and a claims-based identity
ApplicationUser user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
var Identity = new ClaimsIdentity(User.Identity);
// Remove existing claim and replace with a new value
await UserManager.RemoveClaimAsync(user.Id, Identity.FindFirst("AccountNo"));
await UserManager.AddClaimAsync(user.Id, new Claim("AccountNo", value));
// Re-Signin User to reflect the change in the Identity cookie
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
// [optional] remove claims from claims table dbo.AspNetUserClaims, if not needed
var userClaims = UserManager.GetClaims(user.Id);
if (userClaims.Any())
{
foreach (var item in userClaims)
{
UserManager.RemoveClaim(user.Id, item);
}
}

Add method to ApplicationUserManager

First time I use ASP.NET Identity and I probably miss something.
I know how to use ApplicationUserManager (my class extending UserManager) but I want to create a method inside of it that use UserManager methods because I don't want to repeat code.
Calling "base" doesn't work.
EDIT: the "base" didn't work because I had the method as static (I don't know why I wrote that).
Now it doesn't give me errors but if I try to call it from my Web API Controller I get the "Does not contain a definition of ..." error.
ApplicationUserManager:
namespace BLL
{
// Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
public async Task<int> RegistraPuntoScan(string userId, string sitoVisitato)
{
var user = await base.FindByIdAsync(userId);
if(user != null)
{
var s = new Stringa(sitoVisitato);
if (!user.URLVisitati.Contains(s))
{
user.Punti++;
user.URLVisitati.Add(s);
await base.UpdateAsync(user);
return 1;
}
else
{
return 2;
}
}
else
{
return 3;
}
}
}
}
Web API Controller:
namespace MyProject.Controllers.API
{
[CustomAuthorization]
public class PuntiController : ApiController
{
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
[HttpPost]
public IHttpActionResult RegistraPuntoScan(RegisterPointScanVm vm)
{
ClaimsPrincipal principal = Request.GetRequestContext().Principal as ClaimsPrincipal;
var idUtente = ClaimsPrincipal.Current.Identity.GetUserId();
var user = UserManager.FindById(idUtente);
switch(UserManager.RegistraPuntoScan(idUtente, vm.ScannedURL))
{
case 1:
return Ok();
case 2:
return Conflict();
case 3:
return BadRequest();
}
return BadRequest();
}
}
}
I solved the problem.
I was unable to call the new method because I have the ApplicationUserManager in another project and I probably forgot to delete the default ApplicationUserManager n IdentityConfig.cs, or VisualStudio created it, I don't know.
I deleted IdentityConfig (I have all the classes in other projects) and referenced the right one, now everything works.

How can I delete an asp.net identity 2.2.1 user in MVC5?

I am using asp.net identity 2.2.1 and I want to delete a user when he/she tries to hit a specific action method in one of none account controllers. Being into many SO question each of them points to a version dependent solution and frankly I couldn't find a to the point answer.
Why there is no plain and simple documentation on deleting an identity user and most importantly why this feature is not part of the identity it self?
Please note that I am using individual user accounts for external logins no local login is allowed.
My identity.cofig files looks like below:
namespace SocialManager
{
public class EmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Plug in your email service here to send an email.
return Task.FromResult(0);
}
}
public class SmsService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Plug in your SMS service here to send a text message.
return Task.FromResult(0);
}
}
// Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configure the application sign-in manager which is used in this application.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}
}
UserManager has method that looks like this:
public virtual async Task<IdentityResult> DeleteAsync(TUser user)
Use it to delete user record.
Update:
here is how to delete a user:
// id is id of the user to be deleted.
var user = await userManager.FindByIdAsync(id); //use async find
var result = await userManager.DeleteAsync(user);
if (result.Succeeded)
{
// user is deleted
}

Get user claims before any page loads on external ADFS login

What I'm trying to do is accessing user claims which returns from ADFS login. ADFS returns username and with that username I have to run a query to another DB to get user information and store it. I don't really know where to do that and what the best practice is. I can access user claims in the view controller like:
public ActionResult Index()
{
var ctx = Request.GetOwinContext();
ClaimsPrincipal user = ctx.Authentication.User;
IEnumerable<Claim> claims = user.Claims;
return View();
}
But what I need to do is as I said access claims like in global.asax.cs or startup.cs to store user information before the application runs.
This is my Startup.Auth.cs file:
public partial class Startup
{
private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"];
private static string adfsMetadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"];
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(WsFederationAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(
new CookieAuthenticationOptions
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
});
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = adfsMetadata
});
}
}
We add an event handler to the WsFederationAuthenticationOptions value in our startup file.
This happens immediately after the security token has been validated.
app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions()
{
MetadataAddress = MetadataAddress,
Wtrealm = Wtrealm,
Wreply = CallbackPath,
Notifications = new WsFederationAuthenticationNotifications()
{
SecurityTokenValidated = (ctx) =>
{
ClaimsIdentity identity = ctx.AuthenticationTicket.Identity;
DoSomethingWithLoggedInUser(identity);
}
}
};

ASP.Net Owin Identity, Signup Successful but cannot Login

My Startup.Auth.cs is as follows
[assembly: OwinStartup(typeof(IoTWeb.App_Start.Startup))]
namespace IoTWeb.App_Start
{
public class Startup
{
private const int DEFAULTTIMEOUT = 5;
private const int DEFAULTEXPIRETIMESPAN = 5;
public void Configuration(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
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
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity =
SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, User>(TimeSpan.FromMinutes(DEFAULTTIMEOUT),
(manager, user) => Task.FromResult(manager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie)))
},
SlidingExpiration = true,
ExpireTimeSpan = TimeSpan.FromMinutes(DEFAULTEXPIRETIMESPAN)
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
}
and my UserStoreService.cs
public class UserStoreService : IUserStore<User>, IUserPasswordStore<User>, IUserEmailStore<User>
{
private readonly TenantEntities context = new TenantEntities();
public Task<User> Find(string userName, string password)
{
Task<User> task = context.User.Where(
apu => apu.UserName == userName && apu.Password == password)
.FirstOrDefaultAsync();
return task;
}
My AccountController
public ActionResult Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var manager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
User user = manager.Find(model.UserName, model.Password);
if (user != null)
{
IAuthenticationManager authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity identity = manager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(new AuthenticationProperties { IsPersistent = false }, identity);
return RedirectToLocal(returnUrl);
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "Login failed due to incorrect credentials.");
return View(model);
}
[AllowAnonymous]
public ActionResult Register()
{
// Remove the Cookie in Here as he goes a bit further
if (ControllerContext.HttpContext.Request.Cookies.AllKeys.Contains("FinancesModelDataCookie"))
{
HttpCookie cookie = ControllerContext.HttpContext.Request.Cookies["FinancesModelDataCookie"];
if (cookie != null)
{
cookie.Expires = DateTime.Now.AddDays(-1);
ControllerContext.HttpContext.Response.Cookies.Add(cookie);
}
}
return View();
}
Register works successfully but Login stucks at
User user = manager.Find(model.UserName, model.Password);
and gives following exception
Edit here is the Identity.Config
public class ApplicationUserManager : UserManager<User>
{
public ApplicationUserManager()
: base(new UserStoreService())
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options,
IOwinContext context)
{
var manager = new ApplicationUserManager();
manager.PasswordHasher = new PasswordHasher(); // new NoPasswordHasher();
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<User>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
//Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
};
manager.PasswordValidator = new CustomPasswordValidator(6); //commented for and used above defined validator
//manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<User>
//{
// Subject = "SecurityCode",
// BodyFormat = "Your security code is: {0}"
//});
manager.EmailService = new EmailService();
IDataProtectionProvider dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<User>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}

Categories

Resources