[c#] Konfigurieren Sie den Endpunkt des Autorisierungsservers



0 Answers

Mit Hilfe von @ Pinpoint haben wir die Grundlagen einer Antwort zusammengefügt. Es zeigt, wie die Komponenten miteinander verdrahten, ohne eine vollständige Lösung zu sein.

Fiedler Demo

Mit unserem rudimentären Projektaufbau konnten wir in Fiddler folgende Anfrage und Antwort geben.

Anfordern

POST http://localhost:50000/connect/token HTTP/1.1
User-Agent: Fiddler
Host: localhost:50000
Content-Length: 61
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=my_username&password=my_password

Antwort

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 1687
Content-Type: application/json;charset=UTF-8
Expires: -1
X-Powered-By: ASP.NET
Date: Tue, 16 Jun 2015 01:24:42 GMT

{
  "access_token" : "eyJ0eXAiOi ... 5UVACg",
  "expires_in" : 3600,
  "token_type" : "bearer"
}

Die Antwort enthält ein Bearer-Token, mit dem wir Zugriff auf den sicheren Teil der App erhalten.

Projektstruktur

Dies ist die Struktur unseres Projekts in Visual Studio. Wir mussten seine Properties > Debug > Port auf 50000 damit es als Identitätsserver fungiert, den wir konfiguriert haben. Hier sind die relevanten Dateien:

ResourceOwnerPasswordFlow
    Providers
        AuthorizationProvider.cs
    project.json
    Startup.cs

Startup.cs

Zur besseren Lesbarkeit habe ich die Startup Klasse in zwei Teiltöne aufgeteilt.

Startup.ConfigureServices

Für die Grundlagen benötigen wir nur AddAuthentication() .

public partial class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication();
    }
}

Startup.Configure

public partial class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
        JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();

        // Add a new middleware validating access tokens issued by the server.
        app.UseJwtBearerAuthentication(new JwtBearerOptions
        {
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
            Audience = "resource_server_1",
            Authority = "http://localhost:50000/",
            RequireHttpsMetadata = false
        });

        // Add a new middleware issuing tokens.
        app.UseOpenIdConnectServer(options =>
        {
            // Disable the HTTPS requirement.
            options.AllowInsecureHttp = true;

            // Enable the token endpoint.
            options.TokenEndpointPath = "/connect/token";

            options.Provider = new AuthorizationProvider();

            // Force the OpenID Connect server middleware to use JWT
            // instead of the default opaque/encrypted format.
            options.AccessTokenHandler = new JwtSecurityTokenHandler
            {
                InboundClaimTypeMap = new Dictionary<string, string>(),
                OutboundClaimTypeMap = new Dictionary<string, string>()
            };

            // Register an ephemeral signing key, used to protect the JWT tokens.
            // On production, you'd likely prefer using a signing certificate.
            options.SigningCredentials.AddEphemeralKey();
        });

        app.UseMvc();

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    }
}

AutorisierungsProvider.cs

public sealed class AuthorizationProvider : OpenIdConnectServerProvider
{
    public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
    {
        // Reject the token requests that don't use
        // grant_type=password or grant_type=refresh_token.
        if (!context.Request.IsPasswordGrantType() &&
            !context.Request.IsRefreshTokenGrantType())
        {
            context.Reject(
                error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
                description: "Only grant_type=password and refresh_token " +
                             "requests are accepted by this server.");

            return Task.FromResult(0);
        }

        // Since there's only one application and since it's a public client
        // (i.e a client that cannot keep its credentials private), call Skip()
        // to inform the server that the request should be accepted without 
        // enforcing client authentication.
        context.Skip();

        return Task.FromResult(0);
    }

