web-dev-qa-db-ja.com

JWTを使用した.Net Core 2.0 Web API-IDを追加するとJWT認証が壊れる

(編集-適切な修正が見つかりました!下記を参照)

OK-これは.Net Core 2.0と認証での最初の試みですが、過去にWeb API 2.0を使用したことがあり、さまざまなMVCおよびWebforms ASPプロジェクト過去数年にわたって。

.Net Coreを使用してWeb APIのみのプロジェクトを作成しようとしています。これにより、いくつかのレポートを作成するためのマルチテナントアプリケーションのバックエンドが形成されるため、ユーザーを認証できる必要があります。通常のアプローチはJWTを使用することです。まずユーザーを認証してトークンを生成し、次にそれをクライアントに渡してすべてのAPI要求で使用します。データは、EF Coreを使用して保存および取得されます。

私は この投稿 に従って、この設定を取得する基本的な方法を実行しましたが、これをうまく機能させることができました-ユーザー名/パスワードを受け入れ、有効であればトークンを返すコントローラーがあり、クレームに基づいて設定された承認ポリシー。

次に必要なのは、実際にユーザー/パスワード/などを管理することです。ユーザー/ロール、パスワードなどを心配するための既製のコードがたくさんあるので、このために.Net Core Identityを使用するだけだと思いました。カスタムUserクラスとUserRoleクラスは、標準のIdentityUserクラスとIdentityRoleクラスから派生しましたが、現在は標準のクラスに戻っています。

私が抱えている問題は、基本的にStartup.ConfigureServicesクラスにこの行を追加するとすぐに、認証を壊さずにアイデンティティを追加し、さまざまなサービス(rolemanager、usermanagerなど)をすべて登録する方法を把握できないことです:

services.AddIdentity<IdentityUser, IdentityRole>()
    .AddEntityFrameworkStores<MyContext>();

それはすべてうまくいかず、リクエストを受け取ったときにクレームが表示されなくなったため、すべてのポリシーがロックされ、何にもアクセスできません。

これらの行がない場合、UserManager、RoleManager、UserStoreなどに関連するエラーがすべてDIに登録されていません。

だから...どうすれば(可能であれば)アイデンティティを登録してコンテキストに正しく接続できますが、実際の承認メカニズムへの変更を回避/削除できますか?

私はかなりオンラインで調べましたが、これの多くは.Net Core 1.x以降に変更されたため、多くのチュートリアルなどはもはや有効ではありません。

このAPIアプリケーションにフロントエンドコードを持たせるつもりはないので、今のところフォームなどにCookie認証は必要ありません。

編集
OK、このコードでStartup.ConfigureServices()メソッドでJWT認証を設定することがわかりました。

 services.AddAuthentication(
            JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                 >>breakpoint>>>   options.TokenValidationParameters =
                        new TokenValidationParameters
                        {
                            ValidateIssuer = true,
                            ValidateAudience = true,
                            ValidateLifetime = true,
                            ValidateIssuerSigningKey = true,

                            ValidIssuer = "Blah.Blah.Bearer",
                            ValidAudience = "Blah.Blah.Bearer",
                            IssuerSigningKey =
                            JwtSecurityKey.Create("verylongsecretkey")

                        };
                });

( ">> breakpoint >>>"を介して)示された行にブレークポイントを置くと、do n'tがIDサービスを追加する行を追加するときにヒットしますが、それらを追加する場合行し、それはneverヒットします。これは、メソッドのどこでservices.AddIdentity()呼び出しを行っても当てはまります。これは単にラムダであることがわかりますので、後で実行されますが、AddIdentityのものを取得して認証を設定しない、またはすぐにコードを削除する方法はありますか?ある時点で、Identityスタッフが既に設定しているLambdaを設定用に実行しないことを選択するコードがあると思います...

あなたが持っている場合はすべてを読んでくれてありがとう:)

EDIT-回答が見つかりました
OK、最終的にこのGHの問題を発見しました。これは基本的にまさにこの問題です。 https://github.com/aspnet/Identity/issues/1376

基本的に私がしなければならなかったことは2つありました:

services.AddIdentity<IdentityUser, IdentityContext()の呼び出しが行われたことを確認してくださいfirst

呼び出しを変更して、次から認証を追加します。

