Как добавить пользовательскую заявку в файл cookie аутентификации, сгенерированный промежуточным программным обеспечением OpenIdConnect в ASP.Net Core после аутентификации? - PullRequest
0 голосов
/ 02 мая 2019

У меня есть проект приложения ASP.Net Core, в котором используется поток гибридной аутентификации IdentityServer4. Это настроено следующим образом,

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    }).AddCookie("Cookies")
      .AddOpenIdConnect("oidc", options =>
      {   
          options.Authority = IdentityServerUrl;
          options.RequireHttpsMetadata = false;

          options.ClientId = ClientId;
          options.ClientSecret = ClientSecret;
          options.ResponseType = "code id_token";
          options.SaveTokens = true;

          options.GetClaimsFromUserInfoEndpoint = true;
          options.Scope.Add("openid");
          options.Scope.Add("profile");
          options.Scope.Add("email");
          options.Scope.Add("offline_access");
          options.Scope.Add("ApiAuthorizedBasedOnIdentity");
          options.GetClaimsFromUserInfoEndpoint = true;
          options.TokenValidationParameters.NameClaimType = JwtClaimTypes.Name;
          options.TokenValidationParameters.RoleClaimType = JwtClaimTypes.Role;                  
      });

    //Setup Tenant Role based authorization
    services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();

    services.AddProxy();
}

Я могу аутентифицироваться, и SaveTokens = true успешно сохраняет маркер доступа в файле cookie аутентификации ASP.Net. Теперь мне нужно добавить пользовательское утверждение к этому же файлу аутентификации из действия контроллера ( Не через промежуточное ПО ) в моем клиентском проекте ASP.Net Core. Скажем, например, действие Index в HomeController.

Мне также нужно, чтобы это требование сохранялось в файле cookie аутентификации, чтобы оно сохранялось при выполнении запросов и действий контроллера.

Я немного покопался и заметил, что могу сделать это с ASP.Net Identity

if (User.Identity.IsAuthenticated)
{
    var claimsIdentity = ((ClaimsIdentity)User.Identity);
    if (!claimsIdentity.HasClaim(c => c.Type == "your-claim"))
    {
        ((ClaimsIdentity)User.Identity).AddClaim(new Claim("your-claim", "your-value"));

        var appUser = await userManager.GetUserAsync(User).ConfigureAwait(false);
        await signInManager.RefreshSignInAsync(appUser).ConfigureAwait(false);
    }
}

Аутентификация выполняется IdentityServer с использованием ASP.Net Identity, который настроен в этом проекте. Однако чтобы использовать SignInManager, UserManager и т. Д. В клиентском проекте, мне нужно будет внести в него ASP.Net Identity. Настройка удостоверения ASP.Net и хранилищ в клиентском проекте также для обновления куки-файла аутентификации с дополнительным требованием выглядит излишним. Есть ли другой способ сделать это?

Ответы [ 2 ]

2 голосов
/ 02 мая 2019

Вам, конечно, не нужно включать ASP.NET Core Identity в ваш клиентский проект, но вы можете использовать его для получения вдохновения о том, как добиться того, что вы ищете. Давайте начнем с рассмотрения реализации RefreshSignInAsync:

public virtual async Task RefreshSignInAsync(TUser user)
{
    var auth = await Context.AuthenticateAsync(IdentityConstants.ApplicationScheme);
    var authenticationMethod = auth?.Principal?.FindFirstValue(ClaimTypes.AuthenticationMethod);
    await SignInAsync(user, auth?.Properties, authenticationMethod);
}

Как видно выше, это также вызывает SignInAsync, который выглядит следующим образом:

public virtual async Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties, string authenticationMethod = null)
{
    var userPrincipal = await CreateUserPrincipalAsync(user);
    // Review: should we guard against CreateUserPrincipal returning null?
    if (authenticationMethod != null)
    {
        userPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.AuthenticationMethod, authenticationMethod));
    }
    await Context.SignInAsync(IdentityConstants.ApplicationScheme,
        userPrincipal,
        authenticationProperties ?? new AuthenticationProperties());
}

