web-dev-qa-db-fra.com

Application MVC 5 - implémentation du flux de code d'autorisation OAuth

Sur la base de ce tutoriel http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server , j'ai créé un serveur d'autorisations, un serveur de ressources et un MVC Client. Le client MVC a un contrôleur qui extrait certaines données du serveur de ressources. Le serveur de ressources requiert une authentification. Les clients MVC obtiennent un code d'autorisation du serveur d'autorisations et redirigent l'utilisateur vers le serveur d'autorisations pour l'authentification. Enfin, les clients MVC échangent le code d'autorisation d'un jeton d'accès pour accéder au serveur de ressources. Il s'agit du flux de code d'autorisation décrit par le protocole OAuth 2. Cela fonctionne bien.

Maintenant, j'ai l'obligation de faire en sorte qu'un contrôleur du client MVC nécessite lui-même une authentification. Je ne trouve pas de tutoriel pour cela.

J'ai ajouté

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

vers mon fichier Startup.Auth.cs. Je suppose que je dois configurer les options de redirection vers le serveur d'autorisations. Je peux également définir le fournisseur sur les options:

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

Mais je suis également bloqué dans la mise en œuvre des événements du fournisseur. Quelqu'un peut-il me guider dans la bonne direction? Ou y a-t-il des tutoriels qui pourraient m'aider?

15
MatthiasRamp

Je me suis retrouvé avec une solution basée sur ces deux articles de Brock Allen:

L'idée fondamentale est d'enregistrer deux middlewares d'authentification. Une authentification active par cookie et une authentification passive OAuthBearer. Dans Startup.Auth.cs, ils sont ajoutés comme ceci:

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,
});

Vous ajoutez également un contrôleur ExternalLogin. Sa méthode de connexion doit rediriger l'utilisateur vers la page de connexion de votre serveur d'autorisations pour obtenir le code d'autorisation. Vous devez fournir une fonction de rappel où vous allez traiter le code d'autorisation.

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);
}

Dans votre fonction de rappel, vous échangez le code d'autorisation contre un jeton d'accès (plus le jeton d'actualisation), mettez au défi votre middleware d'authentification passive OAuthBearer et connectez-vous avec le jeton d'accès comme cookie.

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");
}

J'espère que cela est utile pour quelqu'un qui implémente le flux de codes d'autorisation OAuth dans son application MVC 5.

30
MatthiasRamp

J'ai utilisé l'exemple officiel MVC Implicit Client qui, à mon avis, constitue le flux d'authentification correct pour l'application MVC.

Pour l’autorisation, j’ai utilisé ceci getting started , en particulier la partie relative à la boucle infinie lorsque les rôles sont spécifiés [Authorize(Roles = "Foo,Bar")] et que l’utilisateur est authentifié mais ne possède aucun de ceux-ci.

1
Jan Zahradník