aksoftware98 / ticketsbasket

TicketsBasket is a full cloud project that represents a tickets marketplace, associated with a YouTube course on AK Academy
27 stars 1 forks source link

Creating users in my db when new user register (in Azure B2C) #4

Open Ematko opened 2 years ago

Ematko commented 2 years ago

Hi, I respond to a comment you sent me (under 23 episode).

I have been solving the problem that I want to automatically send an api request from my client-side (blazor wasm project) (creates a new user in my own db) when users register (cretes new acc in azure B2C).

I search on internet and found this: https://wellsb.com/csharp/aspnet/azure-ad-b2c-onticketreceived-newuser-claim/. Thats exactly what i want to do :D. But in this article they use AzureADB2C.UI (when i install nuget errors occured), so i cant used the events.

Again thx for for immediate response. I appreciate it really much.

aksoftware98 commented 2 years ago

Hi

Sorry for that late reply.

What you can do in this case is to use the AccountClaimsPrincipalFactory I think this is the best solution, after the user logs in to your system automatically this will fetch the claims and then you make a call to your backend if there is a user locally then okay if not you can basically just create a new one, and you can find this in details in the TicketsBasket course in the videos 34-36-36 I have not used that mechanism to create the user automatically but I have used to manage a hybrid role-based authorization with this please check the following https://www.youtube.com/watch?v=JaKlfk5juuI&list=PLFJQnCcZXWjvdIXP6xYstD3MHKYpuxOax&index=36

And read more about this AccountClaimsPrincipalFactory it's basically a mechanism to manipulate the user claims or to do something automatically after the user logs in, which a create utility by MSAL library from Microsoft, to get more about this the previous video I shared explains that in details so instead of dealing with the roles you can basically send a request to check or create the user Here is the Microsoft official documentation: https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/additional-scenarios?view=aspnetcore-5.0#customize-the-user-1

Please feel free to ask any questions regarding this on this thread, I will keep it open until you manage it

Thanks

Ematko commented 2 years ago

Hi,

absolutely no problem. I'm very happy that you help me at all and thank you very much for that.

Finally i got it! xD

But it was little bit complicated then i thought.

this works well.

But in Api CustomIdentityMiddleware

image

Soo now it works. But i got feeling i do it prety bad and ugly way. So please correct me

aksoftware98 commented 2 years ago

That's it, you have done in a perfect, this is all what you need, the code is clear and doing the task very well.

So happy for that.

Stay tuned for the rest of the videos next month.

Thank

Ematko commented 2 years ago

NIce!! :D I would like to ask one more question.

How to do so that during registration the user can enter additional information specified by me (eg He can check If he is Organizer or smth in that way).

In razor pages, when i use external providers (fb, google, ...) after user login via this acc. i redirect him to my "register page" where he can fill u other information specified by me.

But idk how to do it here. Could you point me in or give me some advice.

Again thx very much!

Ematko commented 2 years ago

Hi, some new thinks i discovered!

when user register and immediately want do smth without log out. The claim isNewUser be still true => every call to api i create new same user :D. Soo i thinking about what is best solution for this.

  1. somehow delete isNewUser claim (set him to null or smth) after first api createUser call.
  2. check in db if this user (with same UserId) exist if yes then dont created new and if not created new one (soo i completely dont need isNewUser claim)

Idk what is better. What do u think? @aksoftware98

Ematko commented 2 years ago

Hi again :D,

so I figured out that the second approach is much better and I have more control over it (I don't use the IsNewUser claim at all).

But I found out that when registering a new user (request from client to api "CreateUser") is not possible because CustomIdentityMiddleware catch the request and can not load user from db (wants to detect roles, using "GetUserByUserIdAsync", this method return error if user is not found) -> raised error and send back client error response -> soo it is not possible to register users due to CustomIdentityMiddleware.

So I created a new method that returns directly the user object (not DTO object, bcs null cannot convert to DTO :)) or simply instead of userService I could use the userReository method (that do exactly what i want (Return User or null))

Sorry for spam. I just want to update my proggres so I don't burden you so much and I'm more interested in if it's a generally good idea or whether I'm thinking about it in bad way. (aka if i doing The Best Paractise if u know what i mean :D)

aksoftware98 commented 2 years ago

Sorry for that late reply,

Regarding how to do that in your pages, you can simply check if the user existing in some role if not redirect him to the roles picking page where user can pick a role and update it in the database but that requires the user to logout and login again, this issue will be solved when I get back to my YouTube channel, one quick solution for this, is to give the user a default role and then user can upgrade for another one through a specific page, for example in case of TicketsBasket we can give him a default role as a normal user, and then within the system user can choose if he wants to be an organizer.

