I'm trying to write a custom method that does something similar to what the CreateAsync(...) method in AspNetCore Identity UserManager does: save data to the data store and return the original data with updated properties from an async method.
For example:
var user = new ApplicationUser { Username = "Someone", Email = "some#one.com" };
var result = await _userManager.CreateAsync(user, "P#ssw0rd");
In this example, the user variable will contain other properties that have been updated after the CreateAsync(...) was called.
How does this work when you cannot use ref in an async method AND they aren't using Tuple<,>?
Related
New to Blazor and have been doing a hatchet job to get things working how I want.
I am using Blazor WASM with AAD for Authentication created based on this document MS Doc. I implemented the SecureAccountFactory class from the example and call a db where I get the associated user based on the AAD Guid, then add everything into Claims.
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(SecureUserAccount account,
RemoteAuthenticationUserOptions options)
{
var initialUser = await base.CreateUserAsync(account, options);
if (initialUser.Identity.IsAuthenticated)
{
var userIdentity = (ClaimsIdentity)initialUser.Identity;
var claims = userIdentity.Claims;
var principalId = claims.Where(x => x.Type == "oid").First();
//Get some user info from SQL
var User = await _UserService.Get(principalId.Value);
//Get user Roles from SQL and add to Claims
var UsersInRoles = await _UsersInRoleService.RolesByUserId(principalId.Value);
//Add the ClientId to Claims
userIdentity.AddClaim(new Claim("clientId", User.ClientId.ToString()));
foreach (var userrole in UsersInRoles)
{
userIdentity.AddClaim(new Claim("appRole", userrole.Role.Name));
}
}
return initialUser;
}
I then have a Profile Component that appears on every page as part of the MainLayout which should have some info about the current user, so I made a static class to retrieve this info.
public static class UserHelper
{
public static async Task<CurrentUserClaims> GetCurrentUserClaims(Task<AuthenticationState> authenticationStateTask)
{
AuthenticationState authenticationState;
authenticationState = await authenticationStateTask;
var AuthenticationStateUser = authenticationState.User;
var user = authenticationState.User;
var claims = user.Claims;
var clientClaim = claims.Where(x => x.Type == "clientId").First();
var principalId = claims.Where(x => x.Type == "oid").First();
return new CurrentUserClaims
{
ClientId = Convert.ToInt32(clientClaim.Value),
PrincipalId = Guid.Parse(principalId.Value),
user = user
};
}
}
In my ProfileComponent, I call CascadingParameter and then onParametersSet I query my Static class for the info from the current logged in user
[CascadingParameter]
private Task<AuthenticationState> authenticationStateTask { get; set; }
private string profilePath;
protected override async Task OnParametersSetAsync()
{
CurrentUserClaims UserClaims = await UserHelper.GetCurrentUserClaims(authenticationStateTask);
var principal = UserClaims.PrincipalId;
//... do stuff
}
The above all works, after a Refresh or once I route to any other page. The initial Load, after login on the home page shows that the below line always fails with 'Sequence contains no elements'
var clientClaim = claims.Where(x => x.Type == "clientId").First();
I am using Authorize to protect the pages and I will eventually be using the Roles to determine what to display to the user.
A: Surely there's a better way of doing the above. There are lots and lots of articles on creating a custom Auth which inherits AuthenticationState but every one I've seen adds the Claims manually as a fake user, so I don't see how to access the actual Claims.
B: I'm wondering if just using LocalStorage for the User info might be a simpler way to go but is it considered 'safe' or best practice?
Any pointers to a solution are appreciated.
I want to switch my code to an async implementation. When I want to do this then I notice that my related data gets not set automatically after I retrieve them like it used to do it.
This is the initial function that gets called from an API controller. I used the AddDbContext function to add the dbcontext class via dependency injection into my controller:
public async Task<Application> GetApplicationById(AntragDBNoInheritanceContext dbContext, int id)
{
List<Application> ApplicationList = await dbContext.Applications.FromSqlRaw("Exec dbo.GetApplication {0}", id).ToListAsync();
Application Application = ApplicationList.First();
if(Application != null)
{
await CategoryFunctions.GetCategoryByApplicationID(Application.Id);
}
}
The GetCategoryByApplicationId function loads the related category of an application which is a many to one relation between Category and Application:
public async Task<Category> GetCategoryByApplicationID(int applicationID)
{
var optionsBuilder = new DbContextOptionsBuilder<AntragDBNoInheritanceContext>();
optionsBuilder.UseSqlServer(ApplicationDBConnection.APPLICATION_CONNECTION);
using (var dbContext = new AntragDBNoInheritanceContext(optionsBuilder.Options))
{
List<Category> category = await dbContext.Categories.FromSqlRaw("Exec GetApplicationCategory {0}", applicationID).ToListAsync();
if (category.Any())
{
return category.First();
}
}
return null;
}
When I want to retrieve an application then the field Category is not set. When I did not use async/await it would set the category automatically for me. Of course I could just return the Category Object from the GetCategoryByApplicationId and then say:
Application.Category = RetrievedFromDbCategory;
But this seems a bit unmaintainable compared to the previous behaviour. Why does this happen now and can I do something about it? Otherwise I don't see much benefits on using async/await .
I am trying to implement an Identity Server Solution that redirects the user to different Login Views depending on the client application they come from.
To do this, in the AccountController.cs file I have the following method:
private async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl)
{
var isValid = this.interaction.IsValidReturnUrl(returnUrl);
var context = await this.interaction.GetAuthorizationContextAsync(returnUrl);
var schemes = await this.schemeProvider.GetAllSchemesAsync();
return new LoginViewModel
{
AllowRememberLogin = AccountOptions.AllowRememberLogin,
ReturnUrl = returnUrl,
Username = context?.LoginHint
};
}
I have set up a Configuration & Operational DbContexts as per this tutorial from the IdentityServer4 documentation.
Additionally, I have seeded the database with some rows in the Clients & ClientRedirectUris tables.
Presumably, that should be all I need to access the AuthorizationContext from the IIdentityServerInteractionService API, but the method above always returns null, and the isValid variable is always false too.
I have made sure that the returnUrl I am passing in is exactly the same as the redirectUri stored in my database (I am using localhost and running all this locally, if that matters)
Can someone please help? I have no idea what I'm doing wrong...
You have to do like this:
Html.BeginForm("Login", "Account", new {ReturnUrl = Request.QueryString["ReturnUrl"] })
[HttpPost]
public async Task<IActionResult> Login(LoginInputModel model, string ReturnUrl) {
...
}
So for example I have a sequelize model as follows:
// MyModel.js
module.exports = (sequelize, DataTypes) => {
const MyModel = sequelize.define('MyModel',{
foo: DataTypes.STRING,
});
return MyModel
}
And then I use this model to express templating engine using controller as follows:
app.get('/foobar',async function(req,res,next){
var myModel = await MyModel.findById('abcd1234');
myModel.bar = bar
return res.render('foobar',{
myModel: myModel
});
})
and my foobar.pug is like this:
html
#{JSON.stringify(myModel)}
Apparently I can find the field called foo, but I can't get the field called bar, how do I pass this additional calculated field from my controller through my model?
Reason behind this is :
var myModel = await MyModel.findById('abcd1234');
// will return an instance of MyModel not json
// So you can't just do
myModel.bar = bar;
To make it happen ( Convert instance to JSON Object )
var myModel = await MyModel.findById('abcd1234').toJSON();
// Now this will return the JSON object and not instance of MyModel
// Now this will work
myModel.bar = bar;
toJSON() is sequelizejs's model's method , you can convert it via javascript function also.
If you want to retain the sequelize object , retain it in different variable
var myModelInstance = await MyModel.findById('abcd1234');
var myModel = myModelInstance.get({plain: true});
// OR
var myModel = myModelInstance.toJSON();
// Now this will work
myModel.bar = bar;
These are the possible ways of doing , but because of lack of
requirement and code this is the best I can suggest , you can still
look into GETTER , if you need the extra fields inside
instance.
For anyone looking for this in 2K22 and after :D,
you can use DataType.VIRTUAL when registering a field,
this allow predefining additional fields that will be used and keep it at serialize time but not persist into database.
Using .net core 1.1 mvc
The objective is to restrict users so that they may only edit themselves, or allow admin to edit user. I need to get the current user and compare that user's id with the user id passed in an an argument (or cookie).
If the user ids don't match, check if the current user is an admin. If so, grab the user of the user id passed in as an argument (or cookie).
We have a controller that receives ApplicationDBContext and UserManager in the constructor via Dependency Injection.
I failed with both of these attempts:
1) I tried creating an ActionFilter which accessed UserManager through context.Controller.UserManager (a property I set from the controller constructor) but UserManager had an IDisposable error.
2) I tried doing this from the Controller constructor but I need to set the response to 404 if the user is not found and didnt know how to do that from outside of the Action method.
After a day of trial and error I figured it out and wanted to share since I could not find any examples online.
Note that I had to use IAsyncActionFilter because code execution would begin inside the Action method as soon as I called an async method in the filter (Controller.UserToEditProp = await UserManager.FindByIdAsync)
public class ImpersonateAttribute : ActionFilterAttribute, IAsyncActionFilter
{
public override async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
string ThisUserID = context.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
ApiController Controller = (ApiController)context.Controller;
var UserManager = Controller.UserManager;
if (context.ActionArguments.ContainsKey("UserID"))
{
string RequestedUserID = context.ActionArguments["UserID"].ToString();
if (ThisUserID != RequestedUserID)
{
if (!context.HttpContext.User.IsInRole(UserType.Admin.GetDisplayName()))
{
context.Result = new UnauthorizedResult();
}
}
Controller.UserToEditProp = await UserManager.FindByIdAsync(RequestedUserID);
}
else
{
Controller.UserToEditProp = await UserManager.FindByIdAsync(ThisUserID);
}
await next();
// do something after the action executes
}
}