存储身份验证后的显示名称。

huangapple go评论59阅读模式
英文:

Store DisplayName after Authentication

问题

I am building a WebAPI and I have some metadata columns in my model like InsertUser and UpdateUser. I am using Microsoft.Identity to authenticate users like so

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddMicrosoftIdentityWebApi(_configuration.GetSection("AzureAd"))
  .EnableTokenAcquisitionToCallDownstreamApi()
  .AddMicrosoftGraph(_configuration.GetSection("MicrosoftGraph"))
  .AddInMemoryTokenCaches();

I want to make a call to Graph after successful authentication and store the DisplayName somewhere so I can use it in my DataContext in SaveChanges. How can I achieve this?

DataContext.cs

public override int SaveChanges()
{
    var entries = ChangeTracker
        .Entries()
        .Where(e => e.Entity is BaseEntity && (
                e.State == EntityState.Added
                || e.State == EntityState.Modified));

    foreach (var entityEntry in entries)
    {
        ((BaseEntity)entityEntry.Entity).UpdateTimestamp = DateTime.UtcNow;
        ((BaseEntity)entityEntry.Entity).UpdateUser = "System"; //Change to logged in user

        if (entityEntry.State == EntityState.Added)
        {
            ((BaseEntity)entityEntry.Entity).InsertTimestamp = DateTime.UtcNow;
            ((BaseEntity)entityEntry.Entity).InsertUser = "System"; //Change to logged in user
        }
    }

    return base.SaveChanges();
}

I tried following some tutorials and have tried storing it into the ClaimsIdentity

Startup.cs

services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
    options.Events ??= new JwtBearerEvents
    {
        OnTokenValidated = async context =>
        {
            var userProfileService = context.HttpContext.RequestServices.GetRequiredService<IUserProfile>();
            var user = await userProfileService.RetrieveUserProfileAsync();

            var identity = context.Principal.Identity as ClaimsIdentity;
            if (identity != null)
            {
                identity.AddClaim(new Claim("DisplayName", user.DisplayName));
            }
        }
    };
});
services.AddScoped<IUserProfile, UserProfile>();

UserProfile.cs

using Microsoft.Graph;

namespace WebAPI.Services
{
    public interface IUserProfile
    {
        Task<User> RetrieveUserProfileAsync();
    }
    public class UserProfile : IUserProfile
    {
        private readonly GraphServiceClient _graphServiceClient;

        public UserProfile(GraphServiceClient graphServiceClient)
        {
            _graphServiceClient = graphServiceClient;
        }

        public async Task<User> RetrieveUserProfileAsync()
        {
            var user = await _graphServiceClient.Me.Request().GetAsync();
            return user;
        }
    }
}

And to test I've added this when an API call is made

var claimsIdentity = _httpContextAccessor.HttpContext?.User?.Identity as ClaimsIdentity;
var userProfileDisplayName = claimsIdentity?.FindFirst("DisplayName")?.Value;
Console.WriteLine($"User: {userProfileDisplayName}");

But it prints just User: . What am I doing wrong and how could I fix it?

英文:

I am building a WebAPI and I have some metadata columns in my model like InsertUser and UpdateUser. I am using Microsoft.Identity to authenticate users like so

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddMicrosoftIdentityWebApi(_configuration.GetSection(&quot;AzureAd&quot;))
  .EnableTokenAcquisitionToCallDownstreamApi()
  .AddMicrosoftGraph(_configuration.GetSection(&quot;MicrosoftGraph&quot;))
  .AddInMemoryTokenCaches();

I want to make a call to Graph after successful authentication and store the DisplayName somewhere so I can use it in my DataContext in SaveChanges. How can achieve this?

DataContext.cs

