How do I loosely couple the Blazor Identity scaffold with my own Database Context? - entity-framework-core

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);

Related

ASP MVC EF6 Code first Multi tenant get tenant id

we keep fighting with out multi tenant application.
This is an ASP MVC EF6 Code First web application.
We initialize a list of tenants in the Application_Start, getting a pair of values:
Host
TenantId
So we can associate any host with one TenantId, and store that list in cache.
We have configured a custom filter to get the current tenant.
public class TenantActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Items.Add("TenantId", GetCurrentTenant(filterContext.HttpContext.Request.Url.Host));
base.OnActionExecuting(filterContext);
}
}
The GetCurrentTenant function just access the list in cache and get the current one based on the host passed.
Is it correct to store the current tenant in an item in the context?
After that, we have created an Interceptor to get any query and add a filter to filter by TenantId. This is done and working good, we just need to add the tenantId from the context:
The problem we have is where we get the TenantId for each request.
if (HttpContext.Current.CurrentHandler == null) return;
var clientId = Convert.ToInt32(HttpContext.Current.Items["ClientId"]);
foreach (DbParameter param in command.Parameters)
{
if (param.ParameterName != TenantAwareAttribute.TenantIdFilterParameterName)
continue;
param.Value = clientId;
}
We don't know if this is the correct approach since there is a lot of informationon the net.
Thanks.
In my experience, the persistence of the tenant Id in the HTTP context is not right, as in some cases, the HTTP context becomes null.
You can try to get the tenant Id from the claims of the current principal. Creating a static class with a tenant identifier property that reads from the claims and gives is more reliable. Assuming you are using the owin pipeline, this should be easy to do. You can take a look at the reference sample application from github here
It looks like the below block,
public static class UserContext
{
public static string TenantId
{
get
{
return Threading.Thread.CurrentPrincipal.FindFirst("tenantid");
}
}
}

IdentityServer3 TokenHandleStore

I have implemented IdentityServer3 within our web application. I use custom login with LocalRegistrationUserService and clients from db. It works great so far.
My next task is to store the access tokens to the database. I've looked at the IdentityServer3.EntityFramework source code (https://github.com/IdentityServer/IdentityServer3.EntityFramework) and it looks quite a lot of things in there plus SQL tables.
My question is, do I need all of source code and the SQL tables? I just want to store the tokens and be able to load them. Also has anyone done it without using EntityFramework? If anyone has sample please share. Thank you.
ps: it's a pain that I have to convert this to VB
Based in your description, it appears you intent to keep your clients and scopes in memory but have all token and flow data persistent.
You will absolutely need more than just the TokenHandleStore to support the standard OIDC flows. Every operational store is essential except the ConsentStore. This one is optional only if your clients will never prompt users for scope consent. If you only want to support a subset of flow(s), you may be able to slip by with less, but it depends on the flows involved. For extensibility, posterity, and conformity, you probably want to have a fully-functional set of operational stores.
So, the options are to pull in the IdentityServer3.EntityFramework package and use the RegisterOperationalServices extension method in your startup, or to write the operational stores yourself. Writing it all yourself would be somewhat beneficial as it provides some good insight into all of the OIDC flows. You are also not restricted to EntityFramework, and are free to choose your data access methodology.
I have written operational stores myself, excluding the ConsentStore. The schema is quite different from that in IdentityServer3.EntityFramework, but both serve the same purpose:
The answer is ITokenHandleStore. Implementing this interface will make you save tokens in your own database. Follow these steps below.
Step 1. Get to know the interfaces in charge.
ITokenHandleStore itself doesn't have any signatures.
public interface ITokenHandleStore : ITransientDataRepository<Token>
{
}
ITransientDataRepository<T> has them instead.
public interface ITransientDataRepository<T> where T : ITokenMetadata
{
//
// Retrieves all data for a subject identifier.
//
// subject:
// The subject identifier.
//
// A list of token metadata
Task<IEnumerable<ITokenMetadata>> GetAllAsync(string subject);
//
// Retrieves the data.
//
// key:
// The key.
Task<T> GetAsync(string key);
//
// Removes the data.
//
// key:
// The key.
Task RemoveAsync(string key);
//
// Revokes all data for a client and subject id combination.
//
// subject:
// The subject.
//
// client:
// The client.
Task RevokeAsync(string subject, string client);
//
// Stores the data.
//
// key:
// The key.
//
// value:
// The value.
Task StoreAsync(string key, T value);
}
Step 2. The interface overview.
This is the hierarchical relationship between the interfaces and implementations I've extracted from VisualStudio
Step 3. Implementation for your own token storage.
All you need to do now is to create a class implementing ITokenHandleStore and tweak codes that access to your own database like this. ( this is a set of pseudo codes. )
public class HoanMadeTokenHandleStore : ITokenHandleStore
{
public async Task<IEnumerable<ITokenMetadata>> GetAllAsync(string subject)
{
#region pseudo code
// 1. Connect to database
var db = Connection.connectDatabase("yourDatabaseConnectionString");
// 2. Select tokens
db.Query("SELECT * FROM Tokens");
db.AddWhereCondition("SubjectId", subject);
// 3. Actual Database Querying
// SELECT * FROM Tokens WHERE SubjectId = subject
var results = db.Select();
#endregion
return results.Cast<ITokenMetadata>();
}
public async Task<T> GetAsync(string key)
{
#region pseudo code
// 1. Connect to database
var db = Connection.connectDatabase("yourDatabaseConnectionString");
// 2. Select tokens
db.Query("SELECT * FROM Tokens");
db.AddWhereCondition("key", key);
// 3. Actual Database Querying
// SELECT * FROM Tokens WHERE AccessToken = key
var token = db.Select();
#endregion
return token;
}
public async Task RemoveAsync(string key)
{
#region pseudo code
// 1. Connect to database
var db = Connection.connectDatabase("yourDatabaseConnectionString");
// 2. Delete tokens
db.Query("DELETE FROM Tokens");
db.AddWhereCondition("key", key);
// 3. Actual Database Querying
// DELETE FROM Tokens WHERE AccessToken = key
var token = db.Delete();
#endregion
}
public async Task RevokeAsync(string subject, string client)
{
#region pseudo code
// 1. Connect to database
var db = Connection.connectDatabase("yourDatabaseConnectionString");
// 2. Delete tokens
db.Query("DELETE FROM Tokens");
db.AddWhereCondition("ClientId", client);
db.AddWhereCondition("SubjectId", subject);
// 3. Actual Database Querying
// DELETE FROM Tokens WHERE ClientId = client AND SubjectId = subject
var token = db.Delete();
#endregion
}
public override async Task StoreAsync(string key, Token value)
{
#region pseudo code
// 1. Connect to database
var db = Connection.connectDatabase("yourDatabaseConnectionString");
// 2. Add Token
db.Query("INSERT INTO Tokens");
db.AddValues("AccessToken", key);
db.AddValues("TokenType", value.type);
db.AddValues("LifeTime", value.LifeTime);
// and so on~
// 3. Insert a token
// INSERT 항목은 정하기 나름입니다.
// this is only an example, you can store any properties you want or you think you'd need in the future.
// INSERT INTO Tokens ( AccessToken, TokenType, LifeTime ) VALUES ( "adasdfcbe66qsa123512312", 3, 3600 );
db.Add();
#endregion
}
}
Step 4. Last thing to do
IdentityServer3 uses Autofac as its IoC container, which means you're going to have to register your own implementation, HoanMadeTokenHandleStore. You may have seen this code when you were working with IdentityServer3.
.
.
.
var factory = new IdentityServerServiceFactory();
factory.UseInMemoryUsers(Users.Get());
factory.UseInMemoryClients(Users.Get());
factory.UseInMemoryScopes(Users.Get());
.
.
.
Yes, this factory is you should look at this time. This is the literally a factory for processing the token and every tasks in IdentityServer3 pipeline. Simply adding this after initializing the factory object will do.
factory.TokenHandleStore = new Registration<ITokenHandleStore, HoanMadeTokenHandleStore>();
Then the autofac will inject your implementation at runtime. You can write any database related code in your own implementation eg. HoanMadeTokenHandleStore. Entityframework, ADO.NET, Dapper.NET or explicit SqlConnection and Commands, whatever.

