Как получить доступ к действиям контроллера [Авторизовать] с помощью HttpClient с токеном носителя? Получение 401 Аудитория недействительна

В приложении есть четыре клиента:

  1. angular.application - владелец ресурса
  2. identity_ms.client - webapi app (.net core 2.1)
    • IdentityServer4 with AspNetIdentity
    • AccountController с общими действиями для регистрации пользователей, сброса пароля и т. д.
    • UserController с защищенными действиями. Действие Data в UserController имеет атрибут [Authorize(Policy = "user.data")]
  3. ms_1.client - приложение webapi (.net core 2.1)
  4. request.client - добавлен специально для отправки запросов от ms_1.client к UserController identity_ms.client для получения некоторых пользовательских данных.

Я прошу клиентов с помощью Postman:

  1. http://localhost:identity_ms_port/connect/token, чтобы получить access_token
  2. port/api/secured/action" rel="nofollow noreferrer">http: // localhost: ms[Authorize(Policy = "user.data")]port / api / secured / action, чтобы получить некоторые защищенные данные из ms_1
  3. http://localhost:identity_ms_port/api/user/data, чтобы получить некоторые защищенные данные пользователя из identity_ms

Все работает нормально.

Кроме того, служба ms_1 имеет защищенное действие, запрашивающее http://localhost:identity_ms_port/api/user/data с использованием System.Net.Http.HttpClient.

// identity_ms configuration
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(/*cors options*/);

    services
        .AddMvc()
        .AddApplicationPart(/*Assembly*/)
        .AddJsonOptions(/*SerializerSettings*/)
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.Configure<IISOptions>(iis =>
    {
        iis.AuthenticationDisplayName = "Windows";
        iis.AutomaticAuthentication = false;
    });

    var clients = new List<Client>
    {
        new Client
    {
            ClientId = "angular.application",
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            AllowedScopes = { "user.data.scope", "ms_1.scope", "identity_ms.scope" },
            AllowedGrantTypes = GrantTypes.ResourceOwnerPassword
    },
        new Client
        {
            ClientId = "ms_1.client",
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            AllowedScopes = { "user.data.scope", "ms_1.scope" },
            AllowedGrantTypes = GrantTypes.ClientCredentials
        },
        new Client
        {
            ClientId = "identity_ms.client",
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            AllowedScopes =
            {
                "user.data.scope",
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile
            },
            AllowedGrantTypes = GrantTypes.Implicit
        },
        new Client
        {
            ClientId = "request.client",
            AllowedScopes = { "user.data.scope" },
            AllowedGrantTypes = GrantTypes.ClientCredentials,
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            }
        }
    };
    var apiResources = new List<ApiResource>
    {
        new ApiResource("ms_1.scope", "MS1 microservice scope"),
        new ApiResource("identity_ms.scope", "Identity microservice scope"),
        new ApiResource("user.data.scope", "Requests between microservices scope")
    };

    var identityResources = new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile()
    };

    services
        .AddAuthorization(options => options.AddPolicy("user.data", policy => policy.RequireScope("user.data.scope")))
        .AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryIdentityResources(identityResources)
        .AddInMemoryApiResources(apiResources)
        .AddInMemoryClients(clients);

    services
        .AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(options =>
        {
            options.Audience = "identity_ms.scope";
            options.RequireHttpsMetadata = false;
            options.Authority = "http://localhost:identity_ms_port";
        });

    services.AddSwaggerGen(/*swagger options*/);
}

public void Configure(IApplicationBuilder app)
{
    app.UseMiddleware<CustomMiddleware>();
    app.UseIdentityServer();
    app.UseAuthentication();
    app.UseCors("Policy");
    app.UseHttpsRedirection();
    app.UseMvc(/*routes*/);
    app.UseSwagger();
}

// ms_1.client configuration
public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(/*cors options*/);

    services
        .AddMvc()
        .AddJsonOptions(/*SerializerSettings*/)
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services
        .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
        {
            options.Audience = "ms_1.scope";
            options.RequireHttpsMetadata = false;
            options.Authority = "http://localhost:identity_ms_port";
        });
}

public void Configure(IApplicationBuilder app)
{
    app.UseMiddleware<CustomMiddleware>();
    app.UseAuthentication();
    app.UseCors("Policy");
    app.UseStaticFiles();
    app.UseHttpsRedirection();
    app.UseMvc(/*routes*/);
    app.UseSwagger();
}

// ms_1.client action using HttpClient
[HttpPost]
public async Task<IActionResult> Post(ViewModel model)
{
    //...
    using (var client = new TokenClient("http://localhost:identity_ms_port/connect/token", "ms_1.client", "secret"))
    {
        var response = await client.RequestClientCredentialsAsync("user.data.scope");

        if (response.IsError)
        {
            throw new Exception($"{response.Error}{(string.IsNullOrEmpty(response.ErrorDescription) ? string.Empty : $": {response.ErrorDescription}")}", response.Exception);
        }

        if (string.IsNullOrWhiteSpace(response.AccessToken))
        {
            throw new Exception("Access token is empty");
        }

        var udClient = new HttpClient();

        udClient.SetBearerToken(response.AccessToken);
        udClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        var result = await udClient.GetAsync("http://localhost:identity_ms_port/api/user/data");
    }
    //...
}

Я пробовал следующее:

  1. Чтобы получить access_token из запроса к ms_1 заголовок авторизации и использовать его для доступа к user / data.
  2. Чтобы получить новый access_token для доступа к user / data с его помощью. См. public async Task<IActionResult> Post(ViewModel model) код в блоке кода.

В обоих случаях у меня есть правильный токен, который я могу использовать для запроса действий secure / action и user / data от Postman, но HttpClient получает неавторизованный ответ ( 401).

Снимок экрана заголовков ответов

Что я делаю неправильно?


person FridensonDev    schedule 12.02.2019    source источник
comment
Установите аудиторию в конфигурации api на «MS1 microservice scope», возможно, также опубликуйте образец токена (желательно с исходной кодировкой)   -  person Vidmantas Blazevicius    schedule 12.02.2019
comment
Привет, @VidmantasBlazevicius, спасибо за ответ. Вы имели в виду заменить это .AddJwtBearer(options => { options.Audience = "identity_ms.scope"; options.RequireHttpsMetadata = false; options.Authority = "http://localhost:identity_ms_port"; }); на это .AddJwtBearer(options => { options.Audience = "MS1 microservice scope"; options.RequireHttpsMetadata = false; options.Authority = "http://localhost:identity_ms_port"; });?   -  person FridensonDev    schedule 13.02.2019


Ответы (1)


В вашем клиентском коде с HttpClient вы не запрашиваете какие-либо области для API, поэтому токен, выпущенный Identity Server 4, не будет содержать API в качестве одной из аудиторий, а затем вы получите 401 из API.

Измените запрос токена, чтобы также запрашивать область действия API.

var response = await client.RequestClientCredentialsAsync("user.data.scope ms_1.scope");
person Vidmantas Blazevicius    schedule 13.02.2019