webentwicklung-frage-antwort-db.com.de

MVC 5-Anwendung - Implementieren Sie den OAuth-Autorisierungscode-Fluss

Basierend auf diesem Tutorial http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server habe ich einen Autorisierungsserver, einen Ressourcenserver und einen MVC-Client. Der MVC-Client verfügt über einen Controller, der Daten vom Ressourcenserver erhält. Der Resource Server erfordert eine Authentifizierung. Die MVC-Clients erhalten einen Autorisierungscode vom Autorisierungsserver und leiten den Benutzer zur Authentifizierung an den Autorisierungsserver. Schließlich tauschen die MVC-Clients den Autorisierungscode für ein Zugriffstoken aus, um auf den Ressourcenserver zuzugreifen. Dies ist der Autorisierungscode-Fluss, wie er vom OAuth 2-Protokoll beschrieben wird. Das funktioniert gut.

Jetzt muss ich für einen Controller des MVC-Clients selbst eine Authentifizierung anfordern. Ich kann dafür kein Tutorial finden.

Ich fügte hinzu

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

zu meinem Startup.Auth.cs. Ich gehe davon aus, dass ich die Optionen zum Umleiten an den Autorisierungsserver einrichten muss. Ich kann den Provider auch auf die Optionen setzen:

app.UseOAuthBearerAuthentication(new Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions()
{
    Provider = new OAuthBearerAuthenticationProvider()
});

Aber ich bin auch fest entschlossen, die Ereignisse des Anbieters umzusetzen. Kann mich jemand in die richtige Richtung leiten? Oder gibt es Tutorials, die mir helfen könnten?

15
MatthiasRamp

Ich erhielt eine Lösung basierend auf diesen zwei Artikeln von Brock Allen:

Die grundlegende Idee ist die Registrierung von zwei Authentifizierungs-Middlewares. Eine aktive Cookie-Authentifizierung und eine passive OAuthBearer-Authentifizierung. In Startup.Auth.cs werden sie wie folgt hinzugefügt:

app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/ExternalLogin/Login"),
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
    AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
    AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
});

Sie fügen auch einen ExternalLogin-Controller hinzu. Die Login-Methode muss den Benutzer auf die Login-Seite Ihres Autorisierungsservers umleiten, um den Autorisierungscode zu erhalten. Sie müssen eine Callback-Funktion angeben, mit der Sie den Autorisierungscode bearbeiten.

public async Task<ActionResult> Login(string returnUrl)
{
    if (string.IsNullOrEmpty(returnUrl) && Request.UrlReferrer != null)
        returnUrl = Server.UrlEncode(Request.UrlReferrer.PathAndQuery);

    if (Url.IsLocalUrl(returnUrl) && !string.IsNullOrEmpty(returnUrl))
        _returnUrl = returnUrl;

    //callback function
    _redirectUrl = Url.Action("AuthorizationCodeCallback", "ExternalLogin", null, Request.Url.Scheme);

    Dictionary<string, string> authorizeArgs = null;
    authorizeArgs = new Dictionary<string, string>
    {
        {"client_id", "0123456789"}
        ,{"response_type", "code"}
        ,{"scope", "read"}
        ,{"redirect_uri", _redirectUrl}
        // optional: state
    };

    var content = new FormUrlEncodedContent(authorizeArgs);
    var contentAsString = await content.ReadAsStringAsync();
    return Redirect("http://localhost:64426/oauth/authorize?" + contentAsString);
}

In Ihrer Callback-Funktion tauschen Sie den Autorisierungscode für ein Zugriffstoken (plus Aktualisierungstoken) aus, fordern Sie Ihre passive OAuthBearer-Authentifizierungs-Middleware heraus und melden Sie sich mit dem Access-Token als Cookie an.

public async Task<ActionResult> AuthorizationCodeCallback()
{
    // received authorization code from authorization server
    string[] codes = Request.Params.GetValues("code");
    var authorizationCode = "";
    if (codes.Length > 0)
        authorizationCode = codes[0];

    // exchange authorization code at authorization server for an access and refresh token
    Dictionary<string, string> post = null;
    post = new Dictionary<string, string>
    {
        {"client_id", "0123456789"}
        ,{"client_secret", "ClientSecret"}
        ,{"grant_type", "authorization_code"}
        ,{"code", authorizationCode}
        ,{"redirect_uri", _redirectUrl}
    };

    var client = new HttpClient();
    var postContent = new FormUrlEncodedContent(post);
    var response = await client.PostAsync("http://localhost:64426/token", postContent);
    var content = await response.Content.ReadAsStringAsync();

    // received tokens from authorization server
    var json = JObject.Parse(content);
    _accessToken = json["access_token"].ToString();
    _authorizationScheme = json["token_type"].ToString();
    _expiresIn = json["expires_in"].ToString();
    if (json["refresh_token"] != null)
        _refreshToken = json["refresh_token"].ToString();

    //SignIn with Token, SignOut and create new identity for SignIn
    Request.Headers.Add("Authorization", _authorizationScheme + " " + _accessToken);
    var ctx = Request.GetOwinContext();
    var authenticateResult = await ctx.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalBearer);
    ctx.Authentication.SignOut(DefaultAuthenticationTypes.ExternalBearer);
    var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
    ctx.Authentication.SignIn(applicationCookieIdentity);

    var ctxUser = ctx.Authentication.User;
    var user = Request.RequestContext.HttpContext.User;

    //redirect back to the view which required authentication
    string decodedUrl = "";
    if (!string.IsNullOrEmpty(_returnUrl))
        decodedUrl = Server.UrlDecode(_returnUrl);

    if (Url.IsLocalUrl(decodedUrl))
        return Redirect(decodedUrl);
    else
        return RedirectToAction("Index", "Home");
}

Ich hoffe, das ist nützlich für jemanden, der den OAuth-Autorisierungscode-Fluss in seiner MVC 5-Anwendung implementiert.

30
MatthiasRamp

Ich habe das offizielle Beispiel MVC Implicit Client verwendet, von dem ich glaube, dass es der korrekte Authentifizierungsfluss für die MVC-Anwendung ist.

Für die Autorisierung habe ich Getting Started verwendet, insbesondere den Teil über die Endlosschleife, wenn Rollen angegeben werden [Authorize(Roles = "Foo,Bar")] und der Benutzer authentifiziert ist, aber keine davon besitzt.

1
Jan Zahradník