6

ho sviluppato un meccanismo di autenticazione in Asp.Net Web Api 2 con la funzione per la concessione di aggiornamento gettoni, sulla base del tutorial su Taiseer's blog.Aggiornamento Ruoli nel concedere token di aggiornamento in Api Web 2

Ecco la mia domanda. Si supponga il seguente scenario: Un utente accede utilizzando la password e ottiene un token di aggiornamento e un token di accesso. Il token di accesso in realtà include i ruoli in cui si trova (quindi le sue autorità all'interno dell'app). Nel frattempo l'amministratore di sistema cambierà i ruoli di questa persona, quindi una volta che il suo token di accesso scade e desidera utilizzare il token di aggiornamento per ottenere un nuovo token di accesso, il suo nuovo token di accesso deve includere i ruoli appena aggiornati per lui.

Nella mia classe "RefreshTokenProvider", io sto usando il seguente codice nel metodo "GrantResourceOwnerCredentials" per ottenere i ruoli utente dal database e aggiungerli alle rivendicazioni:

var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext())); 
     var y = roleManager.Roles.ToList(); 

     var id = new ClaimsIdentity(context.Options.AuthenticationType); 
     id.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); 
     id.AddClaim(new Claim("sub", context.UserName)); 

     var roles2 = UserRoleManagerProvider.RoleManager().Roles.ToList(); 

     foreach (IdentityRole i in roles2) 
     { 
      if (roleIds.Contains(i.Id)) 
       id.AddClaim(new Claim(ClaimTypes.Role, i.Name)); 
     } 

Questo pezzo funziona bene (anche anche se credo che dovrebbe esserci un modo migliore per farlo ?!)

Ma la parte che non funziona correttamente è nel metodo "GrantRefreshToken", dove dobbiamo aggiornare i ruoli per rispecchiarli nel nuovo accesso gettone:

var newId = new ClaimsIdentity(context.Ticket.Identity); 

     // *** Add shit here.... 

     var userId = context.Ticket.Properties.Dictionary["userId"]; 
     IdentityUser user = UserRoleManagerProvider.UserManager().FindById(userId); 

     foreach (Claim c in newId.Claims) 
     { 
      if (c.Type == ClaimTypes.Role) newId.RemoveClaim(c); 
     } 

     if (user.Roles.Count > 0) 
     { 
      var roleIds = new List<string>(); 
      var roles2 = UserRoleManagerProvider.RoleManager().Roles.ToList(); 
      foreach (IdentityUserRole ir in user.Roles) 
      { 
       roleIds.Add(ir.RoleId); 
      } 
      foreach (IdentityRole r in roles2) 
      { 
       if (roleIds.Contains(r.Id)) 
        newId.AddClaim(new Claim(ClaimTypes.Role, r.Name)); 
      } 
     } 

Ancora, se c'è un modo migliore per farlo, apprezzerei l'aiuto del tuo ragazzo! Ma principalmente, il mio problema è che la parte per rimuovere i ruoli che non sono più in vigore, non funziona. Per caso sai cosa c'è di sbagliato in quel pezzo ?!

FYI, nel codice di sopra della "UserRoleManagerProvider" è una classe statica semplice che ho creato, che è come questo:

public static class UserRoleManagerProvider 
{ 
    public static RoleManager<IdentityRole> RoleManager() 
    { 
     var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext())); 
     return roleManager; 
    } 

    public static UserManager<IdentityUser> UserManager() 
    { 
     var userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(new ApplicationDbContext())); 
     return userManager; 
    } 


} 
+0

Hai mai trovato una soluzione e sei ancora interessato se non? –

+0

@RuardvanElburg Apprezzerei comunque una buona risposta :) –

risposta

2

E 'difficile rispondere a questa domanda, e dal momento che c'è un sacco che ha bisogno per essere incluso, ho cercato di separare alcuni problemi.

reclami

Ci sono due modi per aggiungere crediti al ClaimsIdentity.

  1. Rivendicazioni persistenti nel negozio (nel database le tabelle AspNetUserClaims, AspNetRoleClaims). Per aggiungere reclami utilizzare UserManager.AddClaim o RoleManager.AddClaim. I ruoli (AspNetUserRoles) sono speciali, poiché sono anche conteggiati come attestazioni.
  2. Aggiungi reclami nel codice. È possibile aggiungere attestazioni dalla classe ApplicationUser (utile per le proprietà estese di IdentityUser) o nel flusso.

Si prega di notare la differenza! Mentre in tutti i casi si chiama AddClaim, la prima variante aggiunge le rivendicazioni allo store, mentre la seconda variante aggiunge le attestazioni direttamente a ClaimsIdentity.

Quindi, come vengono aggiunte le attestazioni persistenti a ClaimsIdentity? Questo è fatto automaticamente!

Come nota a margine, è possibile estendere IdentityUser con le proprietà, ma è anche possibile aggiungere rivendicazioni utente allo store. In entrambi i casi il reclamo verrà aggiunto a ClaimsIdentity. La proprietà estesa deve essere aggiunta in ApplicationUser.GenerateUserIdentityAsync:

public class ApplicationUser : IdentityUser 
{ 
    public string DisplayName { get; set; } 

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager) 
    { 
     var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie); 

     // Add custom user claims here 
     userIdentity.AddClaim(new Claim("DisplayName", DisplayName)); 

     return userIdentity; 
    } 
} 

