Validating user in Azure Mobile App Against Exiting User Database - c#

I am writing my first Azure Mobile App and I want to implement "Custom Authentication" against an existing websites user database.
In the existing ASP.Net website I have the usual dbo.AspNetUsers tables etc.
I cannot work out how to call this existing website to authenticate a user.
I have the following code but I am lost how to get the isValidAssertion function to talk to my existing user database from within the Axure Mobile App.
It would be the equivalent of this line found in the website..
ApplicationSignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
So, I have the following code:
private static bool isValidAssertion(JObject assertion)
{
// this is where I want to call the existing user database
// this is how it's done in the MVC website
//ApplicationSignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
return true;
}
public IHttpActionResult Post([FromBody] JObject assertion)
{
if (isValidAssertion(assertion)) // user-defined function, checks against a database
{
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, (string)assertion["username"]) },
mySigningKey,
myAppURL,
myAppURL,
TimeSpan.FromHours(24));
return Ok(new LoginResult()
{
AuthenticationToken = token.RawData,
User = new LoginResultUser() { UserId = (string)assertion["username"] }
});
}
else // user assertion was not valid
{
return ResponseMessage(Request.CreateUnauthorizedResponse());
}
}
Can anyone point me in the right direction please?

You would need to use Asp.Net Identity for this.
Basically you need to create BaseApiController to get OWIN Context first:
public class BaseApiController : ApiController
{
private ApplicationUserManager _appUserManager = null;
protected ApplicationUserManager AppUserManager
{
get
{
return _appUserManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
}
}
Then in your custom authentication controller need to inherit from BaseApiController:
[MobileAppController]
public class CustomAuthController : BaseApiController
{
private static bool isValidAssertion(JObject assertion)
{
var username = assertion["username"].Value<string>();
var password = assertion["password"].Value<string>();
//Validate user using FindAsync() method
var user = await this.AppUserManager.FindAsync(username, password);
return (user != null);
}
}
You will also need to init OWIN context in Startup class:
[assembly: OwinStartup(typeof(UPARMobileApp.Startup))]
namespace UPARMobileApp
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOWinContext(app);
ConfigureMobileApp(app);
}
}
}
public static void ConfigureOWinContext(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
}
More information about how to setup ApplicationDbContext, ApplicationUserManager and OWIN configuration can be read from here:
http://bitoftech.net/2015/01/21/asp-net-identity-2-with-asp-net-web-api-2-accounts-management/

Related

Access session variables across all controllers in .net core 5.0

I have been researching this for a minute but nothing seems to work. I want to be able to set multiple session variables at different points in time and access them across all controllers.
I have tried the below:
I created a base controller and set a public string of the variable I want to access.
public class BaseController : Controller
{
public string UserToken
{
get
{
string token = HttpContext.Session.GetString("SessionUserToken");
return token == null ? null : token.ToString();
}
set
{
HttpContext.Session.SetString("SessionUserToken", value);
}
}
}
then I inherited from the base controller into my home controller
public class HomeController : BaseController
{
public async Task<IActionResult> Index()
{
string userToken = HttpContext.Session.GetString("SessionUserToken"); ;
if (userToken == null)
//do something
else
//do something else
}
}
And in another controller as well
public class ProfileController: BaseController
{
public async Task<IActionResult> Login(LoginModel login)
{
Login account = await _Repository.Login(login);
if(account.succeeded)
{
UserToken = account.data;
}else{
redirectToAction("Index", "Home");
}
}
}
Does anyone know how I can accomplish this?
Looks like your re-creating the authentication system in ASP.NET
I would very much suggest that you consider using the Cookie Authentication system that's already build in. This does NOT require you to use ASP.NET Identity.
See https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-6.0
In your startup file you would have to set
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
options.SlidingExpiration = true;
options.AccessDeniedPath = "/Forbidden/";
});
Then in your ProfileController you can run the below code to set the authentication cookie.
var claimsIdentity = new ClaimsIdentity(new List<Claim>
{
new Claim("SessionUserToken", account.data)
}, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties());
Then you can validate access either using the classic attributes like [Authorize] or you can check User.Identity.IsAuthenicated and when you need the SessionUserToken you can access User.FindFirstValue("SessionUserToken")

IdentityServer AdditionalClaims not included after login