The best solution regarding your second question is to remove the claim after creating user request processed successfully because no need to that on the server every time you are trying to do any operation it's fully not needed, while you can save a big amount of requests and any way the user will be logged-out at the end and the value will be changed and regarding the request on the server you can improve it by making it check if there is already a user existing before creating the new, if it's exists just return it. this will prevent the issue from happening again

For the third point, just simply in the middleware write a check if the result is null don't add a new role just go ahead, because user can just continue normally until he picks a role.

Keep me updated please

Ematko commented 2 years ago

no problem,

y the first 2 thinks u worte i allready does :).

the 3 point, is litle bit complicated (the CustomIdentityMiddleware & creatingNewUser request).

  1. in client user register
  2. client send request to get logged user from db -> api respon is IsSuccessfull = false "user not found"
  3. client send request to create new user -> CustomIdentityMiddleware catch request and calls "GetUserByUserIdAsync" image -> return error (user not found) and automaticali send response (user not found) to client. (and the method in middleware is never finished (the if with user != null be never checked, bcs error was thrown before)

Idk if i miss understand smthing if u know what i mean, but this behavior seems logical to me (we programed this way)

aksoftware98 commented 2 years ago

Ah okay, I understood now, the issue is that the GetUserById is throwing error if the user is not found.

Okay modify the GetUserByUserAsync to not throw an exception and return Null instead but here you have also to modify the controller that calls the method by checking if the user is null return NotFound(); instead of return Ok();

Ematko commented 2 years ago

yeah that's what I thought :D.

So I created a new method (in UserServices) that returns directly the user object/null (not DTO object, bcs null cannot convert to DTO :)) or simply instead of userService I could use the userReository method "GetUserByUserId" (that do exactly what i want (Return User or null)). image

but idk if its correct mix middleware and repository together, our just using services, all the time.

aksoftware98 commented 2 years ago

You approche is right in general but you can make it return dto just like this

var user = await _db.UserProfiles.SingleOrDefaultAsync(up => up.UserId == userId);

return user == null ? null : user.ToDto.......;
Ematko commented 2 years ago

Hi, its me again :D

Just for info I successfully finished everything as I wanted. But recently I came across Azure functons at work. Soo i started learning them (some courses on Udemy etc.). But I came up with a lot of questions that I can't find the answers to. And I would like to ask you about some of them, or consult.

  1. Because I really like Azure Static web apps service. I now mean full-stack web apps development. Specifically Blazor WASM + Rest Api (c# .net 5)+ SQL db. But on SWA u cant deploy classic Rest api (like web app in .Net Core) instead it's only with Azure functions app.
    • I went through the course at microsoft learn this one (https://docs.microsoft.com/en-us/learn/modules/publish-app-service-static-web-app-api-dotnet/). So the first thing that occurred to me was if it is possible to redo the classic rest api (web app) into azure functions?
    • And if azure functions are suitable to it at all. For example, × I thought auth. middleware (finding out the user's role, or how to find out the currently logged in user? etc.). For example, my Rest api is very large (dozens of gets, multiple posts and so on), that means dozens of azure functions. ×Or how to smth biggers actions in these methods, for example, I have a planning algorithm that creates optimal time plans for users based on their time options in a given week. Thats takes dozen of relatively large subtasks, such as getting users from and their plan from db, then filtering them by parameter, then going through several cycles and so on (and it requires having a steady state through all these sub-steps in the application). And I can't imagine doing that in Azure functions. Bcs theyir targeting to small operations, like get user from db etc .. So I just think that complex resins in azure functions can't do much or I'm wrong and I just have the wrong view?

I would just like to know if Azure functions are going to do real world rest apis, with more complex business logic.

aksoftware98 commented 2 years ago

Hi

Sorry for late reply, you have a very interesting question, actually for me I'm totally focusing now into developing a full apps using Azure Functions, they have all the power to develop a full application even much better than an ASP.NET Core back-end for many reasons, that I will mention them in a upcoming video.

Regarding how to know the logged in user you can do the same as you ASP.NET Core API, from your request object you can the token from the header and then decode it and have access for all the data, please checkout this Azure Functions I have built in the following link I'm building a sample app frequently that is a full Azure Functions back-end: https://github.com/aksoftware98/cloudness-marketplace // I will implement the user retrieving very soon

The same app will be a big sample of how to achieve exactly what you are trying to do so. then will start a series of videos about that.

Thanks for your patience.