英文:
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("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 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?
答案1
得分: 0
不要紧,我找到了问题。在 **Startup.cs** 的 **OnTokenValidated** 方法中,我添加了这一行代码以覆盖来自令牌声明的用户上下文,现在它可以正常工作了。
```C#
OnTokenValidated = async context =>
{
// 先前的代码
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 =>
{
// 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("name")?.Value ?? "System";
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论