Получить фотографию профиля пользователя из Azure

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

Теперь мне нужно получить фотографию профиля пользователя из лазурного. Я попробовал некоторые решения, представленные в Интернете, но ни одно из них не работает для меня.

Вот мой Startup.Auth.cs код

public partial class Startup
    {
        private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
        private string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
        private string graphResourceID = "https://graph.windows.net";
        private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
        private string authority = aadInstance + "common";
        private ApplicationDbContext db = new ApplicationDbContext();

        public void ConfigureAuth(IAppBuilder app)
        {

            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

            app.UseCookieAuthentication(new CookieAuthenticationOptions { });

            app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                    ClientId = clientId,
                    Authority = authority,
                    TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
                    {
                        // instead of using the default validation (validating against a single issuer value, as we do in line of business apps), 
                        // we inject our own multitenant validation logic
                        ValidateIssuer = false,
                    },
                    Notifications = new OpenIdConnectAuthenticationNotifications()
                    {
                        SecurityTokenValidated = (context) => 
                        {
                            return Task.FromResult(0);
                        },
                        AuthorizationCodeReceived = (context) =>
                        {
                            var code = context.Code;

                            ClientCredential credential = new ClientCredential(clientId, appKey);
                            string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
                            string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;

                            AuthenticationContext authContext = new AuthenticationContext(aadInstance + tenantID, new ADALTokenCache(signedInUserID));
                            AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                                code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceID);

                            return Task.FromResult(0);
                        },
                        AuthenticationFailed = (context) =>
                        {
                            context.OwinContext.Response.Redirect("/Home/Error");
                            context.HandleResponse(); // Suppress the exception
                            return Task.FromResult(0);
                        }
                    }
                });

        }
    }

Вот код для получения основной информации пользователя

        private ApplicationDbContext db = new ApplicationDbContext();
        private string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
        private string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
        private string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
        private string graphResourceID = "https://graph.windows.net";

        // GET: UserProfile
        public async Task<ActionResult> Index()
        {
            string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
            string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
            try
            {
                Uri servicePointUri = new Uri(graphResourceID);
                Uri serviceRoot = new Uri(servicePointUri, tenantID);
                ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot,
                      async () => await GetTokenForApplication());

                // use the token for querying the graph to get the user details

                var result = await activeDirectoryClient.Users
                    .Where(u => u.ObjectId.Equals(userObjectID))
                    .ExecuteAsync();
                IUser user = result.CurrentPage.ToList().First();

                return View(user);
            }
            catch (AdalException)
            {
                // Return to error page.
                return View("Error");
            }
            // if the above failed, the user needs to explicitly re-authenticate for the app to obtain the required token
            catch (Exception)
            {
                return View("Relogin");
            }
        }

        public void RefreshSession()
        {
            HttpContext.GetOwinContext().Authentication.Challenge(
                new AuthenticationProperties { RedirectUri = "/UserProfile" },
                OpenIdConnectAuthenticationDefaults.AuthenticationType);
        }

        public async Task<string> GetTokenForApplication()
        {
            string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
            string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
            string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

            // get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
            ClientCredential clientcred = new ClientCredential(clientId, appKey);
            // initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's database
            AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID, new ADALTokenCache(signedInUserID));
            AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(graphResourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
            return authenticationResult.AccessToken;
        }

Я тоже попробовал это и получаю ошибку

Insufficient privileges to complete the operation.

Мое приложение имеет следующие разрешения

  • Войдите и прочитайте профиль пользователя
  • Читать данные каталога

Код для получения фото пользователя

var servicePoint = new Uri("https://graph.windows.net");
var serviceRoot = new Uri(servicePoint, "<your tenant>"); //e.g. xxx.onmicrosoft.com
const string clientId = "<clientId>";
const string secretKey = "<secretKey>";// ClientID and SecretKey are defined when you register application with Azure AD
var authContext = new AuthenticationContext("https://login.windows.net/<tenant>/oauth2/token");
var credential = new ClientCredential(clientId, secretKey);
ActiveDirectoryClient directoryClient = new ActiveDirectoryClient(serviceRoot, async () =>
{
    var result = await authContext.AcquireTokenAsync("https://graph.windows.net/", credential);
    return result.AccessToken;
});

var user = await directoryClient.Users.Where(x => x.UserPrincipalName == "<username>").ExecuteSingleAsync();
DataServiceStreamResponse photo = await user.ThumbnailPhoto.DownloadAsync();
using (MemoryStream s = new MemoryStream())
{
    photo.Stream.CopyTo(s);
    var encodedImage = Convert.ToBase64String(s.ToArray());
}

2 ответа

Недостаточные права могут означать, что вам нужно добавить некоторые дополнительные разрешения.

Попробуйте предоставить: User.Read разрешений. Подробности смотрите в документах: https://msdn.microsoft.com/en-us/library/azure/ad/graph/howto/azure-ad-graph-api-permission-scopes

Кроме того, попробуйте следовать следующей теме SO о том, как предоставлять разрешения: Graph API - Недостаточно прав для завершения операции.

AFAIK, разрешение " Чтение данных каталога" позволит вашему приложению читать все данные в каталоге организации, такие как пользователи, группы, приложения и т. Д. Я предположил, что это может использовать jwt.io для декодирования вашего access_token и проверить scp и убедитесь, что область действия Directory.Read.All (чтение данных каталога) и User.Read (включение входа и чтение профиля пользователя) существуют следующим образом:

введите описание изображения здесь

Примечание. Разрешение " Чтение данных каталога" является разрешением делегирования и должно быть разрешено администратором. Если вы являетесь администратором AAD, вы можете предоставить разрешение, нажав кнопку " Предоставить разрешения", показанную на следующем снимке экрана:

введите описание изображения здесь

Получив разрешение, вы можете подождать некоторое время, проверить свое приложение и убедиться, что область Directory.Read.All была добавлена ​​в ваш декодированный файл. access_token,

введите описание изображения здесь

ОБНОВИТЬ:

Исходя из вашего кода, вы используете поток учетных данных клиента для получения токена, на данный момент вам необходимо установить РАЗРЕШЕНИЯ НА ПРИЛОЖЕНИЕ для API Windows Azure Active Directory следующим образом:

введите описание изображения здесь

Подождите немного и расшифруйте access_token и проверьте предоставленные разрешения по roles следующее:

введите описание изображения здесь

UPDATE2:

Поскольку вы сказали, что вы не являетесь администратором в каталоге Microsoft, я проверил это на своем собственном AAD, я мог бы предоставить разрешения, и это могло бы работать на моей стороне. Более того, я заметил, что вы используете поток кода и получаете делегированные разрешения для успешного получения базовой информации пользователя. Вы используете поток учетных данных клиента для получения базовой информации пользователя, но у вас нет разрешения на предоставление разрешений. Я предположил, что вы могли бы использовать ADALTokenCache и использовать GetTokenForApplication для получения access_token, а затем получить пользовательское фото.

Другие вопросы по тегам