I have implemented a CurrentUserPropertyBinder (see below) for a web application using FubuMVC.
public class CurrentUserPropertyBinder : IPropertyBinder
{
private readonly Database _database;
private readonly ISecurityContext _security;
public CurrentUserPropertyBinder(Database database, ISecurityContext security)
{
_database = database;
_security = security;
}
public bool Matches(PropertyInfo property)
{
return property.PropertyType == typeof(User)
&& property.Name == "CurrentUser";
}
public void Bind(PropertyInfo property, IBindingContext context)
{
var currentUser = //check database passing the username to get further user details using _security.CurrentIdentity.Name
property.SetValue(context.Object, currentUser, null);
}
}
When I login to my site, this works fine. The CurrentUserPropertyBinder has all the information it requires to perform the task (i.e. _security.CurrentIdentity.Name has the correct User details in it)
When I try and import a file using fineUploader (http://fineuploader.com/) which opens the standard fileDialog the _security.CurrentIdentity.Name is empty.
It doesn't seem to remember who the user was, I have no idea why. It works for all my other routes but then I import a file it will not remember the user.
Please help! Thanks in Advance
NOTE: We are using FubuMVC.Authentication to authenticate the users
I'm guessing your action for this is excluded from authentication; perhaps it's an AJAX-only endpoint/action. Without seeing what that action looks like, I think you can get away with a simple fix for this, if you've updated FubuMVC.Authentication in the past 3 months or so.
You need to enable pass-through authentication for this action. Out of the box, FubuMVC.Auth only wires up the IPrincipal for actions that require authentication. If you want access to that information from other actions, you have to enable the pass-through filter. Here are some quick ways to do that.
Adorn your endpoint/controller class, this specific action method, or the input model for this action with the [PassThroughAuthentication] attribute to opt-in to pass-through auth.
[PassThroughAuthentication]
public AjaxContinuation post_upload_file(UploadInputModel input) { ... }
or
[PassThroughAuthentication]
public class UploadInputModel { ... }
Alter the AuthenticationSettings to match the action call for pass-through in your FubuRegistry during bootstrap.
...
AlterSettings<AuthenticationSettings>(x => {
// Persistent cookie lasts 3 days ("remember me").
x.ExpireInMinutes = 4320;
// Many ways to filter here.
x.PassThroughChains.InputTypeIs<UploadInputModel>();
});
Check /_fubu/endpoints to ensure that the chain with your action call has the pass-through or authentication filter applied.
Related
I've created a Blazor Server App with the option to scaffold an identity system. This created an Entity Framework IdentityDbContext with a number of tables to manage user logins and settings. I decided to keep my own DbContext separate from this so that I could replace either of the contexts later, if necessary.
What I would like to do is have a User entity in my own custom dbcontext, and in it store a reference to the user id of the scaffolded IdentityDbContext entity. I would also like to ensure that I don't have to query the db for the custom entity every time the user opens a new page.
I've been looking around StackOverflow trying to find good suggestions of how to approach this, but I'm still not sure how to start. So I have a few questions:
Is my approach a sensible one?
How do I find a permanent id number or string to couple with on the UserIdentity?
Should I store my custom user entity in some sort of context so I don't have to query it all the time? If so, how?
All help is greatly appreciated!
It looks like your requirement is to store custom information about the current user above and beyond what is stored in Identity about the current user.
For simpler use cases you can create your own User class derived from IdentityUser and add additional properties on there and let Identity take care of all persistence and retrieval.
For more complex use cases you may follow the approach you have taken, whereby you create your own tables to store user related information.
It seems that you have taken the second approach.
Is my approach a sensible one?
I think so. Burying lots of business-specific context about the user in the Identity tables would tightly bind you to the Identity implementation.
How do I find a permanent id number or string to couple with on the
UserIdentity?
IdentityUser user = await UserManager<IdentityUser>.FindByNameAsync(username);
string uniqueId = user.Id;
// or, if the user is signed in ...
string uniqueId = UserManager<IdentityUser>.GetUserId(HttpContext.User);
Should I store my custom user entity in some sort of context so I
don't have to query it all the time? If so, how?
Let's say you have a class structure from your own DbContext that stores custom information about the user, then you can retrieve that when the user signs in, serialize it, and put it in a claim on the ClaimsPrincipal. This will then be available to you with every request without going back to the database. You can deserialize it from the Claims collection as needed and use it as required.
How to ...
Create a CustomUserClaimsPrincipalFactory (this will add custom claims when the user is authenticated by retrieving data from ICustomUserInfoService and storing in claims):
public class CustomUserClaimsPrincipalFactory
: UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
private readonly ICustomUserInfoService _customUserInfoService;
public CustomUserClaimsPrincipalFactory(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
IOptions<IdentityOptions> optionsAccessor,
ICustomUserInfoService customUserInfoService)
: base(userManager, roleManager, optionsAccessor)
{
_customUserInfoService= customUserInfoService;
}
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(
ApplicationUser user)
{
var identity = await base.GenerateClaimsAsync(user);
MyCustomUserInfo customUserInfo =
await _customUserInfoService.GetInfoAsync();
// NOTE:
// ... to add more claims, the claim type need to be registered
// ... in StartUp.cs : ConfigureServices
// e.g
//services.AddIdentityServer()
// .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
// {
// options.IdentityResources["openid"].UserClaims.Add("role");
// options.ApiResources.Single().UserClaims.Add("role");
// options.IdentityResources["openid"].UserClaims.Add("my-custom-info");
// options.ApiResources.Single().UserClaims.Add("my-custom-info");
// });
List<Claim> claims = new List<Claim>
{
// Add serialized custom user info to claims
new Claim("my-custom-info", JsonSerializer.Serialize(customUserInfo))
};
identity.AddClaims(claims.ToArray());
return identity;
}
}
Register your CustomUserInfoService in Startup.cs (your own service to get your custom user info from the database):
services.AddScoped<ICustomUserInfoService>(_ => new CustomUserInfoService());
Register Identity Options (with your CustomUserClaimsPrincipalFactory and authorisation in Startup.cs. NOTE: addition of "my-custom-info" as a registered userclaim type. Without this your code in CustomUserInfoService will fail to add the claim type "my-custom-info":
services.AddDefaultIdentity<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = false;
options.User.RequireUniqueEmail = true;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddClaimsPrincipalFactory<CustomUserClaimsPrincipalFactory>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
options.IdentityResources["openid"].UserClaims.Add("my-custom-info");
options.ApiResources.Single().UserClaims.Add("my-custom-info");
});
You can then retrieve your custom user info from claims, without returning to database, by using:
MyCustomUserInfo customUserInfo =
JsonSerializer.Deserialize<MyCustomUserInfo>(
HttpContext.User.Claims
.SingleOrDefault(c => c.Type == "my-custom-info").Value);
I am developing an internal MVC Application using Windows Authentication (WA). Authenticating users using WA is straight forward, however; with respect to user Roles, I have the following requirements:
We will use custom Roles ignoring the AD Roles. For example, a user
may have a 'Manager' role in the AD but his app role is set to
'Supervisor'. After the User is authenticated, the system will fetch
the user roles and set the CurrentPrincipal accordingly.
For the above, I plan to have 3 tables including User, Role
and UserRole. The Role table has the custom roles while the
User table consists of company users. The UserRole table will define
the mapping between User and their Role(s). The issue I see with this approach
is to pre-populate all 3 tables. The User table must have the list of all
company employees and is maintained for new/inactive employees. The UserRole
table should be set with each user and his role(s) before he logs in.
In the application, User are assigned to different tasks (for example John is
supervising Vehicles) plus we need to maintain user activity logs. Assuming
the above two points are valid, is it OK to use the ID field in the User
table for this purpose?
There is also a chance that later, we may deploy the application
over the public domain. In such a case, how can we use the existing
User/Role infrastructure for this purpose.
Thanks in advance.
You are in exactly the same boat as me, my friend! I managed to do this through a Custom Authorization Attribute. Here are a couple of points that I have stumbled on through this process.
I did not create my own user table. You can, but you can query AD for users depending on the amount of users on your domain and link it to your Roles / Activities tables using the Guid to search. If you do create a users table that mirrors AD, use the Guid for the user. This way, if the login/name/anything else changes, the Guid stays the same.
Custom authorization attribute:
namespace YourSite.Attributes
{
[AttributeUsage(AttributeTargets.Method)]
public class AuthRoleAttribute : ActionFilterAttribute
{
public string RoleName { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!ViewIsAuthorizedActivity(RoleName)) // create a bool function to see if your user is in this role... check your db or whatever
{
string requestMethod = filterContext.HttpContext.Request.HttpMethod;
if (requestMethod == "GET")// I chose two different means of forbidding a user... this just hides the part of the page based on the #if(ViewBag.Authorization = "FORBIDDEN") where you render a partial, else show the view
{
filterContext.Controller.ViewBag.Authorization = "FORBIDDEN";
}
else if (requestMethod == "POST") // This prevents over posting by redirecting them completely away from that controller... also prevents them from posting if they have the page loaded and you remove permission
{ // Hiding a part of the page doesn't matter for the POST if the page is already loaded
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "controller", "Home" },
{ "action", "Forbidden" },
{ "area", ""}
});
}
base.OnActionExecuting(filterContext);
}
}
}
}
How GETs are handled in the view:
#if (ViewBag.Authorization == "FORBIDDEN")
{
ViewBag.Title = "Forbidden!";
#Html.Partial("~/Views/Forbidden.cshtml");
}
else
<!-- User is not forbidden and have the view here -->
Note that for the POSTs the user is redirected away from the controller to the Forbidden controller.
Attribute on controller:
[AuthRole(RoleName = "Admin")]
public ActionResult YourController()
I also made a extension to the User so things may be hidden in the view if they don't have permission:
public static bool IsAuthorized(this IPrincipal user, string roleName)
{
return Attributes.AuthActivityAttribute.ViewIsAuthorizedByRole(roleName); // function determining if the user is in that role, therefore show what you want to show in the view or don't show it if false
}
Which is called by:
#if (User.IsAuthorized("Admin"))
{
<!-- show something, a link, etc. -->
}
Hopefully this gives you a better head start than I had. Let me know if you have questions.
I have REST service requirements in which some calls require authentication and some don't. Absolutely no state is used, as the calls are all independent from one another. I have put something together which seems to work, but is this the right way to go about not using sessions?
This question is kind of related to my WCF question which is answered here.
Firstly I registered the authentication method:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CustomCredentialsAuthProvider(), //HTML Form post of UserName/Password credentials
}
));
I then attribute the respective calls (or service or DTO) with the Authenticate attribute:
[Authenticate]
public HelloResponse Post(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name + " with POST & Auth"};
}
I inherit from the BasicAuthProvider class which does the authentication:
public class CustomCredentialsAuthProvider : BasicAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
return userName == "dylan" && password == "abc123";
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
session.IsAuthenticated = true;
//Important: You need to save the session!
authService.SaveSession(session, new TimeSpan(0,0,10));
}
}
As you can see, I do save the session but it times out after 10 seconds. This is the part that I'm sure can potentially be done better. It seems to work nicely though.
Is there a better way of doing what I'm trying to accomplish?
Is there also any way, due to the sessionless nature of these services, to remove the Auth, AssignRoles and UnassignRoles methods?
If you wanted to keep using ServiceStack's Authentication and Session support you could just add a response filter that clears the user session after the service is executed, e.g:
this.ResponseFilters.Add((req, res, dto) => req.RemoveSession());
Basically after each request is executed it clears the session, so no record of them having authenticated exists.
Otherwise you can just skip using ServiceStack's Authentication completely and just provide your own via RequestFitlers of FilterAttributes (which is essentially what SS Auth does under the hood).
In my authentication service, I would like to call methods (query or invoke) on my User service to validate credentials. So, for example:
protected override AuthUser ValidateCredentials(string name, string password,
string customData, out string userData)
{
AuthUser user = null;
userData = null;
using (UserService svc = new UserService())
{
if (the result of a call on UserService shows a valid username/password)
{
//Create the user object
user = new AuthUser()
{
Name = name,
UserId = // that user's UserId
};
}
if (user != null)
{
//Set custom data fields for HTTP session
userData = user.UserId.ToString();
}
}
return user;
}
The results I'm finding when searching for things like "call ria service from another ria service" and similar are unrelated to actually calling one from another. Am I doing something wrong from a paradigm point of view? If not, how the heck do you do this? :)
Aggregating DomainServices when all you want to do is Query is pretty easy. Something like
new MyDomainService().GetUser(userName)
should work just fine. However, when you're trying to Submit or Invoke it become trickier because you'll need to initialize and dispose the DomainService. It's been a while since I did this, but I think you can override Initialize and Dispose in your parent DS to call through to the methods in your child DS. For submitting, you won't be able to call the methods directly. Instead you'll need to create a ChangeSet and call the DS.Submit method.
Also, for your scenario, it might be worth checking out the custom authentication sample here. It's a slightly different approach for what you're trying to do.
I want to password protect a webpage in Wicket so the user may only access it if he/she has logged in.
I'd also like the page to show the login page, and then after logging in the original page the user was trying to get to.
How is this done with wicket? I've already created a login page and extended the session class.
The framework-supplied way is to provide an IAuthorizationStrategy instance for your application, e.g., by adding to your Application init() method:
init() {
...
getSecuritySettings().setAuthorizationStrategy(...)
}
A working example of Wickets authorization functionality is on Wicket Stuff here, which demonstrates some reasonably complex stuff. For really simple cases, have a look at the SimplePageAuthorizationStrategy. At a very basic level, this could be used like so (taken from the linked Javadoc):
SimplePageAuthorizationStrategy authorizationStrategy = new SimplePageAuthorizationStrategy(
MySecureWebPage.class, MySignInPage.class)
{
protected boolean isAuthorized()
{
// Authorize access based on user authentication in the session
return (((MySession)Session.get()).isSignedIn());
}
};
getSecuritySettings().setAuthorizationStrategy(authorizationStrategy);
Edit in response to comment
I think the best way forward, if you're just going to use something like SimplePageAuthorizationStrategy rather than that class itself. I did something like this to capture pages that are annotated with a custom annotation:
IAuthorizationStrategy authorizationStrategy = new AbstractPageAuthorizationStrategy()
{
protected boolean isPageAuthorized(java.lang.Class<Page.class> pageClass)
{
if (pageClass.getAnnotation(Protected.class) != null) {
return (((MySession)Session.get()).isSignedIn());
} else {
return true;
}
}
};
Then you'd need to register an IUnauthorizedComponentInstantiationListener similar to what is done in SimplePageAuthorizationStrategy (link is to the source code), which should be something like:
new IUnauthorizedComponentInstantiationListener()
{
public void onUnauthorizedInstantiation(final Component component)
{
if (component instanceof Page)
{
throw new RestartResponseAtInterceptPageException(MySignInPage.class);
}
else
{
throw new UnauthorizedInstantiationException(component.getClass());
}
}
});