    public override Task HandleTokenRequest(HandleTokenRequestContext context)
    {
        // Only handle grant_type=password token requests and let the
        // OpenID Connect server middleware handle the other grant types.
        if (context.Request.IsPasswordGrantType())
        {
            // Validate the credentials here (e.g using ASP.NET Core Identity).
            // You can call Reject() with an error code/description to reject
            // the request and return a message to the caller.

            var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
            identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "[unique identifier]");

            // By default, claims are not serialized in the access and identity tokens.
            // Use the overload taking a "destinations" parameter to make sure 
            // your claims are correctly serialized in the appropriate tokens.
            identity.AddClaim("urn:customclaim", "value",
                OpenIdConnectConstants.Destinations.AccessToken,
                OpenIdConnectConstants.Destinations.IdentityToken);

            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
            // (specify offline_access to issue a refresh token).
            ticket.SetScopes("profile", "offline_access");

            context.Validate(ticket);
        }

        return Task.FromResult(0);
    }
}

Projekt.json

{
  "dependencies": {
    "AspNet.Security.OpenIdConnect.Server": "1.0.0",
    "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0",
    "Microsoft.AspNetCore.Mvc": "1.0.0",
  }

  // other code omitted
}
Question

Frage

Wie verwenden wir ein Bearer-Token mit ASP.NET 5 mithilfe eines Benutzernamens und eines Kennwortflusses? In unserem Szenario möchten wir, dass sich ein Benutzer mithilfe von AJAX-Anrufen registrieren und anmelden kann, ohne dass eine externe Anmeldung erforderlich ist.

Um dies zu tun, benötigen wir einen Autorisierungsserver-Endpunkt. In den vorherigen Versionen von ASP.NET würden wir Folgendes tun und uns dann unter der URL ourdomain.com/Token .

// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
};

In der aktuellen Version von ASP.NET funktioniert das obige jedoch nicht. Wir haben versucht, den neuen Ansatz zu finden. Das Beispiel aspnet / identity auf GitHub konfiguriert beispielsweise die Facebook-, Google- und Twitter-Authentifizierung, scheint aber keinen externen OAuth-Berechtigungsserver-Endpunkt zu konfigurieren, es sei denn, AddDefaultTokenProviders() tut dies. In diesem Fall fragen wir uns, was URL zum Provider wäre.

Forschung

Wir haben aus dem Lesen der Quelle gelernt , dass wir der HTTP-Pipeline "Bearer Authentication Middleware" hinzufügen können, indem wir IAppBuilder.UseOAuthBearerAuthentication in unserer Startup Klasse aufrufen. Dies ist ein guter Anfang, auch wenn wir noch nicht sicher sind, wie wir den Token-Endpunkt einstellen sollen. Das hat nicht funktioniert:

public void Configure(IApplicationBuilder app)
{  
    app.UseOAuthBearerAuthentication(options =>
    {
        options.MetadataAddress = "meta";
    });

    // if this isn't here, we just get a 404
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello World.");
    });
}

Unter ourdomain.com/meta wir nur unsere Hallo-Welt-Seite.

Weitere Untersuchungen haben gezeigt, dass wir auch die Erweiterungsmethode IAppBuilder.UseOAuthAuthentication verwenden können, die einen OAuthAuthenticationOptions Parameter benötigt. Dieser Parameter hat eine TokenEndpoint Eigenschaft. Obwohl wir uns nicht sicher sind, was wir machen, haben wir das versucht, was natürlich nicht funktioniert hat.

public void Configure(IApplicationBuilder app)
{
    app.UseOAuthAuthentication("What is this?", options =>
    {
        options.TokenEndpoint = "/token";
        options.AuthorizationEndpoint = "/oauth";
        options.ClientId = "What is this?";
        options.ClientSecret = "What is this?";
        options.SignInScheme = "What is this?";
        options.AutomaticAuthentication = true;
    });

    // if this isn't here, we just get a 404
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello World.");
    });
}

Mit anderen Worten, ourdomain.com/token zu ourdomain.com/token , gibt es keinen Fehler, es gibt einfach wieder unsere Hallo-Welt-Seite.




Related