6

多くの ajax 呼び出しを含む MVC コア アプリケーションを使用する場合に、アクセス トークンとリフレッシュ トークンを最適に実装する方法について読んでいます (そしてビデオを見ています)。私はそれが正しかったと思いますが、これを行うより良い方法があるかどうか知りたかっただけです. この情報を探している人の参考になるように、この投稿を編集します。

私のセットアップ: 多くの JavaScript を使用した MVC Core アプリケーションがあります。JavaScript は、ajax 呼び出しを使用して json を取得したり、アクションを呼び出したりしています。

ユーザーが Cookie 認証を使用して API にアクセスできないようにするため、app.Map を使用してアプリケーションを 2 つの部分に分割しています。ユーザーが ID トークンを使用してビューにアクセスできるものと、アクセス トークンを必要とするものです。また、アクセス トークンを更新する必要がある時間を保持するために Cookie を追加しています。

Startup.cs (重要ではない部分を削除しました)

  app.UseCookieAuthentication(new CookieAuthenticationOptions
  {
    AuthenticationScheme = "Cookies",
    AutomaticAuthenticate = true,
    ExpireTimeSpan = TimeSpan.FromMinutes(60)
  });

  JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

  var oidcOptions = new OpenIdConnectOptions
  {
    AuthenticationScheme = "oidc",
    SignInScheme = "Cookies",

    Authority = LoginServerUrl,
    RequireHttpsMetadata = false,
    ClientId = "MyApp",
    ClientSecret = "*****",
    ResponseType = "code id_token",
    SaveTokens = true,
    Events = new OpenIdConnectEvents()
    {
      OnTicketReceived = async notification =>
      {
        notification.Response.Cookies.Append("NextAccessTokenRefresh", DateTime.Now.AddMinutes(30).ToString());
        notification.Response.Cookies.Delete("AccessToken");
      },
    },

    TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
    {
      NameClaimType = JwtClaimTypes.Name,
      RoleClaimType = JwtClaimTypes.Role,
    },
  };

  oidcOptions.Scope.Clear();
  oidcOptions.Scope.Add("openid");
  oidcOptions.Scope.Add("roles");
  oidcOptions.Scope.Add("offline_access");

  app.UseOpenIdConnectAuthentication(oidcOptions);

  app.Map("/api", (context) =>
  {
    var bearerTokenOptions = new IdentityServerAuthenticationOptions
    {
      AuthenticationScheme = "Bearer",
      Authority = LoginServerUrl,,
      RequireHttpsMetadata = false,
      ScopeName = "MyApi",
      AutomaticAuthenticate = true
    };

    context.UseIdentityServerAuthentication(bearerTokenOptions);
    context.UseMvcWithDefaultRoute();
  });

コントローラー アクションへのすべての ajax 呼び出しは、次の URL /api/[Controller]/[Action] を使用して行われます。

ID トークンを使用して API にアクセスできるようにしたくないので、Authorize(ActiveAuthenticationSchemes = "Bearer") 属性もコントローラー アクションに追加します。したがって、JavaScript によって呼び出されるコントローラー アクションは次のようになります。

[HttpPost, Authorize(ActiveAuthenticationSchemes = "Bearer")]
public async Task<JsonResult> DoStuff()
{
}

JavaScript が API リソースにアクセスする必要がある場合、コントローラーは最初にアクセス トークンを取得し、カスタム JavaScript init メソッドを使用してそれを JavaScript に挿入します。

この C# メソッドは、アクセス Cookie の更新と取得を担当します。

public async Task<string> GetAccessTokenAsync()
{
  var accessToken = _contextAccessor.HttpContext.Request.Cookies["AccessToken"];
  var nextAccessTokenRefresh = _contextAccessor.HttpContext.Request.Cookies["NextAccessTokenRefresh"];
  if (string.IsNullOrEmpty(nextAccessTokenRefresh) || string.IsNullOrEmpty(accessToken) || DateTime.Parse(nextAccessTokenRefresh) <= DateTime.Now)
  {
    var refreshToken = await _contextAccessor.HttpContext.Authentication.GetTokenAsync("refresh_token");
    var tokenClient = new TokenClient(_appSettings.LoginServerUrl + "/connect/token", _appSettings.LoginClientId, _appSettings.LoginClientSecret);
    var response = await tokenClient.RequestRefreshTokenAsync(refreshToken);
    accessToken = response.AccessToken;

    //Set cookies for next refresh
    _contextAccessor.HttpContext.Response.Cookies.Append("NextAccessTokenRefresh", DateTime.Now.AddMinutes(30).ToString());
    _contextAccessor.HttpContext.Response.Cookies.Append("AccessToken", response.AccessToken);
  }

  return accessToken;
}

すべての $.ajax で、次のパラメーターを追加しました。

beforeSend: function(xhr, settings) { xhr.setRequestHeader('Authorization','Bearer ' + accessToken); }

それでおしまい。デフォルトのアクセス トークンの有効期限は 1 時間です。私はいつもその半分の時間後にそれをリフレッシュします。

今私の質問に:

  1. 何らかの方法でコードを改善できますか?
  2. このようにして、セキュリティ関連のリスクが見られますか?
  3. OnTicketReceived でアクセス トークンを取得できますか?
4

1 に答える 1

3

何らかの方法でコードを改善できますか?

Startup.cs次のようにする必要があります ( Map「/api」パスでのみ機能するためです。詳細については、https : //docs.asp.net/en/latest/fundamentals/middleware.html#run-map-and-useを参照してください):

app.MapWhen(context => !context.Request.Path.Value.StartsWith("/api"), builder=>
{
    app.UseCookieAuthentication(options);
    ...
    app.UseOpenIdConnectAuthentication(oidcOptions);
    ....
});

app.MapWhen(context => context.Request.Path.Value.StartsWith("/api"), builder=>
{
    var bearerTokenOptions = new IdentityServerAuthenticationOptions
    {
      AuthenticationScheme = "Bearer",
      Authority = LoginServerUrl,,
      RequireHttpsMetadata = false,
      ScopeName = "MyApi",
      AutomaticAuthenticate = true
    };

    context.UseIdentityServerAuthentication(bearerTokenOptions);
    context.UseMvcWithDefaultRoute();
});

2 つ目のポイントとして、Cookie の有効期限は 60 分です。この場合、リフレッシュ トークンの有効期間は 60 分になります。これは問題になる可能性があると思います。

このようにして、セキュリティ関連のリスクが見られますか?

リフレッシュ トークンを使用するのに十分な経験がないため、実装が安全かどうかはわかりません。しかし、リフレッシュトークン(実装用)は複雑さを増し、セキュリティリスクも増加すると思います(これは私の意見であり、私はセキュリティの専門家ではありません)。私は暗黙的なフローで有効期間の長いアクセス トークンのみを使用します (単純化のため)。

OnTicketReceived でアクセス トークンを取得できますか?

はい、次のことができます。

OnTicketReceived = ctx =>
{
     var token = ctx.Ticket.Properties.GetTokenValue("access_token");
     ...
}
于 2016-10-02T06:47:51.023 に答える