piotrek-k / generator-dotnetcore-angular2-starterkit

Yeoman generator for ASP.net Core Angular 2 TypeScript applications
Other
5 stars 3 forks source link

How to access Identity on controller calls? #7

Closed Shawski closed 7 years ago

Shawski commented 7 years ago

I can register a user and login. It shows up on the client app. However, I need to be able to check for Claims and the user's identity from within the controller calls.

I tried checking User.Identity, but that is null even once logged in on the client.

Thanks in advance! This is driving me crazy...

piotrek-k commented 7 years ago

You've provided very little information, so I can only guess what's the problem. I've checked how it behaves in one of my applications that use this generator. Here is how it works:

Firstly, I specify that I want to use OpenIddictUserManager in class constructor:

public class ExtendedController : Controller
{
        public AppDbContext _db;
        protected readonly OpenIddictUserManager<ApplicationUser> _userManager;

        public ExtendedController(AppDbContext context, OpenIddictUserManager<ApplicationUser> userManager)
        {
            _db = context;
            _userManager = userManager;
        }

        [...]
        [some other code here]
}

then, in the same class, I create such function:

public ApplicationUser GetCurrentUser()
{
      return _userManager.Users.FirstOrDefault(x => x.NormalizedUserName == User.Identity.Name);
}

Which normally return ApplicationUser object, so basically what you need.

This is all I can help you, if you have any more detailed questions try asking here in OpenIddict repository (library I use for authentication in this generator). And don't forget to write here solution, if you find it. I'm learning to use OpenIddict as you are, so I would be happy to get any useful information.

Shawski commented 7 years ago

Thank you for your fast response! I added your code above. The GetCurrentUser() function above doesn't work for me.

The FirstOrDefault() portion of the call throws an error.

Severity Code Description Project File Line Suppression State Error CS1061 'IQueryable' does not contain a definition for 'FirstOrDefault' and no extension method 'FirstOrDefault' accepting a first argument of type 'IQueryable' could be found (are you missing a using directive or an assembly reference?)

I worked around this by trying:

Task<ApplicationUser> appTask = _userManager.FindByNameAsync(User.Identity.Name);
appTask.Wait();

return appTask.Result;

However, I worry that some of the building blocks between the code you referenced above and the sample project don't match.

I ran the sample app and logged in. The client app navmenu showed me logged in.

My code replacement above blows up on User.Identity.Name since Nameis null.

User.Identity is not null, but it is showing the user as not authenticated. See the results below:

{System.Security.Claims.ClaimsIdentity} Actor: null AuthenticationType: null BootstrapContext: null Claims: {System.Security.Claims.ClaimsIdentity.d__35} CustomSerializationData: null IsAuthenticated: false Label: null Name: null NameClaimType: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" RoleClaimType: "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"

I would respectfully ask if we can continue on this forum since OpenIddict is a component of the template project, but the project doesn't have an example of identifying the user server-side. This could help a lot of others--or may end up being in the template in the future. It is difficult to find anything for this architecture out there.

piotrek-k commented 7 years ago

@Shawski, being honest I have no idea how to help you. Here is what I would check if I had such problem:

Check this part of code (if you use template 2) LINK. Exchange function is where most log in process is being done. I recommend you to check if all data are correctly passed to this function while calling api/Account/Login. Maybe token is created incorrectly and that's why you have null Name.

If token is correct, then it is something wrong with OpenIddict configuration. As you see here there aren't many things I could mess up, but maybe I didn't notice something. OpenIddict is, in my opinion, documented very badly, so it is possible that I've done something that author didn't predicted ;)

Although it works fine in my apps, there is possibility that this is OpenIddict issue. Template2 uses currently alpha2 version, so quite old one. I'm currently working on update to my generator, but it will take some time (everything is changing very quickly).

Template 2 is basically created based on this template JavaScriptServices with OpenIddict included. If you don't want to wait until I recreate this generator to update all packages to latest releases, you can create it by yourself.

I'm sorry, this answer probably doesn't satisfy you, but I currently can't think of any reason why it doesn't work.

Shawski commented 7 years ago

The server-side creation of Identity works when the person logs in. Identity.Name is assigned correctly.

I think the problem lies on the Angular side. In fetchdata.component.ts, the constructor uses the built-in @angular/http service for the call to the server. Can you share the code from one of your working apps you mentioned? It would seem like there would need to be a special http service that would inject the token into the header.

Once again, your help is appreciated more than you know. Thank you!

piotrek-k commented 7 years ago

@Shawski check out this repo: piotrek-k/JavaScriptServices; this is fork of aspnet/JavaScriptServices, but I began working there on authentication using tokens. It is much newer solution, not yet completed, but should work. In future I plan to copy-paste what I've finished there to this dotnetcore-angular2-starterkit generator. Now I have some more experience in angular+c# so maybe I've done some parts better.

Especially, look at this template (Angular2Spa), you can open it in Visual Studio as normal c# project. Take a look at storiesAuth (so, controller that requires authetication to use it).

This is how I communicate with backend using Angular This is how I use this service to add some data This is how I do it at c#-side

I hope that will help.

Shawski commented 7 years ago

Perfect! That is what I needed -- JavaScriptServices/templates/Angular2Spa/ClientApp.

I wired up the ApiMethods.ts and followed the example in the storiesAuth.component.ts and storiesAuth.service.ts. Simply adding this one module and using the approach from the storiesAuth gets the round-trip working within this generator-dotnetcore-angular2-starterkit project.

Now, on the server-side during the call to the controller, I can then get the Identity like this:

        public ApplicationUser GetCurrentUser()
        {
            if (User.Identity.Name != null) { 
            Task<ApplicationUser> appTask = _userManager.FindByNameAsync(User.Identity.Name);
            appTask.Wait();

            return appTask.Result;
        }
        else
        {
        return null;
        }

I would love to send you an Amazon gift card as a thank you. I am trying to learn Angular 2 and am updating my knowledge of NET to NET.CORE from the old ASP days. Quite a learning curve as I am already committed to doing a project with this architecture. Please send your email to me at alan.shaw@fileworks.com--I wish they had a Donate button on GitHub.

piotrek-k commented 7 years ago

@Shawski I'm glad I could help. I don't want any gifts, good luck in your projects 😄