Как работает управление состояниями претензий в Blazor Server?
Я реализовал Identity Server для потока кода аутентификации.
Как правильно отстаивать претензии (в
OnTicketReceived
илиOnTicketValidated
как показано ниже), чтобы при последующих вызовах страниц Blazor я мог получатьUser aka ClaimPrincipal
заселен для моего использования?
Вот код промежуточного программного обеспечения моего сервера ресурсов:
public void ConfigureServices(IServiceCollection services)
{
//....
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.SignIn.RequireConfirmedAccount = false;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
options.SignIn.RequireConfirmedEmail = false;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<SomeContext>()
.AddDefaultTokenProviders();
Интеграция промежуточного программного обеспечения:
services.AddAuthentication(options =>
{
options.DefaultScheme = "cookie";
options.DefaultChallengeScheme = "oidc";
options.DefaultSignOutScheme = "oidc";
})
.AddCookie("cookie", options =>
{
options.Cookie.Name = "__Host-bff";
options.Cookie.SameSite = SameSiteMode.Strict;
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://localhost:5001";
options.ClientId = "mvc.code";
options.ClientSecret = "secret";
options.ResponseType = "code";
options.ResponseMode = "query";
options.GetClaimsFromUserInfoEndpoint = true;
options.MapInboundClaims = false;
options.SaveTokens = true;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
//Critical Parts
options.TokenValidationParameters = new()
{
NameClaimType = "name",
RoleClaimType = "role"
};
Обратный вызов с аутентификацией от сервера идентификации выглядит следующим образом:
options.Events.OnTicketReceived = async n =>
{
var serviceProvider = n.HttpContext.RequestServices;
var accountService = serviceProvider.GetService<IAccountService>() ?? throw new ArgumentNullException("serviceProvider.GetService<IAccountService>()");
Я пробовал использовать BlazoredSessionStorage и т. Д., Но, кажется, слишком рано вызывать это. Нам нужно подождать, пока OnPrerender или OnInit
Я тоже пробовал
CustomTokenStore
. Но как запрос от cookie возвращается на сервер?
var svc = n.HttpContext.RequestServices.GetRequiredService<IUserAccessTokenStore>();
if (n.Principal != null)
{
var userName = n.Principal.FindFirst(x => x.Type == "name")?.Value;
await accountService.UserCreateAsync(new NewAccount
{
Username = userName,
FirstName = userFirstName,
LastName = userLastName,
//ContactId = 100,
TenantId = 1
});
await (authProvider as SomeAuthenticationStateProvider).LoginAsync(new AuthenticationLogin { Username = userName }, 24 * 60);
}
};
public class CustomTokenStore : IUserAccessTokenStore
{
ConcurrentDictionary<string, UserAccessToken> _tokens = new ConcurrentDictionary<string, UserAccessToken>();
public Task ClearTokenAsync(ClaimsPrincipal user, UserAccessTokenParameters parameters = null)
{
var sub = user.FindFirst("sub").Value;
_tokens.TryRemove(sub, out _);
return Task.CompletedTask;
}
public Task<UserAccessToken> GetTokenAsync(ClaimsPrincipal user, UserAccessTokenParameters parameters = null)
{
var sub = user.FindFirst("sub").Value;
_tokens.TryGetValue(sub, out var value);
return Task.FromResult(value);
}
public Task StoreTokenAsync(ClaimsPrincipal user, string accessToken, DateTimeOffset expiration, string refreshToken = null, UserAccessTokenParameters parameters = null)
{
var sub = user.FindFirst("sub").Value;
var token = new UserAccessToken
{
AccessToken = accessToken,
Expiration = expiration,
RefreshToken = refreshToken
};
_tokens[sub] = token;
return Task.CompletedTask;
}
}
3 ответа
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.ClaimActions.Add(new CustomClaimsFactory(
"userName",
"XXXXX@outlook.com"
));
options.TokenValidationParameters = new TokenValidationParameters
{
// Instead of using the default validation (validating against a single issuer value, as we do in
// line of business apps), we inject our own multitenant validation logic
ValidateIssuer = false,
// If the app is meant to be accessed by entire organizations, add your issuer validation logic here.
//IssuerValidator = (issuer, securityToken, validationParameters) => {
// if (myIssuerValidationLogic(issuer)) return issuer;
//}
};
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = context =>
{
// If your authentication logic is based on users then add your logic here
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Response.Redirect("/Error");
context.HandleResponse(); // Suppress the exception
return Task.CompletedTask;
},
// If your application needs to do authenticate single users, add your user validation below.
//OnTokenValidated = context =>
//{
// var claims = new List<Claim>
// {
// new Claim(ClaimTypes.Role, "superadmin")
// };
// var appIdentity = new ClaimsIdentity(claims);
// context.Principal.AddIdentity(appIdentity);
// return Task.CompletedTask;
// //return myUserValidationLogic(context.Ticket.Principal);
//}
};
});
Хотя класс CustomActionFactory определен ниже
public class CustomClaimsFactory : ClaimAction
{
string _ClaimType;
string _ValueType;
public CustomClaimsFactory(string claimType, string valueType) : base(claimType, valueType)
{
_ClaimType = claimType;
_ValueType = valueType;
}
public override void Run(JObject userData, ClaimsIdentity identity, string issuer)
{
identity.AddClaim(new Claim(_ClaimType, _ValueType, issuer));
}
}```
Предлагаем использовать класс ClaimAction, вот ссылка. Он также работает для asp net core 3.x
OnTokenValidated имеет метод для добавления нового удостоверения, которое вы создаете с утверждением:
OnTokenValidated = ctx =>{ ctx.Principal.AddIdentity(myNewClaim); }