web-dev-qa-db-ja.com

MVCページとWeb APIページの両方で認証の使用を組み合わせますか?

私はMVC 5 Webアプリケーションを持っていて、Login.cshtmlページでログインしてCookieを取得でき、ログインは正常に機能します。しかし、私はWeb APIを使用してログインし、次に(たぶん)MVCページにログインするようにCookieを設定したいと思います(またはMVCログインでログインしてからWeb APIにアクセスします)。 Web APIはCookieトークンではなく、ベアラートークンを返すので、これは機能しません。 MVCページとWeb APIページの両方で認証の使用を組み合わせる方法はありますか?

更新:

これは実際にはコードの問題ではなく、概念的な問題です。

通常のMVC Webページは、デフォルトで「.AspNet.ApplicationCookie」という名前のCookieを調べて、要求者のIDを判別します。このCookieは、ApplicationSignInManager.PasswordSignInAsyncを呼び出すことによって生成されます。

一方、WebAPI呼び出しは、Authorization ...という名前の項目の要求ヘッダーを調べ、その値を使用して要求者のIDを判別します。これは、 "/ Token"へのWebAPI呼び出しから返されます。

これらは非常に異なる値です。私のWebサイトは両方のMVCページを使用する必要がありますand WebAPI呼び出し(これらのページを動的に更新するため)...と、タスクを実行するために両方を認証する必要があります。

私が考えることができる唯一の方法は、実際に2回認証することです... 1回はWebAPI呼び出しで、もう1回はログインポストで。 (以下の私の回答を参照してください)。

これは非常にハックのようです...しかし、これを達成するためのより適切な方法があるかどうかを知るのに十分な認証コードを理解していません。

24
Brian Rice

これを実現する最良の方法は、MVCプロジェクトに承認サーバー(トークンを生成するWeb API)とトークン消費ミドルウェアを用意することです。 IdentityServer が役立つはずです。しかし、私はこのようにそれをしました:

説明されているように、JWTとWeb APIおよびASP.Net Identityを使用して認証サーバーを構築しました こちら

これを実行すると、Web API startup.csは次のようになります。

 // Configures cookie auth for web apps and JWT for SPA,Mobile apps
 private void ConfigureOAuthTokenGeneration(IAppBuilder app)
 {
    // Configure the db context, user manager and role manager to use a single instance per request
    app.CreatePerOwinContext(ApplicationDbContext.Create);
    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
    app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);

    // Cookie for old school MVC application
    var cookieOptions = new CookieAuthenticationOptions
    {
        AuthenticationMode = AuthenticationMode.Active,
        CookieHttpOnly = true, // JavaScript should use the Bearer
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,                
        LoginPath = new PathString("/api/Account/Login"),
        CookieName = "AuthCookie"
    };
    // Plugin the OAuth bearer JSON Web Token tokens generation and Consumption will be here
    app.UseCookieAuthentication(cookieOptions);

    OAuthServerOptions = new OAuthAuthorizationServerOptions()
    {
        //For Dev enviroment only (on production should be AllowInsecureHttp = false)
        AllowInsecureHttp = true,
        TokenEndpointPath = new PathString("/oauth/token"),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(30),
        Provider = new CustomOAuthProvider(),                
        AccessTokenFormat = new CustomJwtFormat(ConfigurationManager.AppSettings["JWTPath"])
    };

    // OAuth 2.0 Bearer Access Token Generation
    app.UseOAuthAuthorizationServer(OAuthServerOptions);
}

CustomOAuthProviderおよびCustomJwtFormatクラス here を見つけることができます。

同じトークンを使用して保護したい他のすべてのAPI(リソースサーバー)で消費ロジック(ミドルウェア)を記述しました。 MVCプロジェクトでWeb APIによって生成されたトークンを使用するため、承認サーバーを実装した後、次のことを行う必要があります。

MVCアプリで、これをstartup.csに追加します。

public void Configuration(IAppBuilder app)
{
        ConfigureOAuthTokenConsumption(app);
}

private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
    var issuer = ConfigurationManager.AppSettings["AuthIssuer"];
    string audienceid = ConfigurationManager.AppSettings["AudienceId"];
    byte[] audiencesecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["AudienceSecret"]);

    app.UseCookieAuthentication(new CookieAuthenticationOptions { CookieName = "AuthCookie" , AuthenticationType=DefaultAuthenticationTypes.ApplicationCookie });

    //// Api controllers with an [Authorize] attribute will be validated with JWT
    app.UseJwtBearerAuthentication(
        new JwtBearerAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Passive,
            AuthenticationType = "JWT",
            AllowedAudiences = new[] { audienceid },
            IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
            {
                new SymmetricKeyIssuerSecurityTokenProvider(issuer, audiencesecret)                           
            }

        });
}

