У меня есть приложение Xamarin.Forms, которое я использую для подключения к бэкэнду службы приложений, и я пытаюсь аутентифицироваться с помощью Auzre B2 C JWT токенов.
Через различные учебники, которые у меня есть удалось настроить B2 C с использованием учетных записей Microsoft, и я могу создавать пользователей, изменять пароли и генерировать токены доступа.
Следующим моим шагом было добавление атрибута [Authorize] в мой контроллер и попытка чтобы передать этот токен службе приложений и авторизовать пользователей, но независимо от того, что я пытаюсь, я получаю 401 несанкционированный ответ от моей службы.
- Я добавляю токен JWT в заголовок авторизации моего HttpClient, и он попадает в сервис.
- Я могу вставить свой токен в https://jwt.ms/, и он правильно сообщает мне, что находится в моем токене.
- У меня есть реализован этот код в попытке выяснить, в чем дело.
ConfigureServices в файле startup.cs выглядит следующим образом:
public void ConfigureServices(IServiceCollection services) {
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => {
options.Audience = Configuration["Authentication:AzureAd:ClientId"];
options.Events = new JwtBearerEvents {
OnAuthenticationFailed = AuthenticationFailed
};
options.Authority = $"https://{tenant name}.b2clogin.com/{tenant id}/{Configuration["Authentication:AzureAd:Policy"]}";
options.Events = new JwtBearerEvents {
OnAuthenticationFailed = ctx =>
{
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
message += "From OnAuthenticationFailed:\n";
message += FlattenException(ctx.Exception);
return Task.CompletedTask;
},
OnChallenge = ctx =>
{
message += "From OnChallenge:\n";
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
ctx.Response.ContentType = "text/plain";
return ctx.Response.WriteAsync(message);
},
OnMessageReceived = ctx =>
{
message = "From OnMessageReceived:\n";
ctx.Request.Headers.TryGetValue("Authorization", out var BearerToken);
if (BearerToken.Count == 0)
BearerToken = "no Bearer token sent\n";
message += "Authorization Header sent: " + BearerToken + "\n";
return Task.CompletedTask;
},
OnTokenValidated = ctx =>
{
Debug.WriteLine("token: " + ctx.SecurityToken.ToString());
return Task.CompletedTask;
}
};
});
services.AddMvc();
}
Настройка выглядит следующим образом: * 1 023 *
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
IdentityModelEventSource.ShowPII = true;
} else {
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseMvc();
}
И я также добавил этот вызов в AuthenticationFailed, поэтому я буду знать, работает ли моя аутентификация:
Task AuthenticationFailed(AuthenticationFailedContext arg) {
Console.WriteLine(arg.Exception.Message);
return Task.FromResult(0);
}
При моей текущей настройке я получаю Ошибка 401 с сервера, и это сразу после того, как оно попадает в событие OnChallenge, связанное с Startup.cs. Согласно ссылке выше, это то, что вызывается прямо перед тем, как он возвращает 401 пользователю, поэтому создается впечатление, что служба получает правильный токен и аутентифицируется, но, возможно, у меня не настроены правильные права?
Я не уверен, откуда go отсюда, но любые указания будут оценены.
Редактировать:
Как упоминалось в комментарии ниже, я смог свернуть мой сайт, используя токен доступа, сгенерированный после входа в систему через мое приложение, например:
curl https://mywebsite.azurewebsites.net/api/Values -i --header "Авторизация: Bearer [TOKEN]"
И это, похоже, работает без проблем, поэтому кажется, что это связано с тем, как я звоню контроллеру через мое приложение, а не через саму аутентификацию.
Редактировать 2 (решение):
Итак, что касается Правки 1, я был прав в том, что именно так я добавлял токен в заголовок авторизации. Это был не самый яркий момент, но я не звонил. Стоимость заявления, содержащего мой токен доступа. Я вызывал только .ToString () для самой заявки, поэтому «токен» был фактически всем текстом заявки «Access Token:». Я не особо задумывался об этом в то время, когда отлаживал свой сервис, потому что не понимал, что там не должно быть этого текста.
Как только я исправил эту проблему, сервис начал работать, как ожидалось. .
Итак, в конце концов, я думаю, что все работало так, как ожидалось. На самом деле я не отправлял ожидаемый токен, поэтому я ... не авторизован.
В ответ на запрос строка кода, которую мне пришлось изменить, была такой:
Итак, это Это не на 100% применимо к большинству, потому что я использую бизнес-библиотеку под названием CSLA, но идея та же самая, независимо от того.
После того, как мой вызов b2 c возвращает токен, я сохраняю его в ApplicationContext. User.Identity, который встроен в библиотеку CSLA. Это позволяет мне получить претензию на токен доступа позже. Важным моментом, который следует отнять, является то, что я храню токен в каком-то месте, к которому я могу получить доступ позже, когда захочу добавить его в заголовок авторизации.
Позже, когда я делаю вызов с моим httpclient мне нужно получить этот токен, поэтому изначально я делал это:
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue ("Bearer", ((ClaimsIdentity) ApplicationContext.User.Identity) .Claims. FirstOrDefault (c => c .Type == "AccessToken") .ToString () );
Это не правильно. Это отправляло «токен» как со значением «Access Token: [значение токена]. По сути, это было добавление слов «токен доступа» к токену, который мне был необходим для аутентификации, и это не удавалось, потому что слова «токен доступа» фактически не должны быть частью токена, который вы используете для аутентификации.
После того, как я изменил свой вызов на это:
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue ("Bearer", ((ClaimsIdentity) ApplicationContext.User.Identity) .Claims.FirstOrDefault (c => 1080 * .Type == "AccessToken") .Value );
Он начал получать только значение токена, и когда он был добавлен в заголовок авторизации, он работал просто отлично.