Issues with CurrentUserPropertyBinder it cannot always remember user

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.

How do I prevent EF from reinserting objects of other dbcontext?

I am using EF 4.1 in a MVC 3 project and I am having problems with inserting an object. I am using session as well to hold onto some objects. In concrete :
I have a simple class with a parent child relationship:
public class Event
{
public User Promotor {get;set;}
}
The promotor is based on the CurrentUser. In my application I store the CurrentUser in http session. Now when I add an event like this the user (and all related objects) gets inserted one more time with a new primary key.
//1st request inserts/loads the user
User user;
using (var context = new MyDbContext())
{
user = new User();
context.Users.Add(user);
context.SaveChanges();
}
//2nd request saves the event
var before = db.Users.Count();
var #event = new Event
{
Promotor = user, //user was kept in Session
};
db.Entry(#event).State = EntityState.Added;
db.SaveChanges();
When i check the state of the user it is 'added' as well although the primary key is not 0 and EF should know it is already persistent. How can fix this without adding a lot of to my persistency code. Do I have to reattach my currentuser to the new dbcontext on every request? This will lead to db code 'leaking' into my application. I want to keep the DB stuff in a data layer. I am using a repository like this :
public void Save(T entity)
{
dbContext.Entry(entity).State = IsPersistent(entity) ?
EntityState.Modified : EntityState.Added;
dbContext.SaveChanges();
}
As you've already mentioned yourself, reattaching the user to your current context should solve the problem. The only other way I know of, would be to retrieve the object once again in your context based on the primary key value.

Dependency Injection & Model Binding (ASP MVC, Autofac), When to use what?

This is more like a conceptual question. When to use Model Binding (in ASP.NET MVC Framework) and when to inject objects using IoC (lets say Autofac here) ?
One specific scenario is like lets say, I have the following action method
public ActionResult EditProfile(string UserId)
{
// get user object from repository using the the UserId
// edit profile
// save changes
// return feedback
}
In the above scenario, is it possible to inject a user object to action method such that it automatically gets the user object using the UserId ? The resulting signature being:
public ActionResult EditProfile(UserProfile userObj) //userObj injected *somehow* to automatically retreive the object from repo using UserId ?
Sorry if it all doesn't makes sense. It`s my first time using IoC.
EDIT:
This is the way to do it > http://buildstarted.com/2010/09/12/custom-model-binders-in-mvc-3-with-imodelbinder/
You can do what you need using a custom action filter. By overriding OnActionExecuting, we have access to the route data, and the action parameters of the action that will be executed. Given:
public class BindUserProfileAttribute : ActionFilterAttribute
{
public override OnActionExecuting(FilterContext filterContext)
{
string id = (string)filterContext.RouteData.Values["UserId"];
var model = new UserProfile { Id = id };
filtextContext.ActionParameters["userObj"] = model;
}
}
This attribute allows us to create the parameters that will be passed into the action, so we can load the user object at this point.
[BindUserProfile]
public ActionResult EditProfile(UserProfile userObj)
{
}
You'll probably need to get specific with your routes:
routes.MapRoute(
"EditProfile",
"Account/EditProfile/{UserId}",
new { controller = "Account", action = "EditProfile" });
In MVC3 we get access to the new IDepedencyResolver interface, which allows us to perform IoC/SL using whatever IoC container or service locator we want, so we can push a service like a IUserProfileFactory into your filter, to then be able to create your UserProfile instance.
Hope that helps?
Model binding is used for your data. Dependency injection is used for your business logic.