6

L'esempio su AspNet.Security.OpenIdConnect.Server mi sembra sia un server di autenticazione che di risorse. Mi piacerebbe separare quelli. L'ho fatto.Separazione di server di autenticazione e risorse con AspNet.Security.OpenIdConnect: il pubblico?

Al Startup.Config del server di autenticazione, ho le seguenti impostazioni:

app.UseOpenIdConnectServer(options => { 

    options.AllowInsecureHttp = true; 
    options.ApplicationCanDisplayErrors = true; 
    options.AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme; 
    options.Issuer = new System.Uri("http://localhost:61854"); // This auth server 
    options.Provider = new AuthorizationProvider(); 
    options.TokenEndpointPath = new PathString("/token");    
    options.UseCertificate(new X509Certificate2(env.ApplicationBasePath + "\\mycertificate.pfx","mycertificate")); 

}); 

Ho un AuthorizationProvider scritte, ma non credo che sia rilevante per il mio numero attuale (ma possibilmente rilevante). Alla sua esclusione GrantResourceOwnerCredentials, ho hard-code un affermazioni principale in modo che convalida per ogni richiesta di token:

public override Task GrantResourceOwnerCredentials(GrantResourceOwnerCredentialsNotification context) 
{ 
    var identity = new ClaimsIdentity(OpenIdConnectDefaults.AuthenticationScheme); 

    identity.AddClaim(ClaimTypes.Name, "me"); 
    identity.AddClaim(ClaimTypes.Email, "[email protected]"); 
    var claimsPrincipal = new ClaimsPrincipal(identity); 

    context.Validated(claimsPrincipal); 
    return Task.FromResult<object>(null); 
} 

Sul server risorsa, ho il seguente nella sua Startup.config:

app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")), branch => 
{ 
    branch.UseOAuthBearerAuthentication(options => { 
     options.Audience = "http://localhost:54408"; // This resource server, I believe. 
     options.Authority = "http://localhost:61854"; // The auth server 
     options.AutomaticAuthentication = true;    
    }); 
}); 

su Fiddler, chiedo per un token, e ottengo uno:

POST /token HTTP/1.1 
Host: localhost:61854 
Content-Type: application/x-www-form-urlencoded 

username=admin&password=aaa000&grant_type=password 

Quindi, ora che io uso token di accesso per accedere a una risorsa protetta dal server di risorsa:

GET /api/values HTTP/1.1 
Host: localhost:54408 
Content-Type: application/json;charset=utf-8 
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI..... 

Ora ottengo questo errore - Convalida del pubblico non riuscita. Pubblico: "vuoto". Non corrispondeva a validationParameters.ValidAudience: 'http://localhost:54408' o validationParameters.ValidAudiences: 'null'.

Penso che il motivo sia perché non ho mai impostato un pubblico sul server di autenticazione (su app.UseOpenIdConnectServer (...)), quindi non penso abbia scritto le informazioni sul pubblico al token. Quindi ho bisogno di impostare un pubblico sul server di autenticazione (come quello che viene fatto in IdentityServer3), ma non riesco a trovare una proprietà sull'oggetto delle opzioni che mi consenta di farlo.

AspNet.Security.OpenIdConnect.Server richiede che l'autenticazione e la risorsa siano nello stesso server?

Sta impostando l'audience quando si mette insieme ClaimsPrincipal e, in caso affermativo, come?

Devo scrivere un validatore di pubblico personalizzato e collegarlo al sistema? (Spero che la risposta a questo sia no)

risposta

5

AspNet.Security.OpenIdConnect.Server richiede che l'autenticazione e la risorsa siano nello stesso server?

No, puoi ovviamente separare i due ruoli.

Come hai già capito, se non lo specifichi esplicitamente, il server di autorizzazione non ha modo di determinare la destinazione/il pubblico di un token di accesso, che viene emesso senza il reclamo aud richiesto per impostazione predefinita dal Middleware al portatore OAuth2.

Risolvere questo problema è semplice: basta chiamare il ticket.SetResources(resources) quando si crea il ticket di autenticazione e il server di autorizzazione saprà esattamente quale valore (i.e resource server/API) deve aggiungere nelle richieste aud.

app.UseOpenIdConnectServer(options => 
{ 
    // Force the OpenID Connect server middleware to use JWT tokens 
    // instead of the default opaque/encrypted token format used by default. 
    options.AccessTokenHandler = new JwtSecurityTokenHandler(); 
}); 

public override Task HandleTokenRequest(HandleTokenRequestContext context) 
{ 
    if (context.Request.IsPasswordGrantType()) 
    { 
     var identity = new ClaimsIdentity(context.Options.AuthenticationScheme); 
     identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "unique identifier"); 

     var ticket = new AuthenticationTicket(
      new ClaimsPrincipal(identity), 
      new AuthenticationProperties(), 
      context.Options.AuthenticationScheme); 

     // Call SetResources with the list of resource servers 
     // the access token should be issued for. 
     ticket.SetResources("resource_server_1"); 

     // Call SetScopes with the list of scopes you want to grant. 
     ticket.SetScopes("profile", "offline_access"); 

     context.Validate(ticket); 
    } 

    return Task.FromResult(0); 
}  

app.UseJwtBearerAuthentication(new JwtBearerOptions 
{ 
    AutomaticAuthenticate = true, 
    AutomaticChallenge = true, 
    Audience = "resource_server_1", 
    Authority = "http://localhost:61854" 
}); 
+0

Grazie mille! Ora funziona! Ho usato l'opzione 1 perché potrei avere più di un pubblico in grado di richiedere i token di accesso. –

+0

@MickaelCaruso Ho aggiornato la mia risposta per utilizzare 'ticket.SetResources' in quanto il parametro' resource' non sarà più supportato nativamente nella prossima beta (consultare https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect. Server/issues/186 per ulteriori informazioni). Smetteremo inoltre di utilizzare JWT come formato predefinito per i token di accesso: https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/issues/186 – Pinpoint

+0

Grazie per la tua risposta, mi ha davvero aiutato. Il metodo GrantResourceOwnerCredentials è praticamente lo stesso, ma ho anche aggiunto l'identità identity.AddClaim (ClaimTypes.Name, "MyFullName"); Ma quando chiamo User.GetUserName() nel mio controller web api ottengo NULL? User.GetUserId() restituisce ciò che viene passato nella rivendicazione NameIdentifier. Come posso ottenere User.GetUserName per restituire i dati? – partyelite