web-dev-qa-db-ja.com

MVC5アプリケーション-実装OAuth認証コードフロー

このチュートリアルに基づいて http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server 、承認サーバー、リソースを作成しましたサーバーとMVCクライアント。 MVCクライアントには、リソースサーバーからデータを取得するコントローラーがあります。リソースサーバーには認証が必要です。 MVCクライアントは、認証サーバーから認証コードを取得し、認証のためにユーザーを認証サーバーにリダイレクトします。最後に、MVCクライアントは、アクセストークンの認証コードを交換してリソースサーバーにアクセスします。これは、OAuth 2プロトコルで説明されている認証コードフローです。これは正常に機能します。

これで、MVCクライアント自体のコントローラーに認証を要求させる必要があります。このためのチュートリアルが見つかりません。

追加した

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

私のStartup.Auth.csに。承認サーバーにリダイレクトするオプションを設定する必要があると思います。オプションでプロバイダーを設定することもできます。

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

しかし、私はプロバイダーのイベントの実装にも固執しています。誰かが私を正しい方向に導くことができますか?または私を助けるかもしれないチュートリアルはありますか?

15
MatthiasRamp

私はブロックアレンからのこれらの2つの記事に基づいた解決策に行き着きました:

基本的な考え方は、2つの認証ミドルウェアを登録することです。アクティブなCookie-AuthenticationとパッシブなOAuthBearer-Authentication。 Startup.Auth.csでは、次のように追加されます。

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

また、ExternalLogin-Controllerを追加します。そのLoginメソッドは、認証コードを取得するために、ユーザーを認証サーバーのログインページにリダイレクトする必要があります。認証コードを処理するコールバック関数を提供する必要があります。

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

コールバック関数では、認証コードをアクセストークン(および更新トークン)と交換して、パッシブOAuthBearer-authenticationミドルウェアにチャレンジし、Accessトークンを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");
}

これが、MVC5アプリケーションでOAuth認証コードフローを実装している人に役立つことを願っています。

32
MatthiasRamp

私は公式サンプルを使用しました MVC Implicit Client これはMVCアプリケーションの正しい認証フローであると私は信じています。

承認のために私はこれを使用しました はじめに 、特にロールが指定されている場合の無限ループに関する部分[Authorize(Roles = "Foo,Bar")]で、ユーザーは認証されていますが、これらのいずれも所有していません。

1
Jan Zahradník