I'm setting up a new instance of IdentityServer as an identity provider. While logging in, I want to set some extra, custom claims on my user object. Right now, I'm using the following code:
[HttpPost]
public async Task<IActionResult> ExecuteLogin(string returnUrl, string loginId)
{
TestUser user = Config.GetUsers().Find(x => x.SubjectId == loginId);
if (user != null)
{
var identityServerUser = new IdentityServerUser(user.SubjectId)
{
AdditionalClaims = user.Claims
};
await HttpContext.SignInAsync(identityServerUser);
return Redirect(returnUrl);
}
else
{
return Redirect("Login");
}
}
I expected the AdditionalClaims to show up on the User.Claims object on the receiving application, which I use as following:
[Authorize]
public class HomeController : Controller
{
public IActionResult Index()
{
var claims = User.Claims;
return View(claims);
}
}
However, in the view only the standard claims are visible. Not my additional claims.
In the setup of IdentityServer I specified a client with access to the scope these claims are in, and an IdentityResource with the claimtypes specified in the TestUser. On the receiving application, I specified I want to receive that scope.
What makes that my claims are not visible on the receiving application?
It is not said what type of authentication you are using, but I suppose you want to add the claims to the access_token from where they can be read by on the API.
AdditionalClaims on IdentityServerUser are only added to the cookie in your client.
What you have to do is to create a profile service (https://docs.identityserver.io/en/latest/reference/profileservice.html).
At the simplest it will be something like this:
public class ProfileService : IProfileService
{
private UserService _userService;
public ProfileService(UserService userService)
{
_userService = userService;
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var user = await _userService.GetUserByIdAsync(context.Subject.GetSubjectId());
context.IssuedClaims.AddRange(user.Claims);
return Task.FromResult(0);
}
public Task IsActiveAsync(IsActiveContext context)
{
var user = await _userService.GetUserByIdAsync(context.Subject.GetSubjectId());
context.IsActive = user.IsActive;
return Task.FromResult(0);
}
}
And register it in the Startup.cs:
services.AddIdentityServer()
.AddProfileService<ProfileService>();
These can then be read from the access_token on the API side (if that's what you wanted as it is not clear from the question):
var user = User.Identity as ClaimsIdentity;
var claims = user.Claims;
You need to explicitly map those extra claims in your client, using code like:
options.ClaimActions.MapUniqueJsonKey("website", "website");
options.ClaimActions.MapUniqueJsonKey("gender", "gender");
options.ClaimActions.MapUniqueJsonKey("birthdate", "birthdate");
There is also this option you can set:
options.GetClaimsFromUserInfoEndpoint = true;

ASP.NET Core - Authorization Using Windows Authentication

I have configured my web api to work with windows authentication. My goal is essentially to restrict certain actions in my controllers based on a users windows account. Some will be able to preform read actions while others will be able to preform actions that will write to the underlying database. I have found plenty of documentation on how to set up claims based authorization which is the route I think I need to go. What I have not found is how to set this up with windows auth. I think I am missing a middle step such as registering the windows auth as the identity provider?
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddAuthorization(options =>
{
options.AddPolicy("readOnly", policy =>
policy.RequireClaim(`???????????????????????`));
options.AddPolicy("write", policy =>
policy.RequireClaim(`???????????????????????`));
});
}
Controller
[Authorize(Policy = "ReadOnly")]
public class MyController : Controller
{
public ActionResult SomeReadOnlyAction()
{
//Return data from database
}
[Authorize(Policy = "Write")]
public ActionResult AWriteAction()
{
//Create/Update/Delete data from database
}
}
I guess another way to ask this question is how do you configure or access claims/roles etc... with windows authentication.
That seems you want to use claims-based authorization via policies . After setting windows authentication in your application , you could add custom claim to ClaimsPrincipal ,check user's identity and confirm which permission current user has :
You can add a claims transformation service to your application:
class ClaimsTransformer : IClaimsTransformation
{
public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
var id = ((ClaimsIdentity)principal.Identity);
var ci = new ClaimsIdentity(id.Claims, id.AuthenticationType, id.NameClaimType, id.RoleClaimType);
if (ci.Name.Equals("name"))
{
ci.AddClaim(new Claim("permission", "readOnly"));
}
else
{
ci.AddClaim(new Claim("permission", "write"));
}
var cp = new ClaimsPrincipal(ci);
return Task.FromResult(cp);
}
}
Add to Startup.cs(.net Core 2.0) :
services.AddTransient<IClaimsTransformation, ClaimsTransformer>();
Set your policy :
services.AddAuthorization(options =>
{
options.AddPolicy("Readonly", policy =>
policy.RequireClaim("permission", "readOnly"));
options.AddPolicy("Write", policy =>
policy.RequireClaim("permission", "write"));
});
Restrict access to a controller or action by requiring this policy:
[Authorize(Policy = "Write")]
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
}
If you have already add groups(write,readonly) in your AD and add the related users to group , you can also check the groups :
public static class Security
{
public static bool IsInGroup(this ClaimsPrincipal User, string GroupName)
{
var groups = new List<string>();
var wi = (WindowsIdentity)User.Identity;
if (wi.Groups != null)
{
foreach (var group in wi.Groups)
{
try
{
groups.Add(group.Translate(typeof(NTAccount)).ToString());
}
catch (Exception)
{
// ignored
}
}
return groups.Contains(GroupName);
}
return false;
}
}
And use like :
if (User.IsInGroup("GroupName"))
{
}

Why does my log in info is missing after my RedirectToAction call?

