robisim74 / AngularSPAWebAPI

Angular Single Page Application with an ASP.NET Core Web API that uses token authentication
MIT License
231 stars 59 forks source link

Signin - Custom Check before continue with the authorization #11

Closed aalmajanob closed 7 years ago

aalmajanob commented 7 years ago

Hello Roberto,

First of all thanks for your contribution! I find its code really great and clean.

I am developing an app which follows your authentication and spa design approach. It needs to perform some custom checks once the user starts the signin proccess in order to know if its user email has not been deleted/blocked from the organization Active Directory in order to let him authorize via /connect/token or not.

Where do you consider I should better place this custom bussiness-logic layer?

My first approach has been to create a new backend API Controller which takes the user signin decision, so I could consume it from the client (signin.ts), before calling the 'this.authenticationService.signin(...)' . So if the API Controller did find the user within the Active Directory and it has not been blocked, the authentication will continue. At the same time, if the users email has been deleted/blocked I will remove it also from the Identity database in order to guarantee he wont be able to access anymore.

The problem is that I find this approach easily hackeable just by using the developer tools. Is there any way I could centralize the sigin token login within the backend to make it more secure?

Thanks really much for your time, Andrés.

robisim74 commented 7 years ago

Hi Andrés,

I think you could use Resource Owner Password Validation: http://docs.identityserver.io/en/release/topics/resource_owner.html and its GrantValidationResult. Unfortunately there is not much documentation about it. Try to see also this question, that contains some example of its use: http://stackoverflow.com/questions/35304038/identityserver4-register-userservice-and-get-users-from-database-in-asp-net-core. In theory, in this way, you could give up calling your service from the client, and call only the token endpoint that will contain the validation logic you decide.

Greetings, Roberto

aalmajanob commented 7 years ago

Dear Roberto,

ResourceOwnerPasswordValidator and ProfileServer finally did the trick!

All I needed to completely customize the authorization flow from the backend was to inject such services at the startup.cs:

1. Within the services declaration: Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>(); Services.AddTransient<IProfileService, ProfileService>();

2. And after the Identity Server creation "services.AddIdentityServer()": AddProfileService<ProfileService>(); AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();

And I Finally was able to include both classes ResourceOwnerPasswordValidator.cs and ProfileService.cs which let you customize the login authentication(ResourceOwner) and the Claims logic(ProfileService).

Here you can find a good example of both classes: https://github.com/IdentityServer/IdentityServer4/tree/dev/src/IdentityServer4/Test

This way it is possible to include additional checks (like if the user has been blocked within my app or the organization Active Directory) before continue with the login in a secure way.

Thanks very much for your tips, Andrés.

robisim74 commented 7 years ago

Hi Andrés,

I'm glad to know it! And thanks for sharing the solution. Actually, it's a scenario that can often occur: I think I could add it as an option to this project.

Greetings, Roberto

aalmajanob commented 7 years ago

Hello Roberto,

Sure It would be great if you include such option, at least commented. The only thing you need to take into account is that once you add the custom ResourceOwnerValidator its default behaviour (such as failed login attemps etc.. ) will be overriden with the custom new code so It is required to manually codify this logic by using the different UserManager methods.

Let me know if I could help you with my already developed code.

Regards, Andrés.