flusso

Prima di rilasciare un nuovo access_token il server deve convalidare l'utente. Potrebbero esserci motivi per cui il server non può emettere un nuovo access_token. Anche la configurazione modificata deve essere presa in considerazione. Esistono due provider per questa configurazione. Il provider access_token e il provider refresh_token.

Quando un client effettua una richiesta all'endpoint token (grant_type = *), AccessTokenProvider.ValidateClientAuthentication viene eseguito per primo. Se stai usando client_credentials, puoi fare qualcosa qui. Ma per il flusso corrente assumiamo context.Validated();

Il provider supporta vari flussi. È possibile leggere a questo proposito qui: https://msdn.microsoft.com/en-us/library/microsoft.owin.security.oauth.oauthauthorizationserverprovider(v=vs.113).aspx

Il provider è stato creato come opt-in. Se non si esegue l'override dei metodi determinati, l'accesso viene negato.

token di accesso

Per ottenere un token di accesso, le credenziali devono essere inviati. Per questo esempio assumerò 'grant_type = password'. In AccessTokenProvider.GrantResourceOwnerCredentials vengono verificate le credenziali, ClaimsIdentity è configurato e viene emesso un token.

Per aggiungere un refresh_token al ticket, è necessario eseguire l'override di AccessTokenProvider.GrantRefreshToken. Qui hai due opzioni: respingere il token. Poiché il refresh_token è stato revocato o per un altro motivo per cui l'utente non può più utilizzare il token di aggiornamento. Oppure imposta un nuovo ClaimsIdentity per generare un nuovo access_token per il ticket.

class AccessTokenProvider : OAuthAuthorizationServerProvider 
{ 
    public override async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context) 
    { 
     // Reject token: context.Rejected(); Or: 

     // chance to change authentication ticket for refresh token requests 
     var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); 
     var appUser = await userManager.FindByNameAsync(context.Ticket.Identity.Name); 
     var oAuthIdentity = await appUser.GenerateUserIdentityAsync(userManager); 
     var newTicket = new AuthenticationTicket(oAuthIdentity, context.Ticket.Properties); 

     context.Validated(newTicket); 
    } 

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
    { 
     var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); 
     var appUser = await userManager.FindAsync(context.UserName, context.Password); 
     if (appUser == null) 
     { 
      context.SetError("invalid_grant", "The user name or password is incorrect."); 
      return; 
     } 

     var propertyDictionary = new Dictionary<string, string> { { "userName", appUser.UserName } }; 
     var properties = new AuthenticationProperties(propertyDictionary); 

     var oAuthIdentity = await appUser.GenerateUserIdentityAsync(userManager); 
     var ticket = new AuthenticationTicket(oAuthIdentity, properties); 

     // Token is validated. 
     context.Validated(ticket); 
    } 
} 

Se il contesto ha un ticket convalidato, viene chiamato il RefreshTokenProvider. Nel metodo Create è possibile impostare il tempo di scadenza e scegliere di aggiungere il token di aggiornamento al ticket. Non emettere nuovi token mentre quello attuale non è ancora scaduto. In caso contrario, l'utente potrebbe non dover mai più effettuare il login!

È sempre possibile aggiungere il refresh_token se è in qualche modo persistente. Oppure puoi aggiungere un nuovo refresh_token solo al login. L'utente è identificato in modo che il 'vecchio' refresh_token non abbia più importanza dal momento che scadrà prima che il nuovo refresh_token faccia. Se si desidera utilizzare solo un refesh_token attivo, è necessario mantenerlo.

class RefreshTokenProvider : AuthenticationTokenProvider 
{ 
    public override void Create(AuthenticationTokenCreateContext context) 
    { 
     var form = context.Request.ReadFormAsync().Result; 
     var grantType = form.GetValues("grant_type"); 

     // do not issue a new refresh_token if a refresh_token was used. 
     if (grantType[0] != "refresh_token") 
     { 
      // 35 days. 
      int expire = 35 * 24 * 60 * 60; 
      context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddSeconds(expire)); 
      // Add the refresh_token to the ticket. 
      context.SetToken(context.SerializeTicket()); 
     } 
     base.Create(context); 
    } 

    public override void Receive(AuthenticationTokenReceiveContext context) 
    { 
     context.DeserializeTicket(context.Token); 
     base.Receive(context); 
    } 
} 

Questa è solo una semplice implementazione del flusso refresh_token e non completa né testata. È solo per darvi alcune idee sull'implementazione del flusso refresh_token. Come puoi vedere, non è difficile aggiungere attestazioni a ClaimsIdentity. Non ho aggiunto codice in cui vengono mantenute le dichiarazioni persistenti. Tutto quello che importa è che le richieste persistenti sono automaticamente aggiunto!

Si prega di notare che ho ripristinato il ClaimsIdentity (nuovo ticket) su aggiornamento del token di accesso utilizzando il refresh_token. Ciò creerà un nuovo ClaimsIdentity con lo stato corrente delle attestazioni.

Terminerò con un'ultima osservazione. Stavo parlando dei ruoli che vengono rivendicati. È probabile che User.IsInRole controlli la tabella AspNetUserRoles. Ma non è così. Poiché i ruoli sono rivendicazioni, controlla la raccolta sinistri per i ruoli disponibili.