public override int SaveChanges()
{
    var entries = ChangeTracker
        .Entries()
        .Where(e =&gt; e.Entity is BaseEntity &amp;&amp; (
                e.State == EntityState.Added
                || e.State == EntityState.Modified));

    foreach (var entityEntry in entries)
    {
        ((BaseEntity)entityEntry.Entity).UpdateTimestamp = DateTime.UtcNow;
        ((BaseEntity)entityEntry.Entity).UpdateUser = &quot;System&quot;; //Change to logged in user

        if (entityEntry.State == EntityState.Added)
        {
            ((BaseEntity)entityEntry.Entity).InsertTimestamp = DateTime.UtcNow;
            ((BaseEntity)entityEntry.Entity).InsertUser = &quot;System&quot;; //Change to logged in user
        }
    }

    return base.SaveChanges();
}

I tried following some tutorials and have tried storing it into the ClaimsIdentity

Startup.cs

services.Configure&lt;JwtBearerOptions&gt;(JwtBearerDefaults.AuthenticationScheme, options =&gt;
{
    options.Events ??= new JwtBearerEvents
    {
        OnTokenValidated = async context =&gt;
        {
            var userProfileService = context.HttpContext.RequestServices.GetRequiredService&lt;IUserProfile&gt;();
            var user = await userProfileService.RetrieveUserProfileAsync();

            var identity = context.Principal.Identity as ClaimsIdentity;
            if (identity != null)
            {
                identity.AddClaim(new Claim(&quot;DisplayName&quot;, user.DisplayName));
            }
        }
    };
});
services.AddScoped&lt;IUserProfile, UserProfile&gt;();

UserProfile.cs

using Microsoft.Graph;

namespace WebAPI.Services
{
    public interface IUserProfile
    {
        Task&lt;User&gt; RetrieveUserProfileAsync();
    }
    public class UserProfile : IUserProfile
    {
        private readonly GraphServiceClient _graphServiceClient;

        public UserProfile(GraphServiceClient graphServiceClient)
        {
            _graphServiceClient = graphServiceClient;
        }

        public async Task&lt;User&gt; RetrieveUserProfileAsync()
        {
            var user = await _graphServiceClient.Me.Request().GetAsync();
            return user;
        }
    }
}

And to test I've added this when an api call is made

var claimsIdentity = _httpContextAccessor.HttpContext?.User?.Identity as ClaimsIdentity;
var userProfileDisplayName = claimsIdentity?.FindFirst(&quot;DisplayName&quot;)?.Value;
Console.WriteLine($&quot;User: {userProfileDisplayName}&quot;);

But it prints just User: . What am I doing wrong and how could I fix it?

答案1

得分: 0

不要紧,我找到了问题。在 **Startup.cs**  **OnTokenValidated** 方法中,我添加了这一行代码以覆盖来自令牌声明的用户上下文,现在它可以正常工作了。

```C#
OnTokenValidated = async context =&gt;
{
  // 先前的代码

  var identity = context.Principal.Identity as ClaimsIdentity;
  context.HttpContext.User = new ClaimsPrincipal(identity) ?? context.HttpContext.User;

  // 其余的代码
}

所以在我的 DataContext.cs 中,我可以这样存储显示名称:

var currentUserDisplayName = _httpContextAccessor.HttpContext?.User?.FindFirst("name")?.Value ?? "System";

<details>
<summary>英文:</summary>

Nevermind, I found the issue. In **Startup.cs**, in **OnTokenValidated** I added this line to overwrite the User context from the token claims and it works now.

```C#
OnTokenValidated = async context =&gt;
{
  // Prior Code

  var identity = context.Principal.Identity as ClaimsIdentity;
  context.HttpContext.User = new ClaimsPrincipal(identity) ?? context.HttpContext.User;

  // Rest of the Code
}

So in my DataContext.cs I can store the display name like so

var currentUserDisplayName = _httpContextAccessor.HttpContext?.User?.FindFirst(&quot;name&quot;)?.Value ?? &quot;System&quot;;

huangapple
  • 本文由 发表于 2023年5月26日 01:04:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76334707.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定