Я пытаюсь изменить пароль пользователя Azure AD.
Пользователь уже аутентифицирован в приложении СПА с использованием неявный поток и библиотеки adal
.
Во время вызова:
return await graphClient.Me.Request().UpdateAsync(new User
{
PasswordProfile = new PasswordProfile
{
Password = userPasswordModel.NewPassword,
ForceChangePasswordNextSignIn = false
},
});
Я получаю это исключение:
{"Code: Request_ResourceNotFound\r\nMessage: Resource '35239a3d-67e3-4560-920a-2e9ce027aeab' does not exist or one of its queried reference-property objects are not present.\r\n\r\nInner error\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;
Я использую неявный поток в приложении СПА. При выполнении 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;
но теперь я получаю сообщение об ошибке:
insufficient privileges to complete the operation. authorization_requestdenied
Я немного запутался. Вы упомянули об использовании неявного гранта OAuth, но токен, который вы получаете, содержит Roles
, а не scp
. Роли — это приложения (учетные данные клиента), что подразумевает, что вы не используете здесь неявный. То, что вы хотите, - это неявное использование области Directory.AccessAsUser.All
.
Просто чтобы уточнить, области приложения (roles
) нельзя использовать для сброса паролей пользователей. Только делегированные области поддерживают сброс паролей. Это гарантирует, что любой сброс пароля может быть проверен для конкретного пользователя («глоток, чтобы задохнуться», как они говорят).
@MarcLaFleur Спасибо, что заглянули и разъяснили. Я прибегнул к использованию оригинального clientApp.AcquireTokenForClient
вместо clientApp.AcquireTokenOnBehalfOf
. Написал ответ ниже.
После долгого сеанса отладки (8 часов или около того) я наконец смог получить то, что хотел, после того, как увидел этот ответ от @Michael Mainer.
Это "правильный" код, который я собрал:
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;
}
}
Note 1:
graphClient.Users[graphUser.ObjectId]
is being used instead ofgraphClient.Me
Note 2:
.ToGraphUserAccount()
is from Microsoft.Graph.Auth.
У меня был пример запроса PATCH
в Почтальон, который правильно установил новый пароль для пользователя.
Токен доступа, используемый в заголовке запроса Postman Authorization
, имел тот же формат\свойства, что и тот, который я получал с помощью Microsoft Graph API. Я только что сравнил их, используя jwt.io. Значит, я что-то неправильно назвал...
Вместо этого я использовал clientApp.AcquireTokenForClient
:
var authResult = await clientApp.AcquireTokenForClient(new[] { MSGraphScope }).ExecuteAsync();
return authResult.AccessToken;
куда:
MSGraphScope = "https://graph.microsoft.com/.default"
Сразу после публикации этого вопроса я нашел этот github.com/AzureAD/microsoft-authentication-library-for-dotnet/…