Open dalbard opened 1 year ago
Interesting, I'll take a look and see if that would be possible with the data the claims supplies
I had a little look.. doesn't seem to be anything from the claims that can be used, but did find two aproaches.
restapi call.. but I can't seem to get an access_token from the externalLogin, and nothing is being stored in the umbracoExternalLoginToken table?
I've added options.SaveTokens = true;
and that gives me an id_token
but doesn't seem to let me use that against the graphapi
string? accessToken = loginInfo.AuthenticationTokens?.FirstOrDefault(t => t.Name.Equals("id_token"))?.Value;
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var pictureResult = httpClient.GetAsync("https://graph.microsoft.com/v1.0/me/photo/$value").Result;
Then stumbled on maybe using MicrosoftGraph package by adding .AddMicrosoftGraph()
after EnableTokenAcquisitionToCallDownstreamApi(..)
and changing scopes to var initialScopes = new string[] { "user.read" };
but then get issues with Cannot consume scoped service 'Microsoft.Graph.GraphServiceClient' from singleton 'Microsoft.Extensions.Options.IOptionsMonitor'
using (var photoStream = await _graphServiceClient.Me.Photo.Content.Request().GetAsync())
{
byte[] photoByte = ((MemoryStream)photoStream).ToArray();
}
beyond my skills at this point. :-)
So turned out just had to update Microsoft.Identity.Web
latest is 2.13.0 and then add options.SaveTokens = true
to the AddMicrosoftIdentityWebApp
config
And heh presto access_token
is now present.
and I can fetch the picture from the msgraph api
Final piece to the puzzle, though not sure .Result
for that GetAsync is correct.. though didn't get far with async await up the method tree before method signatures started complaining.
private readonly AzureSsoSettings _settings;
private readonly IUserService _userService;
private readonly MediaFileManager _mediaFileManager;
public MicrosoftAccountBackOfficeExternalLoginProviderOptions(AzureSsoSettings settings, IUserService userService, MediaFileManager mediaFileManager)
{
_settings = settings;
_userService = userService;
_mediaFileManager = mediaFileManager;
}
if (loginInfo.AuthenticationTokens?.FirstOrDefault(t => t.Name.Equals("access_token"))?.Value is string accessToken)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var pictureResult = httpClient.GetAsync("https://graph.microsoft.com/v1.0/me/photo/$value").Result;
if (pictureResult.IsSuccessStatusCode)
{
if (_userService.GetByUsername(user.UserName) is User u && pictureResult.Headers.ETag is EntityTagHeaderValue etag)
{
//etag : identifier for a specific version of a resource
u.Avatar = $"UserAvatars/{etag.ToString().GenerateHash<SHA1>()}.jpg";
if (u.IsDirty())
{
using (Stream fs = pictureResult.Content.ReadAsStream())
{
_mediaFileManager.FileSystem.AddFile(u.Avatar, fs, true);
}
_userService.Save(u);
}
}
}
}
}
UserAvatars in the core here.. https://github.com/umbraco/Umbraco-CMS/blob/contrib/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs#L185-L196C1
Final piece to the puzzle, though not sure
.Result
for that GetAsync is correct.. though didn't get far with async await up the method tree before method signatures started complaining.private readonly AzureSsoSettings _settings; private readonly IUserService _userService; private readonly MediaFileManager _mediaFileManager; public MicrosoftAccountBackOfficeExternalLoginProviderOptions(AzureSsoSettings settings, IUserService userService, MediaFileManager mediaFileManager) { _settings = settings; _userService = userService; _mediaFileManager = mediaFileManager; }
if (loginInfo.AuthenticationTokens?.FirstOrDefault(t => t.Name.Equals("access_token"))?.Value is string accessToken) { using (var httpClient = new HttpClient()) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var pictureResult = httpClient.GetAsync("https://graph.microsoft.com/v1.0/me/photo/$value").Result; if (pictureResult.IsSuccessStatusCode) { if (_userService.GetByUsername(user.UserName) is User u && pictureResult.Headers.ETag is EntityTagHeaderValue etag) { //etag : identifier for a specific version of a resource u.Avatar = $"UserAvatars/{etag.ToString().GenerateHash<SHA1>()}.jpg"; if (u.IsDirty()) { using (Stream fs = pictureResult.Content.ReadAsStream()) { _mediaFileManager.FileSystem.AddFile(u.Avatar, fs, true); } _userService.Save(u); } } } } }
UserAvatars in the core here.. https://github.com/umbraco/Umbraco-CMS/blob/contrib/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs#L185-L196C1
Great findings! Does this update your avatar? I've just tested the code but it doesn't seem to work for me... maybe I'm not calling the function properly.
As the title suggests, how would you go about modifying the code to fetch the logged in user's Azure AD profile image and set it as their Umbraco Avatar?