Как получить доступ к действиям контроллера [Authorize] с помощью HttpClient с токеном Bearer?Получение 401 «Аудитория недействительна» - PullRequest
0 голосов
/ 12 февраля 2019

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

  1. angular.application - владелец ресурса
  2. identity_ms.client - приложение webapi (.netcore 2.1)
    • IdentityServer4 с 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 для получения некоторых пользовательских данных.

Iзапрашиваю клиентов, использующих Postman:

  1. http://localhost:identity_ms_port/connect/token для получения access_token
  2. http://localhost:ms_1_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 для доступа пользователь / данные с ним.См. public async Task<IActionResult> Post(ViewModel model) код в блоке кода.

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

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

Что я делаю не так?

1 Ответ

0 голосов
/ 13 февраля 2019

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

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

var response = await client.RequestClientCredentialsAsync("user.data.scope ms_1.scope");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...