Два вызова, которые нас больше всего интересуют:

  1. Context.AuthenticateAsync, который создает AuthenticateResult, содержащий как ClaimsPrincipal, так и AuthenticationProperties, которые были прочитаны из файла cookie.
  2. Context.SignInAsync, что в итоге переписывает куки с ClaimsPrincipal и связанными AuthenticationProperties.

ASP.NET Core Identity создает совершенно новый ClaimsPrincipal, который обычно берется из базы данных, чтобы «обновить» его. Вам не нужно этого делать, поскольку вы просто хотите использовать существующий ClaimsPrincipal с дополнительной претензией. Вот полное решение для ваших требований:

var authenticateResult = await HttpContext.AuthenticateAsync();

if (authenticateResult.Succeeded)
{
    var claimsIdentity = (ClaimsIdentity)authenticateResult.Principal.Identity;

    if (!claimsIdentity.HasClaim(c => c.Type == "your-claim"))
    {
        claimsIdentity.AddClaim(new Claim("your-claim", "your-value"));

        await HttpContext.SignInAsync(authenticateResult.Principal, authenticateResult.Properties);
    }
}

При вызове HttpContext.AuthenticateAsync будет использоваться схема по умолчанию, которую вы уже настроили в своей конфигурации ("Cookies"), чтобы получить доступ к ClaimsPrincipal и AuthenticationProperties. После этого это просто случай добавления новой заявки и выполнения вызова на HttpContext.SignInAsync, который также будет использовать схему по умолчанию ("Cookies").

0 голосов
/ 02 мая 2019

Опция 1

Вам нужно будет использовать HttpContext.SignInAsync метод.Кроме того, вам необходимо обновить HttpContext.User новым ClaimsPrincipal, как только вы добавите дополнительные претензии к пользователю.Пожалуйста, смотрите код ниже:

var identity = (ClaimsIdentity)User.Identity;
identity.AddClaim(new Claim("your-claim", "your-value"));

// genereate the new ClaimsPrincipal
var claimsPrincipal = new ClaimsPrincipal(identity);

// store the original tokens in the AuthenticationProperties
var props = new AuthenticationProperties();

// get the current tokens
var accessToken = await HttpContext.GetTokenAsync("access_token");
var refreshToken = await HttpContext.GetTokenAsync("refresh_token");

// create the enumerable list
var tokens = new List<AuthenticationToken>
{
    new AuthenticationToken {Name = "access_token", Value = accessToken},
    new AuthenticationToken {Name = "refresh_token", Value = refreshToken}
};

//store the tokens
props.StoreTokens(tokens);

// update the thread's current principal as it is changed, otherwise 
// System.Security.Claims.ClaimsPrincipal.Current is referring to the 
// ClaimsPrincipal created from the cookie on the initial request. This is required 
// so that the next instance of HttpContext will be injected with the updated claims
HttpContext.User = claimsPrincipal;
Thread.CurrentPrincipal = claimsPrincipal;

// sign in using the built-in Authentication Manager and ClaimsPrincipal
// this will create a cookie as defined in CookieAuthentication middleware
await HttpContext.SignInAsync("your scheme", claimsPrincipal, props);

Пожалуйста, убедитесь, что вы заменили «свою схему» на имя схемы, которую вы используете.Надеюсь, это поможет.

Вариант 2

Правильно указал @Ruard van Elburg, что приведенное выше решение переопределит маркер доступа.( обновлено, чтобы разрешить сохранение исходного токена )

Если вы добавляете заявки сразу после того, как пользователь зарегистрировался, вы можете использовать событие OnTokenValidated

.AddOpenIdConnect("oidc", options =>
{
    options.Events = new OpenIdConnectEvents
    {
        OnTokenValidated = async ctx =>
        {
            var claim = new Claim("your-claim", "your-value");

            var identity = new ClaimsIdentity(new[] { claim });

            ctx.Principal.AddIdentity(identity);

            await Task.CompletedTask;
        }
    };
}
...