Я использую ASP.Net Core 2.1 с IdentityCore Service , приложение представляет собой чистый API, никаких представлений вообще.Для аутентификации я просто использую Steam аутентификацию (Нет входа пользователя / пароля), предоставленную, https://github.com/aspnet-contrib/AspNet.Security.OpenId.Providers
Этот API был создан для соответствия очень специфическому рабочему процессу аутентификации (пользователь может тольковойдите в API через Steam) Angular SPA в качестве внешнего интерфейса отлично справляется с рабочим процессом.
Проблема в том, что когда я добавляю роль пользователю (у меня уже естьроли пропущены, и я добавил свою собственную учетную запись Steam в роль администратора), утверждения типа роли не добавляются при входе в систему, поэтому, когда пользователь администратора пытается получить доступ к маршруту API, защищенному [Authorize (Roles = "Admin")Мне возвращают Несанкционированное перенаправление.
Ниже я добавил все фрагменты кода, которые я считаю необходимыми (не стесняйтесь запрашивать больше).
Если я использую (в настоящее время я использую это каквременное решение, но оно не идеально для будущей разработки);
services.AddIdentity<User, Role>()
.AddEntityFrameworkStores<RSContext>()
.AddSignInManager<SignInManager<User>>()
.AddRoleManager<RoleManager<Role>>()
.AddDefaultTokenProviders();
Приложение правильно добавляет заявки на роль при входе пользователя (и атрибуты Authorize work), используя весь существующий код из AuthController.cs, но используя IdentityCore, он не работает.Я чувствую, что мне не хватает одной строки, которая отвечает за это, но после просмотра документов MSDN в течение нескольких дней я, наконец, перехитрил.
ПРИМЕЧАНИЕ: API правильно аутентифицирует и установить cookie-файлы пользователей при входе в систему, но не добавляет роли пользователей к заявкам пользователей.Поэтому аутентификация работает, авторизация - нет.Если я использую атрибут [Authorize] без указания роли, он работает безупречно и позволяет только аутентифицированным пользователям получать доступ к маршруту, в то же время отказывая неаутентифицированным пользователям.Это можно увидеть на скриншоте тестирования в конце: identity [0] .isAuthenticated = True, но роль администратора не добавляется в утверждения Identity.Как отмечалось выше, если я не использую AddIdentityCore и использую AddIdentity, роли будут правильно добавлены в утверждения пользователя, и атрибут [Authorize (Role = "Admin")] будет работать, как и ожидалось, только пользователи, не входящие в состав Adminроль для доступа к нему.
Startup.cs (пропущены нерелевантные части, например, соединение с базой данных)
public void ConfigureServices(IServiceCollection services)
{
IdentityBuilder builder = services.AddIdentityCore<User>(opt =>
{
opt.Password.RequireDigit = true;
opt.Password.RequiredLength = 6;
opt.Password.RequireNonAlphanumeric = true;
opt.Password.RequireUppercase = true;
opt.User.AllowedUserNameCharacters += ":/";
});
builder = new IdentityBuilder(builder.UserType, typeof(Role), builder.Services);
builder.AddEntityFrameworkStores<RSContext>();
builder.AddSignInManager<SignInManager<User>>();
builder.AddRoleValidator<RoleValidator<Role>>();
builder.AddRoles<Role>();
builder.AddRoleManager<RoleManager<Role>>();
builder.AddClaimsPrincipalFactory<UserClaimsPrincipalFactory<User>>();
builder.AddDefaultTokenProviders();
services.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignOutScheme = IdentityConstants.ApplicationScheme;
options.DefaultForbidScheme = IdentityConstants.ApplicationScheme;
})
.AddSteam(options =>
{
options.ApplicationKey = Configuration.GetSection("Authentication:Steam:Key").Value;
options.CallbackPath = "/api/auth/steam/callback";
options.Events.OnAuthenticated = OnClientAuthenticated;
})
.AddIdentityCookies(options =>
{
options.ApplicationCookie.Configure(appCookie =>
{
appCookie.Cookie.Name = "RaidSimulator";
appCookie.LoginPath = "/api/auth/login";
appCookie.LogoutPath = "/api/auth/logout";
appCookie.Cookie.HttpOnly = true;
appCookie.Cookie.SameSite = SameSiteMode.Lax;
appCookie.Cookie.IsEssential = true;
appCookie.SlidingExpiration = true;
appCookie.Cookie.Expiration = TimeSpan.FromMinutes(1);
appCookie.Cookie.MaxAge = TimeSpan.FromDays(7);
});
options.ExternalCookie.Configure(extCookie =>
{
extCookie.Cookie.Name = "ExternalLogin";
extCookie.LoginPath = "/api/auth/login";
extCookie.LogoutPath = "/api/auth/logout";
extCookie.Cookie.HttpOnly = true;
extCookie.Cookie.SameSite = SameSiteMode.Lax;
extCookie.Cookie.IsEssential = true;
extCookie.Cookie.Expiration = TimeSpan.FromMinutes(10);
});
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, RoleManager<Role> roleManager)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
RolesSeed.Seed(roleManager).Wait();
app.UseCors();
app.UseAuthentication();
app.UseMvc();
}
// Responsible for storing/updating steam profile in database
private async Task OnClientAuthenticated(OpenIdAuthenticatedContext context)
{
var rsContext = context.HttpContext.RequestServices.GetRequiredService<RSContext>();
var userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<User>>();
var profile = context.User?.Value<JObject>(SteamAuthenticationConstants.Parameters.Response)
?.Value<JArray>(SteamAuthenticationConstants.Parameters.Players)?[0]?.ToObject<SteamProfile>();
// TODO: Handle this better, Redir user to an informative error page or something
if (profile == null)
return;
var dbProfile = await rsContext.SteamProfiles.FindAsync(profile.SteamId);
if (dbProfile != null)
{
rsContext.Update(dbProfile);
dbProfile.UpdateProfile(profile);
await rsContext.SaveChangesAsync();
}
else
{
await rsContext.SteamProfiles.AddAsync(profile);
await rsContext.SaveChangesAsync();
}
}
AuthController.cs => Единственный код, отвечающий за аутентификацию по схеме Identity.Application.
[HttpGet("callback")]
[Authorize(AuthenticationSchemes = "Steam")]
public async Task<IActionResult> Callback([FromQuery]string ReturnUrl)
{
ReturnUrl = ReturnUrl?.Contains("api/") == true ? "/" : ReturnUrl;
if (HttpContext.User.Claims.Count() > 0)
{
var provider = HttpContext.User.Identity.AuthenticationType;
var nameIdentifier = HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
var name = HttpContext.User.FindFirstValue(ClaimTypes.Name);
var loginResult = await signInManager.ExternalLoginSignInAsync(provider, nameIdentifier, false);
if (loginResult.Succeeded)
{
return Redirect(ReturnUrl ?? "/api/auth/claims");
}
var result = await userManager.CreateAsync(new User { UserName = nameIdentifier, SteamId = nameIdentifier.Split("/").Last() });
if (result.Succeeded)
{
var user = await userManager.FindByNameAsync(nameIdentifier);
var identity = await userManager.AddLoginAsync(user, new UserLoginInfo(provider, nameIdentifier, name));
if (identity.Succeeded)
{
await signInManager.ExternalLoginSignInAsync(provider, nameIdentifier, false);
return Redirect(ReturnUrl ?? "/api/auth/claims");
}
}
}
return BadRequest(new { success = false });
}
[HttpGet("claims")]
[Authorize]
public async Task<IActionResult> GetClaims()
{
var user = await userManager.GetUserAsync(User);
var claims =
User.Claims.Select(c => new
{
c.Type,
c.Value
});
var inAdmin = new string[] {
"User.IsInRole(\"Admin\") = " + User.IsInRole("Admin"),
"User.IsInRole(\"ADMIN\") = " + User.IsInRole("ADMIN"),
"User.IsInRole(\"admin\") = " + User.IsInRole("admin"),
"userManager.IsInRoleAsync(user, \"admin\") = " + await userManager.IsInRoleAsync(user, "admin")
};
return Ok(new { success = true, data = new { claims, inAdmin, User.Identities } });
}
RoleSeeder.cs
public static async Task Seed(RoleManager<Role> roleManager)
{
// Developer Role
if(!await roleManager.RoleExistsAsync("Developer"))
{
var role = new Role("Developer");
await roleManager.CreateAsync(role);
}
// Community Manager Role
if (!await roleManager.RoleExistsAsync("Community Manager"))
{
var role = new Role("Community Manager");
await roleManager.CreateAsync(role);
}
// Admin Role
if (!await roleManager.RoleExistsAsync("Admin"))
{
var role = new Role("Admin");
await roleManager.CreateAsync(role);
}
// Moderator Role
if (!await roleManager.RoleExistsAsync("Moderator"))
{
var role = new Role("Moderator");
await roleManager.CreateAsync(role);
}
}
Снимок экрана тестирования: Заявка / удостоверение личности / роль в тесте API Ответ