Ресурс "Значение GUID здесь" не существует или один из запрашиваемых объектов ссылочного свойства отсутствует.

Я пытаюсь изменить пароль пользователя Azure AD.

Пользователь уже аутентифицирован в приложении SPA с помощью неявного потока и библиотеки adal.

При звонке:

return await graphClient.Me.Request().UpdateAsync(new User
                {
                    PasswordProfile = new PasswordProfile
                    {
                        Password = userPasswordModel.NewPassword,
                        ForceChangePasswordNextSignIn = false
                    },
                });

Я получаю это исключение:

{"Код: Request_ResourceNotFound \ r \ nMessage: Resource '35239a3d-67e3-4560-920a-2e9ce027aeab' не существует или один из запрошенных объектов ссылочных свойств отсутствует. \ R \ n \ r \ n Внутренняя ошибка \ r \ n "}

Отлаживая токен доступа, который я получил с Microsoft Client SDK, я вижу, что этот GUID относится к свойствам oid и sub. См. ниже:

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

Это код, который я использую для получения токена:

 IConfidentialClientApplication clientApp =
     ConfidentialClientApplicationBuilder.Create(Startup.clientId)
            .WithAuthority(string.Format(AuthorityFormat, Startup.tenant))
            .WithRedirectUri(Startup.redirectUri)
            .WithClientSecret(Startup.clientSecret)
            .Build();

            var authResult = await clientApp.AcquireTokenForClient(new[] { MSGraphScope }).ExecuteAsync();

            return authResult.AccessToken;

Я использую неявный поток в приложении SPA. Выполняя ClaimsPrincipal.Current, я вижу, что мой пользователь аутентифицирован и все претензии присутствуют.

Я прочитал много документов @ GitHub и Microsoft Docs, но мне все еще не ясно, как это реализовать. Кстати, я использую эти пакеты Microsoft Graph:

  <package id="Microsoft.Graph" version="1.15.0" targetFramework="net461" />
  <package id="Microsoft.Graph.Auth" version="0.1.0-preview.2" targetFramework="net461" />
  <package id="Microsoft.Graph.Core" version="1.15.0" targetFramework="net461" />

Думаю, я не приближаюсь к этому соответствию, потому что вместо получения токена для приложения я должен получить токен для пользователя. Должен ли я использовать вместо этого clientApp.AcquireTokenOnBehalfOf?

Если да, то каков рекомендуемый способ получения токена для текущего пользователя, вошедшего в систему, с помощью Microsoft Graph API SDK?

Вы можете пролить свет?

####### ИЗМЕНИТЬ #######

Я смог добиться некоторого прогресса, используя это:

var bearerToken = ClaimsPrincipal.Current.Identities.First().BootstrapContext as string;

JwtSecurityToken jwtToken = new JwtSecurityToken(bearerToken);

var userAssertion = new UserAssertion(jwtToken.RawData, "urn:ietf:params:oauth:grant-type:jwt-bearer");

IEnumerable<string> requestedScopes = jwtToken.Audiences.Select(a => $"{a}/.default");

var authResult = clientApp.AcquireTokenOnBehalfOf(new[] { MSGraphScope }, userAssertion).ExecuteAsync().GetAwaiter().GetResult();

return authResult.AccessToken;

но теперь я получаю сообщение об ошибке:

недостаточно прав для завершения операции. authorization_requestdenied


person Leniel Maccaferri    schedule 11.06.2019    source источник
comment
Сразу после публикации этого вопроса я нашел этот github .com / AzureAD / microsoft-authentication-library-for-dotnet /.   -  person Leniel Maccaferri    schedule 11.06.2019
comment
Я немного запутался. Вы упомянули использование неявного предоставления OAuth, но полученный токен содержит Roles, а не scp. Роли - это функции приложений (учетные данные клиента), что подразумевает, что вы не используете здесь неявный. Вы хотите неявно использовать область Directory.AccessAsUser.All.   -  person Marc LaFleur    schedule 12.06.2019
comment
Чтобы уточнить, области приложений (roles) не могут использоваться для сброса паролей пользователей. Только делегированные области поддерживают сброс паролей. Это гарантирует, что любой сброс пароля может быть проверен конкретному пользователю (как говорится, глотку задыхаться).   -  person Marc LaFleur    schedule 12.06.2019
comment
@MarcLaFleur Спасибо, что заглянули и за разъяснения. Я прибег к использованию оригинального clientApp.AcquireTokenForClient вместо clientApp.AcquireTokenOnBehalfOf. Разместил ответ ниже.   -  person Leniel Maccaferri    schedule 12.06.2019


Ответы (1)


После долгого сеанса отладки (около 8 часов) я наконец смог получить то, что хотел, после того, как увидел этот ответ от @ Майкл Майнер.

Вот "правильный" код, который я собрал:

public async Task<User> ChangeUserPassword(UserPasswordModel userPasswordModel)
{
    try
    {
        var graphUser = ClaimsPrincipal.Current.ToGraphUserAccount();

        var newUserInfo = new User()
        {
            PasswordProfile = new PasswordProfile
            {
                Password = userPasswordModel.NewPassword,
                ForceChangePasswordNextSignIn = false
            },
        };

        // Update the user...
        return await graphClient.Users[graphUser.ObjectId].Request().UpdateAsync(newUserInfo);
    }
    catch(Exception e)
    {
        throw e;
    }
}

Примечание 1: graphClient.Users[graphUser.ObjectId] используется вместо graphClient.Me

Примечание 2: .ToGraphUserAccount() взято из Microsoft.Graph.Auth.

У меня был образец PATCH запроса в Postman, который правильно установил новый пароль для пользователя.

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

Маркер доступа, используемый в Authorization заголовке запроса Postman, имел тот же формат \ свойства, что и тот, который я получал с помощью Microsoft Graph API. Я просто сравнил их с помощью jwt.io. Так что, должно быть, я что-то неправильно назвал ...

Вместо этого я использовал clientApp.AcquireTokenForClient:

var authResult = await clientApp.AcquireTokenForClient(new[] { MSGraphScope }).ExecuteAsync();

return authResult.AccessToken;

куда:

MSGraphScope = "https://graph.microsoft.com/.default"
person Leniel Maccaferri    schedule 12.06.2019