web-dev-qa-db-ja.com

ASP.NET Core 2.0は自動チャレンジを無効にします

ASP.NET Coreプロジェクトを2.0にアップグレードした後、保護されたエンドポイントにアクセスしようとすると401が返されなくなりますが、ユーザーの認証を許可するために(存在しない)エンドポイントにリダイレクトされます。

望ましい動作は、アプリケーションが単に401を返すことです。以前は、認証を構成するときにAutomaticChallenge = falseを設定していましたが、 この記事 に従って、設定はもはや関係ありません(実際には ' tはもう存在します)。

私の認証は次のように構成されています。

Startup.cs.ConfigureServices():

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(o =>
                {
                    o.Cookie.Name = options.CookieName;
                    o.Cookie.Domain = options.CookieDomain;
                    o.SlidingExpiration = true;
                    o.ExpireTimeSpan = options.CookieLifetime;
                    o.TicketDataFormat = ticketFormat;
                    o.CookieManager = new CustomChunkingCookieManager();
                });

構成、設定():

app.UseAuthentication();

ユーザーが認証されていないときにアプリケーションが401を返すように、自動チャレンジを無効にするにはどうすればよいですか?

20
severin

他のいくつかの回答で指摘されているように、Cookie認証による自動チャレンジをオフにする設定はなくなりました。解決策は、OnRedirectToLoginをオーバーライドすることです。

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(options =>
         {                 
             options.Events.OnRedirectToLogin = context =>
             {
                 context.Response.Headers["Location"] = context.RedirectUri;
                 context.Response.StatusCode = 401;
                 return Task.CompletedTask;
             };
         });

これは将来変更される可能性があります: https://github.com/aspnet/Security/issues/1394

25
severin

いくつかの調査の後、次のアプローチでこの問題に対処できることがわかりました。

IdentityとJWTの両方の2つの認証スキームを追加できます。認証にIDスキームを使用し、チャレンジにJWTスキーマを使用すると、JWTはチャレンジ中にログインルートにリダイレクトしません。

services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();

services.AddAuthentication((cfg =>
{
    cfg.DefaultScheme = IdentityConstants.ApplicationScheme;
    cfg.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})).AddJwtBearer();
20
Zheng Xing

@Serverinと同様に、アプリケーションCookieのOnRedirectToLoginの設定は機能しましたが、Startup.cs:ConfigureServicesのservices.AddIdentityに続くステートメントで実行する必要があります。

services.ConfigureApplicationCookie(options => {
  options.Events.OnRedirectToLogin = context => {
    context.Response.Headers["Location"] = context.RedirectUri;
    context.Response.StatusCode = 401;
    return Task.CompletedTask;
  };
});
5
sammarcow

this 記事によると:

1.xでは、AutomaticAuthenticateおよびAutomaticChallengeプロパティは、単一の認証スキームに設定します。これを実施する良い方法はありませんでした。

2.0では、これら2つのプロパティは個々のAuthenticationOptionsインスタンスのフラグとして削除され、ベースAuthenticationOptionsクラスに移動しました。プロパティは、AddAuthenticationメソッド呼び出しで構成できますConfigureServicesメソッドStartup.csの

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);

または、AddAuthenticationメソッドのオーバーロードバージョンを使用して、複数のプロパティを設定します。次のオーバーロードメソッドの例では、デフォルトのスキームはCookieAuthenticationDefaults.AuthenticationSchemeに設定されています。あるいは、認証スキームは、個人[Authorize]属性または許可ポリシー内で指定できます。

services.AddAuthentication(options => {
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
});

次の条件のいずれかに該当する場合、2.0でデフォルトスキームを定義します。

  • ユーザーに自動的にサインインさせたい
  • スキームを指定せずに[Authorize]属性または許可ポリシーを使用します

このルールの例外は、AddIdentityメソッドです。このメソッドはCookieを追加し、デフォルトの認証スキームとチャレンジスキームをアプリケーションCookieに設定しますIdentityConstants.ApplicationScheme。さらに、既定のサインインスキームを外部CookieIdentityConstants.ExternalSchemeに設定します。

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

4
Sergey

これは、CookieAuthenticationEvents.OnRedirectToLoginのソースコードです。

public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToLogin { get; set; } = context =>
{
    if (IsAjaxRequest(context.Request))
    {
        context.Response.Headers["Location"] = context.RedirectUri;
        context.Response.StatusCode = 401;
    }
    else
    {
        context.Response.Redirect(context.RedirectUri);
    }
    return Task.CompletedTask;
};

クライアントからAPI呼び出しを行いながら、「X-Requested-With:XMLHttpRequest」ヘッダーをリクエストに追加できます。

3
Zack

DI /テストにより適したこれを行う別の方法は、AuthenticationSchemeOptions.EventsType(別の答え ここを指して )。これにより、他のコンポーネントを解決プロセスに取り込むことができます。

以下は、認証されていないリクエストでログインへのデフォルトのリダイレクトを停止し、代わりにハード401。また、認証されていないリクエストについて知る必要がある他の依存関係のためのスロットもあります。

Startup.cs

services
    .AddAuthentication("MyAuthScheme")
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
    {
        options.EventsType = typeof(MyEventsWrapper);
    };

...

services.AddTransient<MyEventsWrapper>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

次に、MyEventsWrapper.cs

public class MyEventsWrapper : CookieAuthenticationEvents
{
    private readonly IHttpContextAccessor _accessor;
    private readonly IDependency _otherDependency;

    public MyEventsWrapper(IHttpContextAccessor accessor,
                           IDependency otherDependency)
    {
        _accessor = accessor;
        _otherDependency = otherDependency;
    }

    public override async Task RedirectToLogin(RedirectContext<CookieAuthenticationOptions> context)
    {
        context.Response.Headers.Remove("Location");
        context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
        await _otherDependency.Cleanup(_accessor.HttpContext);
    }
}
1
eouw0o83hf

ほとんどの場合、解決策はオーバーライドすることです

OnRedirectToLogin

しかし、私のアプリでは複数の認証ポリシーを使用していたため、OnRedirectToLoginのオーバーライドは機能しませんでした。私の場合の解決策は、着信リクエストをリダイレクトする単純なミドルウェアを追加することでした。

app.Use(async (HttpContext context, Func<Task> next) => {
    await next.Invoke(); //execute the request pipeline

    if (context.Response.StatusCode == StatusCodes.Status302Found && context.Response.Headers.TryGetValue("Location", out var redirect)) {
        var v = redirect.ToString();
        if (v.StartsWith($"{context.Request.Scheme}://{context.Request.Host}/Account/Login")) {
            context.Response.Headers["Location"] = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}";
            context.Response.StatusCode = 401;
        }
    }
});

1

401エラーを生成する方法はわかりませんが、次を使用する場合:

o.AccessDeniedPath = "{path to invalid}";

これにより、チャレンジが失敗したときにどこかにリダイレクトできます。

0
Gary Holland