MVCコントローラーで、トークンを受け取ったら、トークンを非シリアル化し、アクセストークンからCookieを生成します。

AccessClaims claimsToken = new AccessClaims();
claimsToken = JsonConvert.DeserializeObject<AccessClaims>(response.Content);
claimsToken.Cookie = response.Cookies[0].Value;               
Request.Headers.Add("Authorization", "bearer " + claimsToken.access_token);
var ctx = Request.GetOwinContext();
var authenticateResult = await ctx.Authentication.AuthenticateAsync("JWT");
ctx.Authentication.SignOut("JWT");
var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
ctx.Authentication.SignIn(applicationCookieIdentity);

マシンキーを生成し、それをWeb APIおよびASP.Net MVCサイトのweb.configに追加します。

これにより、Cookieが作成され、MVCサイトの[Authorize]属性とWeb APIがこのCookieを受け入れます。

PS私はこれをWeb API発行JWT(承認サーバーまたはAuth&リソースサーバー)で行い、それをASP.Net MVCで使用することができましたウェブサイト、Angularで構築されたSPAサイト、組み込みのセキュアAPI python(リソースサーバー)、spring(リソースサーバー)、Androidアプリ。

10
Ravi A.

Ugg ...私がしなければならなかったことは、Login.cshtmlフォームを使用して送信をオーバーライドすることです... WebApiベアラートークンを取得するためにAjax呼び出しを行ってください...そして実際のMVC Cookieを取得するためにフォーム送信を実行してください。したがって、私は実際には2つのログインリクエストを作成しています。1つはWebApiトークン用で、もう1つはMVC Cookie用です。

私にはかなりハッキーに見えます...無記名トークンを使用してMVCにサインインする方法があったらいいですね...または、通常のMVCページ要求に使用できるCookieを返すWebApiへの呼び出し。

誰かがより良い方法を持っているなら、私はそれを聞きたいです。

これは、Login.cshtmlに追加したスクリプトコードです。

    $(document).ready(function () {
        $('form:first').submit(function (e) {
            e.preventDefault();
            var $form = $(this);
            var formData = $form.serializeObject(); // https://github.com/macek/jquery-serialize-object
            formData.grant_type = "password";
            $.ajax({
                type: "POST",
                url: '@Url.Content("~/Token")',
                dataType: "json",
                data: formData, // seems like the data must be in json format
                success: function (data) {
                    sessionStorage.setItem('token', data.access_token);
                    $form.get(0).submit(); // do the actual page post now
                },
                error: function (textStatus, errorThrown) {
                }
            });
        });
    });
2
Brian Rice

私がしようとしているのは、MVCによって提供されるページに、Web APIメソッドを呼び出すJavaScriptがあることです。 ASP.NET Identityを使用して認証を処理している場合(これは実行しているように見えます)、MVCはOAuthトークンを使用して認証用にWeb APIに渡すことができます。

これは、同様の状況で私のために働くいくつかのJavaScriptコードからのスニペットです:

var token = sessionStorage.getItem('access_token');
var headers = {};
if (token) {
    headers.Authorization = 'Bearer ' + token;
}
$.ajax({
    type: <GET/POSt/...>,
    url: <your api>,
    headers: headers
}).done(function (result, textStatus) {
1
DavidS

上記のコメントから、私が理解していることから、ブラウザーを介してログインを実行するが、ajax呼び出しを使用してweb-apiメソッドを呼び出す必要があるシナリオがあります。

ブラウザの呼び出しはセッションCookieベースです。ブラウザーからのajax呼び出しではヘッダーにセッションCookieが含まれますが、web-apiが検証を実行するために必要な認証ヘッダーが必要です。

したがって、ログインが成功した場合は、web-apiベースのトークンを生成し、それをCookie(JavaScriptでアクセス可能)として設定し、ajax呼び出しを行うときに、Cookieから取得してヘッダーに含める必要があります。 「Authorization」ヘッダー。

0
Ravindra HV

私もあなたと同じようなケースがありますが、認証には別の方法を使用しています。

私はWebとapiを持っています。これらはすべてイントラネットユーザー向けです。ユーザーのIDを使用してWebおよびAPIを渡しません。代わりに、個別のWebアカウントを作成しました。Webが毎回この特別なアカウントを使用してAPIに接続します。

なぜなら、ユーザーがAPIに直接接続しないようにする必要があるからです。 Web UIにのみ接続する必要があります。

これがお役に立てば幸いです。

0
Xin