Как получить доступ к действиям контроллера [Authorize] с помощью HttpClient с токеном Bearer? Получение 401 "Аудитория недействительна"
В приложении есть четыре клиента:
- angular.application - владелец ресурса
- identity_ms.client - приложение webapi (.net core 2.1)
- IdentityServer4 с AspNetIdentity
- AccountController с общими действиями для регистрации пользователей, сброса пароля и т. Д.
- UserController с защищенными действиями. Действие данных UserController имеет
[Authorize(Policy = "user.data")]
атрибут
- ms_1.client - приложение webapi (.net core 2.1)
- request.client - добавлен специально для отправки запросов от ms_1.client в UserController identity_ms.client для получения некоторых пользовательских данных.
Я запрашиваю клиентов с помощью Postman:
- http://localhost:identity_ms_port/connect/token для получения access_token
- http://localhost:ms_1_port/api/secured/action для получения защищенных данных из ms_1
- 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");
}
//...
}
Я пробовал следующее:
- Извлечь access_token из запроса в заголовок авторизации ms_1 и использовать его для доступа к пользователю / данным.
- Получить новый access_token для доступа к пользователю / данным с ним. Увидеть
public async Task<IActionResult> Post(ViewModel model)
код в блоке кода.
В обоих случаях у меня есть правильный токен, который я могу использовать для запроса как защищенных / действий, так и действий пользователя / данных от Почтальона, но HttpClient получает несанкционированный ответ (401).
Что я делаю неправильно?
1 ответ
В вашем коде клиента с HttpClient
вы не запрашиваете какие-либо области действия для API, поэтому токен, который выдается Identity Server 4, не будет содержать API в качестве одной из аудиторий, а затем вы получите 401 от API.
Измените свой запрос токена, чтобы запросить также область действия API.
var response = await client.RequestClientCredentialsAsync("user.data.scope ms_1.scope");