web-dev-qa-db-ja.com

MVCとWebAPIが異なるプロジェクトにある場合にベアラートークンを保存する方法

状況:承認サーバー(/トークンエンドポイント)およびリソースサーバーとして機能するWeb API2プロジェクトがあります。 ASP.Net WebAPIからMVC参照を除いたものが付属しているテンプレートを使用しています。 Start.Authは次のように構成されています。

public void ConfigureAuth(IAppBuilder app)
        {
            // Configure the db context and user manager to use a single instance per request
            app.CreatePerOwinContext(ApplicationDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

            // Enable the application to use a cookie to store information for the signed in user
            // and to use a cookie to temporarily store information about a user logging in with a third party login provider
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Configure the application for OAuth based flow
            PublicClientId = "self";
            OAuthOptions = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath = new PathString("/Token"),
                Provider = new ApplicationOAuthProvider(PublicClientId),
                AuthorizeEndpointPath = new PathString("/Account/ExternalLogin"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
                // In production mode set AllowInsecureHttp = false
                AllowInsecureHttp = true
            };

            // Enable the application to use bearer tokens to authenticate users
            app.UseOAuthBearerTokens(OAuthOptions);

            var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
            {
                AppId = ConfigurationManager.AppSettings["Test_Facebook_AppId"],
                AppSecret = ConfigurationManager.AppSettings["Test_Facebook_AppSecret"],
                //SendAppSecretProof = true,
                Provider = new FacebookAuthenticationProvider
                {
                    OnAuthenticated = (context) =>
                    {
                        context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
                        return Task.FromResult(0);
                    }
                }
            };

            facebookAuthenticationOptions.Scope.Add("email user_about_me user_location");
            app.UseFacebookAuthentication(facebookAuthenticationOptions);

        }

MVC 5クライアント(別のプロジェクト)は、承認とデータにWebAPIアプリを使用します。以下は、ユーザー名/パスワードストアの場合にベアラートークンを取得するためのコードです。

[HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            model.ExternalProviders = await GetExternalLogins(returnUrl);
            return View(model);
        }

        var client = Client.GetClient();

        var response = await client.PostAsync("Token", 
            new StringContent(string.Format("grant_type=password&username={0}&password={1}", model.Email, model.Password), Encoding.UTF8));

        if (response.IsSuccessStatusCode)
        {
            return RedirectToLocal(returnUrl);
        }
        return View();
    }

問題

Bearerトークンを取得し、それを後続の呼び出しのために認証ヘッダーに追加することができます。 Angular AppまたはSPAの場合は問題ないと思いますが、MVCには、自動的にCookieに保存して送信するなど、それを処理するものがあるはずです。後続のリクエストでCookieを検索しました。これを示唆する投稿がたくさんあります( OWINIdentityを使用した複数のAPIクライアントからのWebAPI 2外部ログインの登録 )が、できませんでした。トークンを取得した後の対処方法を理解してください。

MVCアプリStartup.Authに何かを追加する必要がありますか?

理想的には、ASP.Netテンプレート(MVC + Web API)のAccountControllerがすぐに使用できる機能(ログイン、登録、外部ログイン、パスワードを忘れるなど)が必要ですが、MVCとWebAPIは異なるプロジェクトにあります。

このボイラープレートコードを持つテンプレートまたはgitリポジトリはありますか?

前もって感謝します!

Update@ FrancisDucharmeの提案を組み込んで、以下はGrantResourceOwnerCredentials()のコードです。

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

            ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);

            if (user == null)
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            }

            ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
               OAuthDefaults.AuthenticationType);
            ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
                CookieAuthenticationDefaults.AuthenticationType);

            AuthenticationProperties properties = CreateProperties(user.UserName);
            AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);

            //Add a response cookie...
            context.Response.Cookies.Append("Token", context.Options.AccessTokenFormat.Protect(ticket));


            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(cookiesIdentity);
        }

しかし、私はまだそのCookieを取得したり、次に何をすべきかを理解したりできないようです。

質問の再作成:

  1. MVCクライアントからWebAPIメソッド(認証およびリソースサーバー)を認証、承認、および呼び出す正しい方法は何でしょうか?
  2. 基本的な配管(ログイン、登録-内部/外部、パスワードを忘れたなど)を行うAccountControllerの定型コードまたはテンプレートはありますか?
10
Amanvir Mundra

スタートアップクラスに応答Cookieを返してもらい、それをクライアントが後続のすべての要求で返すようにすることができます。次に例を示します。 GrantResourceOwnerCredentialsで行います。

public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{

    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {                          

        //your authentication logic here, if it fails, do this...
        //context.SetError("invalid_grant", "The user name or password is incorrect.");
        //return;

         var identity = new ClaimsIdentity(context.Options.AuthenticationType);
         identity.AddClaim(new Claim("sub", context.UserName));
         identity.AddClaim(new Claim("role", "user"));

         AuthenticationTicket ticket = new AuthenticationTicket(identity);

        //Add a response cookie...
        context.Response.Cookies.Append("Token", context.Options.AccessTokenFormat.Protect(ticket));

        context.Validated(ticket);

}

スタートアップクラス:

public partial class Startup
{

    public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }

    public Startup()
    {
        OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
    }

    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();

        ConfigureOAuth(app);
        //I use CORS in my projects....
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(config);

        WebApiConfig.Register(config);

    }

    public void ConfigureOAuth(IAppBuilder app)
    {
        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            AllowInsecureHttp = true, //I have this here for testing purpose, production should always only accept HTTPS encrypted traffic.
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
            Provider = new AuthorizationServerProvider()
        };

        app.UseOAuthAuthorizationServer(OAuthServerOptions);
        app.UseOAuthBearerAuthentication(OAuthBearerOptions);

    }
}

もちろん、これはクライアントでCookieが有効になっていることを前提としています。

次に、 MVCヘッダーを変更します すべてのリクエストにAuthorizationヘッダーを追加します。

ActionFilterAttributeで、Cookie値(Token)を取得し、ヘッダーを追加します。

1