services.AddAuthentication(
            JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
...

に:

services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
            .AddJwtBearer(options =>
...

これにより、迷惑なことにCookieが作成されますが、私が知る限りこれは認証には使用されません-[Authorize(Policy = "Administrator")]または同様のセットを持つコントローラー/アクションへのリクエストでベアラートークンを使用するだけです少なくとも。

さらにテストする必要がありますが、何らかの形で機能していないことがわかった場合は、ここに戻って更新を試みます。

(編集-適切なソリューションを今すぐ答えとして入れてください)

18
GPW

私は最終的に解決策をまとめたので、ユーザーの提案を常に学習して、自分の投稿を編集し、実際の回答としてこれを入れています。

わかりました、これは適切に行うことができます。まず、上記の編集で指摘した認証オプションを使用する必要があります-それで構いません。次に、services.AddIdentityCore<TUser>()ではなくuseservices.AddIdentity<TUser>()を使用する必要があります。ただし、これはロール管理のために多くのことを追加するものではなく、使用したいタイプのロールを提供する適切なコンストラクターがないようです。これは、私の場合、これをしなければならなかったことを意味します。

  IdentityBuilder builder = services.AddIdentityCore<IdentityUser>(opt =>
        {
            opt.Password.RequireDigit = true;
            opt.Password.RequiredLength = 8;
            opt.Password.RequireNonAlphanumeric = false;
            opt.Password.RequireUppercase = true;
            opt.Password.RequireLowercase = true;
        }
        );
        builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
        builder
            .AddEntityFrameworkStores<MyContext>();
        //.AddDefaultTokenProviders();

        builder.AddRoleValidator<RoleValidator<IdentityRole>>();
        builder.AddRoleManager<RoleManager<IdentityRole>>();
        builder.AddSignInManager<SignInManager<IdentityUser>>();

それを行った後、次は、トークンを送信する前にユーザーログインを検証するときに、SignInManagerメソッドCheckPasswordSignInAsyncおよびnotPasswordSignInAsync

public async Task<IdentityUser> GetUserForLogin(string userName, string password)
    {   
        //find user first...
        var user = await _userManager.FindByNameAsync(userName);

        if (user == null)
        {
            return null;
        }

        //validate password...
        var signInResult = await _signInManager.CheckPasswordSignInAsync(user, password, false);

        //if password was ok, return this user.
        if (signInResult.Succeeded)
        {
            return user;
        }

        return null;
    }

PasswordSignInAsyncメソッドを使用すると、実行時エラーが発生します。 IAuthenticationSignInHandlerが構成されていません。

これが誰かの助けになることを願っています。

23
GPW

GithubからAddIdentityコードを抽出し、それに基づいてデフォルトのCookie Authenticatorを追加しない拡張メソッドを作成しました。これは、組み込みのAddIdentityCoreと非常に似ていますが、IdentityRole

/// <summary>
/// Contains extension methods to <see cref="IServiceCollection"/> for configuring identity services.
/// </summary>
public static class IdentityServiceExtensions
{
    /// <summary>
    /// Adds the default identity system configuration for the specified User and Role types. (Without Authentication Scheme)
    /// </summary>
    /// <typeparam name="TUser">The type representing a User in the system.</typeparam>
    /// <typeparam name="TRole">The type representing a Role in the system.</typeparam>
    /// <param name="services">The services available in the application.</param>
    /// <returns>An <see cref="IdentityBuilder"/> for creating and configuring the identity system.</returns>
    public static IdentityBuilder AddIdentityWithoutAuthenticator<TUser, TRole>(this IServiceCollection services)
        where TUser : class
        where TRole : class
        => services.AddIdentityWithoutAuthenticator<TUser, TRole>(setupAction: null);

    /// <summary>
    /// Adds and configures the identity system for the specified User and Role types. (Without Authentication Scheme)
    /// </summary>
    /// <typeparam name="TUser">The type representing a User in the system.</typeparam>
    /// <typeparam name="TRole">The type representing a Role in the system.</typeparam>
    /// <param name="services">The services available in the application.</param>
    /// <param name="setupAction">An action to configure the <see cref="IdentityOptions"/>.</param>
    /// <returns>An <see cref="IdentityBuilder"/> for creating and configuring the identity system.</returns>
    public static IdentityBuilder AddIdentityWithoutAuthenticator<TUser, TRole>(this IServiceCollection services, Action<IdentityOptions> setupAction)
        where TUser : class
        where TRole : class
    {
        // Hosting doesn't add IHttpContextAccessor by default
        services.AddHttpContextAccessor();
        // Identity services
        services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
        services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
        services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
        services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
        services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>();
        // No interface for the error describer so we can add errors without rev'ing the interface
        services.TryAddScoped<IdentityErrorDescriber>();
        services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
        services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
        services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
        services.TryAddScoped<UserManager<TUser>>();
        services.TryAddScoped<SignInManager<TUser>>();
        services.TryAddScoped<RoleManager<TRole>>();

        if (setupAction != null)
        {
            services.Configure(setupAction);
        }

        return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
    }
}

これで、上記のコードをWebApiプロジェクトから通常どおり使用できます。

.AddIdentityWithoutAuthenticator<User, IdentityRole>()
2
Ricky Spanish