In my asp.net MVC 4.6 application, after I logged in the site, I need to call RedirectToAction to call another controller, but the current log in data is lost before reaching another controller. However, the data is not lost if I run it in Anonymous authentication is enabled. But I can not make the Anonymous Authentication enabled.
Here is the log on method in my AccountController class:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult LogOn(string userName, string password, string returnUrl)
{
if (ValidateLogOn(userName, password) == false)
{
return View();
}
FormsAuthenticationService.SignIn(userName, false);
IVtrsUser currentUser = MembershipService.Get(userName);
if (currentUser.IsPasswordExpired)
{
return RedirectToAction("ChangePassword");
}
else if (string.IsNullOrEmpty(returnUrl) == false)
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Calendar");
}
}
From the debug, I can see the two services have set the current user properly:
FormsAuthenticationService.SignIn(userName, false);
IVtrsUser currentUser = MembershipService.Get(userName);
But after the code excuated to the CalendarController as below:
[Authorize(Roles = "Admin")]
public class CalendarController : Controller
{ ...
The user signed in data such as Role = "Admin" is lost, which causes the following code in CalendarController is wrong, and is not execute the Index() method of CalendarController, Can anybody help me why this user login data is lost in Anonymous Authentication is Disabled mode? and the user data is remaining when Anonymous Authentication is enabled mode?
Add more , there is related codes:
public IFormsAuthenticationService FormsAuthenticationService { get; private set; }
and this is IFormsAuthenticationService:
namespace VTRS.WebApp.Controllers.Common
{
public interface IFormsAuthenticationService
{
void SignIn(string userName, bool createPersistentCookie);
void SignOut();
}
}
please let me know what is the standard or best way to keep user log
in data, then I can modify my service or rewrite the code to keep the
user log in data.
In ASP.NET MVC, you want to use ASP.NET OWIN Cookie Middleware instead of FormAuthentication.
OwinAuthenticationService
private readonly HttpContextBase _context;
private const string AuthenticationType = "ApplicationCookie";
public OwinAuthenticationService(HttpContextBase context)
{
_context = context;
}
public void SignIn(User user)
{
IList<Claim> claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
};
ClaimsIdentity identity = new ClaimsIdentity(claims, AuthenticationType);
IOwinContext context = _context.Request.GetOwinContext();
IAuthenticationManager authenticationManager = context.Authentication;
authenticationManager.SignIn(identity);
}
public void SignOut()
{
IOwinContext context = _context.Request.GetOwinContext();
IAuthenticationManager authenticationManager = context.Authentication;
authenticationManager.SignOut(AuthenticationType);
}
Startup.cs
You also need to configure Startup for all those to happen.
[assembly: OwinStartup(typeof(YourApplication.Startup))]
namespace YourApplication
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ApplicationCookie",
LoginPath = new PathString("/Account/Login")
});
}
}
}
Then you can start using [Authorize] attribute in Controller and Action methods.
[Authorize]
public class UsersController : Controller
{
// ...
}
Here is my sample application at GitHub which uses AD for Authentication. But the core pieces are OwinAuthenticationService.cs and Startup.cs.

MVC 6 Cookie Authentication - Getting User Details From the Cookie

I'm working on an MVC 6 application that does not use Entity or Identity. Instead, I'm using Dapper. I have a controller that accepts a POST request and uses Dapper to check the database to see if the users name / password match. All I'd like to do is store the users name and whether they're logged in or not so I can make that check on subsequent pages.
I looked around and it seems like using Cookie based authentication should allow me to do what I want. Here's the relevant code in my Startup.cs file:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
LoginPath = "/account/login",
AuthenticationScheme = "Cookies",
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
Here's what the relevant code in my controllers login action looks like:
var user = _repo.FindByLogin(model.VendorId, model.Password);
if (user != null) {
var claims = new List < Claim > {
new Claim("VendorId", user.VendorId),
new Claim("Name", "john")
};
var id = new ClaimsIdentity(claims, "local", "name", "role");
await HttpContext.Authentication.SignInAsync("Cookies", new ClaimsPrincipal(id));
var l = ClaimsIdentity.DefaultNameClaimType;
return RedirectToAction("Index", "PurchaseOrders");
}
The above code seems to work in that a cookie is being saved, but I'm not sure how I would go about getting the user information back out of the cookie (or even how to retrieve the cookie on subsequent requests in the first place).
In my mind I'm imagining being able to do something like: var user = (User)HttpContext.Request.Cookies.Get(????), but I'm not sure if that's practical or not.
You can get the user data back by using the ClaimsPrincipal.FindFirstValue(xxx)
here is my example class which can be used in Controller/Views to get the current user information
public class GlobalSettings : IGlobalSettings
{
private IHttpContextAccessor _accessor;
public GlobalSettings(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
public string RefNo
{
get
{
return GetValue(_accessor.HttpContext.User, "employeeid");
}
}
public string SAMAccount
{
get
{
return GetValue(_accessor.HttpContext.User, ClaimTypes.WindowsAccountName);
}
}
public string UserName
{
get
{
return GetValue(_accessor.HttpContext.User, ClaimTypes.Name);
}
}
public string Role
{
get
{
return GetValue(_accessor.HttpContext.User, ClaimTypes.Role);
}
}
private string GetValue(ClaimsPrincipal principal, string key)
{
if (principal == null)
return string.Empty;
return principal.FindFirstValue(key);
}
}
Example Usage in controller after DI:
var currentUser = GlobalSettings.SAMAccount;
Please note that you need to inject HttpContextAccessor in ConfigureServices method in Startup.cs
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

Categories