dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.19k stars 9.93k forks source link

Asp.Net Core identity with source generators #41125

Closed ziaulhasanhamim closed 2 years ago

ziaulhasanhamim commented 2 years ago

Is there an existing issue for this?

Is your feature request related to a problem? Please describe the problem.

Almost every web app needs some kind of authentication. And it requires quite a lot of code if we want to do it from scratch. Asp.Net Core Identity Simplifies this process a lot. But the main disadvantage is that authentication is very different from app to app. It depends on the DB provider, the auth type(session, jwt), the user model, user identifier(email, phone number, username, etc), and many other stuff.

Describe the solution you'd like

So this authentication part is a great candidate for source generators. Identity Service and Authentication Entities can be generated according to users' needs. So people will get every feature of identity with full customizability without writing much code

Additional context

No response

davidfowl commented 2 years ago

Can you elaborate? What would the source generator do and how would it work?

ziaulhasanhamim commented 2 years ago

Services, Db Provider, Entity Validation, etc will be generated according to needs.

public partial class User
{
  [Key]
  public Guid Id { get; set; }

  [PhoneNumberField, UserIdentifier]
  public string PhoneNumber { get; set; } // will be used for login and indexing on database because of UserIdentifier attribute

  [UniqueField, StoreInClaims]
  public string Username { get; set; } // will be used for stored in claims bacause of StoreInClaimsattribute

  [ShouldBeTrueBeforeSignin]
  public bool Verified { get; set; }

  [HashedPasswordField]
  public string HashedPassword { get; set; }
}
[IdentityUserManager(UserType = typeof(User))]
[UseEfCore(DbContextType = typeof(AppDbContext))]
public partial class UserManager
{
  // needed methods will be generated
} 
[IdentitySiginManager(AuthType.SimpleJwt, UserManagerType = typeof(UserManager))]
public partial class SigninManager // Here signin logic will be generated for jwt based auth
{
  // needed methods will be generated
}
public partial class AppDbContext : DbContext
{
  // code will be generated for identity models
}

So it becomes very very customizable without writing much code. And this example wasn't that great. But the source generators for authentication can be extended in many ways.

davidfowl commented 2 years ago

But the source generators for authentication can be extended in many ways.

If you can detail a design (even if it's a proof of concept) it would have a better chance at happening. Ideally there would be before and after to understand what was being saved here and a list of pros and cons.

ziaulhasanhamim commented 2 years ago

If you can detail a design (even if it's a proof of concept)

Yeah that's a proof of concept. The basic idea here is that authentication varies a lot from application to application so the identity library will provide a bunch of attributes using which people customize according to their needs. People will only get what they need. Authentication code will be optimized for their specific use cases. Like which data they want to store about users, how much data they want to store in claims, the roles of users, which field be unique to the users, what type of authorization(cookies, webtokens) they want to use, etc

So the code in my last comment should generate code like this

public partial class UserManager // source generated
{
  public async Task<User> FindUserByPhoneNumber(string phoneNumber)
  {
      // code to implement the method
  }

  public async Task<bool> PhoneNumberExists(string phoneNumber)
  {
    // code to implement the method
  }

  public async Task<bool> UsernameExists(string username)
  {
    // code to implement the method
  }

  public string GetPhoneNumber(ClaimsPrincipal principal)
  {
        // code to implement the method
  }

  // create, delete, update methods according to the requirement
} 
public partial class SigninManager // source generated
{
  public bool CanSignIn(User user)
  {
      // Check the fields on the user which are marked with ShouldBeTrueBeforeSignin attribute
  }

  public async Task<SigninResult> PasswordSignin(string phoneNumber, string rawPassword)
  {
      // get the user and authenticate by verifying the password
  }

  // other methods
} 
blowdart commented 2 years ago

While you're right when you state "authentication varies a lot from application to application" I'm unconvinced that attributes and source generators would solve the problem. The problem itself is the variation.

If I can take your examples as a starting point

Source Generators aren't that magic. They would still have to have awareness of the things you want to cater for, and the number of combinations that you've started with already makes a generator bloated, yet fragile.

Then there's the question of whether Identity is the solution we should be recommending as the default for new projects. For most use cases a cloud provider gives a better experience to users, and to developers. Cloud identity providers (and I don't care which one, this isn't about pushing AAD, use who you like) have more features, provide newer security standards like Fido or app based auth, provide better logging and alerting, patch automatically etc.

I acknowledge that there are some folks who want to keep their auth data in their app, they may be constrained by data sovereignty concerns, regulatory requirements or are worried about cost (although the free tiers of pretty much all the cloud auth folks are rather generous to me), but that comes at the cost of having to protect your auth data, having a non-standard login mechanism and having to use other 3rd party systems should you want to start using jwt for mobile apps etc.

ASP.NET Identity isn't going away, but it's never going to have these features.

davidfowl commented 2 years ago

@ziaulhasanhamim I'd encourage you to implement something (preferable OSS on github) and publish a nuget package. That will help gauge interest and feasibility.

ziaulhasanhamim commented 2 years ago

@davidfowl yeah I got your point.

ziaulhasanhamim commented 2 years ago

@blowdart I can understand that even source generation can't even fully solve the problem of customizable authentication. But it can surely improve the customizability.

A single true or false in CanSignIn() isn't enough. One variation could be based on time or IP address. That can't be indicated with attributes.

CanSignin will consider all the factors to give a result on whether the user is eligible for login or not. And don't forget about the configuration we do in DI containers for identity. That will also be available for configuring the extra stuff. Identity options can be used for runtime decisions where you can't do source generation (as you mentioned time, or IP address)

I can understand that source generators are not magical and this idea feels very magical.

I acknowledge that there are some folks who want to keep their auth data in their app,

I think the default option should be to keep your auth data in your app because by default your app is standalone. Then if you want configure a cloud provider you can obviusly do that. There is no problem in that.

blowdart commented 2 years ago

We'll disagree on the default option here :)

I'd agree with David here, try it yourself as a proof of concept and lets were where it goes.

ghost commented 2 years ago

Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue.

This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue!