#c# #authentication #azure-active-directory #blazor #blazor-webassembly
Вопрос:
У меня есть веб-приложение blazor для сборки. У него есть проверка подлинности Azure AD для проверки подлинности страниц и API-это работает, У него есть проверка подлинности токенов sql JWS для проверки подлинности страниц и API — это работает
проблема в том, что я пытаюсь включить их оба. Мне нужна моя пользовательская область проверки подлинности, добавленная в program.cs на клиенте для проверки подлинности токена JWS, когда я это делаю, я получаю эту ошибку при попытке войти с помощью Azure auth ncaught (in promise) Error: System.ArgumentException: There is no event handler associated with this event. EventId: '62'. (Parameter 'eventHandlerId') at Microsoft.AspNetCore.Components.RenderTree.Renderer.DispatchEventAsync(UInt64 eventHandlerId, EventFieldInfo fieldInfo, EventArgs eventArgs) at Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer.DispatchEventAsync(UInt64 eventHandlerId, EventFieldInfo eventFieldInfo, EventArgs eventArgs) at Microsoft.AspNetCore.Components.WebAssembly.Infrastructure.JSInteropMethods.DispatchEvent(WebEventDescriptor eventDescriptor, String eventArgsJson) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) --- End of stack trace from previous location --- at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfoamp; callInfo, IDotNetObjectReference objectReference, String argsJson) at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, String argsJson) at Object.endInvokeDotNetFromJS (https://localhost:5001/_framework/blazor.webassembly.js:1:4191) at Object.invokeJSFromDotNet (https://localhost:5001/_framework/blazor.webassembly.js:1:3797) at Object.w [as invokeJSFromDotNet] (https://localhost:5001/_framework/blazor.webassembly.js:1:64301) at _mono_wasm_invoke_js_blazor (https://localhost:5001/_framework/dotnet.5.0.9.js:1:190800) at do_icall (<anonymous>:wasm-function[10596]:0x194e4e) at do_icall_wrapper (<anonymous>:wasm-function[3305]:0x79df9) at interp_exec_method (<anonymous>:wasm-function[2155]:0x44ad3) at interp_runtime_invoke (<anonymous>:wasm-function[7862]:0x12efff) at mono_jit_runtime_invoke (<anonymous>:wasm-function[7347]:0x118e5f) at do_runtime_invoke (<anonymous>:wasm-function[3304]:0x79d42)
Вот мой пользовательский аутентификатор stateProvider
public class ApiAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly HttpClient _httpClient;
private readonly ILocalStorageService _localStorage;
public ApiAuthenticationStateProvider(HttpClient httpClient, ILocalStorageService localStorage)
{
_httpClient = httpClient;
_localStorage = localStorage;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var savedToken = await _localStorage.GetItemAsync<string>("authToken");
if (string.IsNullOrWhiteSpace(savedToken))
{
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
}
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", savedToken);
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(savedToken), "jwt")));
}
public void MarkUserAsAuthenticated(string email)
{
var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, email) }, "apiauth"));
var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
NotifyAuthenticationStateChanged(authState);
}
public void MarkUserAsLoggedOut()
{
var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
var authState = Task.FromResult(new AuthenticationState(anonymousUser));
NotifyAuthenticationStateChanged(authState);
}
private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
{
var claims = new List<Claim>();
var payload = jwt.Split('.')[1];
var jsonBytes = ParseBase64WithoutPadding(payload);
var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);
keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);
if (roles != null)
{
if (roles.ToString().Trim().StartsWith("["))
{
var parsedRoles = JsonSerializer.Deserialize<string[]>(roles.ToString());
foreach (var parsedRole in parsedRoles)
{
claims.Add(new Claim(ClaimTypes.Role, parsedRole));
}
}
else
{
claims.Add(new Claim(ClaimTypes.Role, roles.ToString()));
}
keyValuePairs.Remove(ClaimTypes.Role);
}
claims.AddRange(keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString())));
return claims;
}
private byte[] ParseBase64WithoutPadding(string base64)
{
switch (base64.Length % 4)
{
case 2: base64 = "=="; break;
case 3: base64 = "="; break;
}
return Convert.FromBase64String(base64);
}
}
}
and I am adding it in to program.cs by
builder.Services.AddScoped<AuthenticationStateProvider, ApiAuthenticationStateProviderClient>();
и запуск его с помощью этой службы аутентификации учетной записи
public interface IAccountService
{
User User { get; }
Task Initialize();
Task<LoginResult> Login(LoginRequest model);
Task Logout();
}
public class AccountService : IAccountService
{
// private IHttpService _httpService;
private NavigationManager _navigationManager;
// private ILocalStorageService _localStorageService;
private string _userKey = "user";
private readonly HttpClient _httpService;
private readonly AuthenticationStateProvider _authenticationStateProvider;
private readonly ILocalStorageService _localStorage;
public User User { get; private set; }
public AccountService(
HttpClient httpService,
AuthenticationStateProvider authenticationStateProvider,
ILocalStorageService localStorageService
) {
_httpService = httpService;
_authenticationStateProvider = authenticationStateProvider;
_localStorage = localStorageService;
}
public async Task Initialize()
{
User = await _localStorage.GetItemAsync<User>(_userKey);
}
public async Task<LoginResult> Login(LoginRequest model)
{
AuthCredentials authCredentials = new AuthCredentials()
{
Username = model.Email,
Password = model.Password
};
try
{
var loginAsJson = JsonSerializer.Serialize(authCredentials);
var response = await _httpService.PostAsync("api/auth/login", new StringContent(loginAsJson, Encoding.UTF8, "application/json"));
var loginResult = JsonSerializer.Deserialize<LoginResult>(await response.Content.ReadAsStringAsync(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
if (loginResult.Successful == false)
return null;
await _localStorage.SetItemAsync("authToken", loginResult.Token);
((ApiAuthenticationStateProviderClient)_authenticationStateProvider).NotifyUserAuthentication(loginResult.Token);
_httpService.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", loginResult.Token);
return loginResu<
}
catch (Exception ex)
{
string message = ex.Message;
return null;
}
}
public async Task Logout()
{
await _localStorage.ClearAsync();
((ApiAuthenticationStateProviderClient)_authenticationStateProvider).MarkUserAsLoggedOut();
_httpService.DefaultRequestHeaders.Clear();
_navigationManager.NavigateTo("/");
}
}
}
любая помощь будет очень признательна. Я знаю, что оба метода аутентификации работают на 100%, если я разрешу только один из них. Просто не могу ни за что на свете получить это, чтобы пользователи могли пройти аутентификацию с помощью любого из них в зависимости от того, какую кнопку входа они нажимают
Ответ №1:
Это известная ошибка в blazor.
Ошибка, о которой вы упомянули выше, означает, что у вас проблема с обработчиком событий.
Да, Microsoft выпустила новую версию, вы можете обновить свое приложение.
Для обходного пути вы можете использовать await Task.Yield()
.
Например, вот как будет выглядеть обновление вашего кода:
private a sync Task Callback(KeyboardEventArgs args)
{
if (args.Key == "Enter")
{
await Task.Yield();
showInput = false;
}
}
Комментарии:
1. Спасибо, я решил использовать Auth0, так как не думал, что есть обходной путь