Azure Active Directory 中的简单目录查找
Simple Directory Lookup in Azure Active Directory
我正在编写一个简单的桌面应用程序,它需要从 Microsoft 的目录中检索有关用户的一些基本属性。具体来说:
- 我正在编写单租户本机 LOB 应用程序。
- 应用程序 运行 在我的桌面上。
- 应用程序 运行 作为我登录的域帐户。
- 组织的域帐户已同步到 AAD。
- 我不是要保护本机 Web 应用程序或 Web API 或类似的东西。我不需要用户登录。
- 我通过外部事件管理工具获得了组织中人员的电子邮件地址。我需要根据电子邮件地址从 AAD 中查找 AAD 帐户配置文件数据(地址簿信息 - 特别是职位)。我只会读取 AAD 数据。
到目前为止,我做了以下事情:-
看来 Azure AD Graph API 是获取配置文件信息的正确方法。特别是,该信息可在端点获得:https://graph.windows.net/{tenant}/users/{email}?api-version=1.6
在 AAD 中注册本机应用程序时,未提供密钥。所以我没有客户机密。
查看了 GitHub 中的样本:https://github.com/Azure-Samples/active-directory-dotnet-graphapi-console。此处的说明似乎是错误的,因为没有可用的键部分 [参见 (2)]。
基于上面的示例,我写了一个简单的函数。代码如下:
private static async Task PrintAADUserData(string email)
{
string clientId = "0a202b2c-6220-438d-9501-036d4e05037f";
Uri redirectUri = new Uri("http://localhost:4000");
string resource = "https://graph.windows.net/{tenant}";
string authority = "https://login.microsoftonline.com/{tenant}/oauth2/authorize";
AuthenticationContext authContext = new AuthenticationContext(authority);
AuthenticationResult authResult = await authContext.AcquireTokenAsync(resource, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
string api = String.Format("https://graph.windows.net/{tenant}/users/{0}?api-version=1.6", email);
LOG.DebugFormat("Using API URL {0}", api);
// Create an HTTP client and add the token to the Authorization header
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authResult.AccessTokenType, authResult.AccessToken);
HttpResponseMessage response = await httpClient.GetAsync(api);
string data = await response.Content.ReadAsStringAsync();
LOG.Debug(data);
}
问题
运行时的应用程序能够调出认证页面。为什么我需要那个?该应用程序已经 运行 作为我的域帐户。是否需要额外的身份验证?如果我要 运行 Azure 中的这个应用程序作为工作进程,那么我不想使用我的域凭据。
主要问题似乎是资源 URL 是错误的。我需要指定什么资源才能访问 Azure AD Graph API?
谢谢,
维杰.
编辑
根据@Saca 的评论,对代码和应用程序进行了编辑。
代码
string clientId = ConfigurationManager.AppSettings["AADClientId"];
string clientSecret = ConfigurationManager.AppSettings["AADClientSecret"];
string appIdUri = ConfigurationManager.AppSettings["AADAppIdURI"];
string authEndpoint = ConfigurationManager.AppSettings["AADGraphAuthority"];
string graphEndpoint = ConfigurationManager.AppSettings["AADGraphEndpoint"];
AuthenticationContext authContext = new AuthenticationContext(authEndpoint, false);
AuthenticationResult authResult = await authContext.AcquireTokenAsync("https://graph.windows.net", new ClientCredential(clientId, clientSecret));
ExistingTokenWrapper wrapper = new ExistingTokenWrapper(authResult.AccessToken);
ActiveDirectoryClient client = new ActiveDirectoryClient(new Uri(graphEndpoint), async () => await wrapper.GetToken());
IUser user = client.Users.Where(_ => _.UserPrincipalName.Equals(email.ToLowerInvariant())).Take(1).ExecuteSingleAsync().Result;
应用程序
错误
未处理的异常:System.AggregateException:发生一个或多个错误。 ---> System.AggregateException: 发生一个或多个错误。 ---> Microsoft.Data.OData.ODataErrorException: 权限不足,无法完成操作。 ---> System.Data.Services.Client.DataServiceQueryException: 处理此请求时发生错误。 ---> System.Data.Services.Client.DataServiceClientException: {"odata.error":{"code":"Authorization_RequestDenied","message":{"lang":"en","value":"Insufficient privileges to complete the operation."}}}
看来,尽管提供了正确的权限、正确的资源并能够获取令牌,但仍然缺少一些东西。
这里要考虑的关键是您的应用程序是来自安全服务器的无外设客户端 运行 还是用户在其计算机上 运行 的桌面客户端。
如果是前者,那么您的应用程序被认为是机密客户端并且可以用秘密(即密钥)来信任。如果这是您的场景,也就是示例涵盖的场景,那么您需要使用 clientId 和 clientSecret。
您在应用程序的 Configure 页面中没有看到 Keys 部分的最可能原因是,而不是选择 Web 应用程序 and/or Web API 根据示例中的第 7 步,您在首次创建应用程序时选择了 Native Client Application .此 "type" 无法更改,因此您需要创建一个新应用程序。
如果您的情况是后者,那么您的应用程序将被视为 public 客户端 并且不能信任机密,在这种情况下,您唯一的选择是提示用户输入凭据。否则,即使您的应用程序有自己的授权层,也很容易被反编译并提取和使用秘密。
顺便说一下,您的资源 URL 是正确的。
原来真正的问题不在于代码。我不是 AAD 管理员。似乎任何需要在我们的租户中针对 AAD 执行身份验证的应用程序都需要具有 AAD 管理员启用的权限。一旦他们为我的应用程序启用了权限(并且也获得了 AAD 注册的所有权),这就开始工作了。
希望对正在使用 GraphClient 的人有所帮助:
var userPriNam = "johndoe@cloudalloc.com";
var userLookupTask = activeDirectoryClient.Users.Where(
user => user.UserPrincipalName.Equals(userPriNam, StringComparison.CurrentCultureIgnoreCase)).ExecuteSingleAsync();
User userJohnDoe = (User)await userLookupTask;
来自 https://www.simple-talk.com/cloud/security-and-compliance/azure-active-directory-part-5-graph-api/
我正在编写一个简单的桌面应用程序,它需要从 Microsoft 的目录中检索有关用户的一些基本属性。具体来说:
- 我正在编写单租户本机 LOB 应用程序。
- 应用程序 运行 在我的桌面上。
- 应用程序 运行 作为我登录的域帐户。
- 组织的域帐户已同步到 AAD。
- 我不是要保护本机 Web 应用程序或 Web API 或类似的东西。我不需要用户登录。
- 我通过外部事件管理工具获得了组织中人员的电子邮件地址。我需要根据电子邮件地址从 AAD 中查找 AAD 帐户配置文件数据(地址簿信息 - 特别是职位)。我只会读取 AAD 数据。
到目前为止,我做了以下事情:-
看来 Azure AD Graph API 是获取配置文件信息的正确方法。特别是,该信息可在端点获得:https://graph.windows.net/{tenant}/users/{email}?api-version=1.6
在 AAD 中注册本机应用程序时,未提供密钥。所以我没有客户机密。
查看了 GitHub 中的样本:https://github.com/Azure-Samples/active-directory-dotnet-graphapi-console。此处的说明似乎是错误的,因为没有可用的键部分 [参见 (2)]。
基于上面的示例,我写了一个简单的函数。代码如下:
private static async Task PrintAADUserData(string email)
{
string clientId = "0a202b2c-6220-438d-9501-036d4e05037f";
Uri redirectUri = new Uri("http://localhost:4000");
string resource = "https://graph.windows.net/{tenant}";
string authority = "https://login.microsoftonline.com/{tenant}/oauth2/authorize";
AuthenticationContext authContext = new AuthenticationContext(authority);
AuthenticationResult authResult = await authContext.AcquireTokenAsync(resource, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
string api = String.Format("https://graph.windows.net/{tenant}/users/{0}?api-version=1.6", email);
LOG.DebugFormat("Using API URL {0}", api);
// Create an HTTP client and add the token to the Authorization header
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(authResult.AccessTokenType, authResult.AccessToken);
HttpResponseMessage response = await httpClient.GetAsync(api);
string data = await response.Content.ReadAsStringAsync();
LOG.Debug(data);
}
问题
运行时的应用程序能够调出认证页面。为什么我需要那个?该应用程序已经 运行 作为我的域帐户。是否需要额外的身份验证?如果我要 运行 Azure 中的这个应用程序作为工作进程,那么我不想使用我的域凭据。
主要问题似乎是资源 URL 是错误的。我需要指定什么资源才能访问 Azure AD Graph API?
谢谢,
维杰.
编辑
根据@Saca 的评论,对代码和应用程序进行了编辑。
代码
string clientId = ConfigurationManager.AppSettings["AADClientId"];
string clientSecret = ConfigurationManager.AppSettings["AADClientSecret"];
string appIdUri = ConfigurationManager.AppSettings["AADAppIdURI"];
string authEndpoint = ConfigurationManager.AppSettings["AADGraphAuthority"];
string graphEndpoint = ConfigurationManager.AppSettings["AADGraphEndpoint"];
AuthenticationContext authContext = new AuthenticationContext(authEndpoint, false);
AuthenticationResult authResult = await authContext.AcquireTokenAsync("https://graph.windows.net", new ClientCredential(clientId, clientSecret));
ExistingTokenWrapper wrapper = new ExistingTokenWrapper(authResult.AccessToken);
ActiveDirectoryClient client = new ActiveDirectoryClient(new Uri(graphEndpoint), async () => await wrapper.GetToken());
IUser user = client.Users.Where(_ => _.UserPrincipalName.Equals(email.ToLowerInvariant())).Take(1).ExecuteSingleAsync().Result;
应用程序
看来,尽管提供了正确的权限、正确的资源并能够获取令牌,但仍然缺少一些东西。
这里要考虑的关键是您的应用程序是来自安全服务器的无外设客户端 运行 还是用户在其计算机上 运行 的桌面客户端。
如果是前者,那么您的应用程序被认为是机密客户端并且可以用秘密(即密钥)来信任。如果这是您的场景,也就是示例涵盖的场景,那么您需要使用 clientId 和 clientSecret。
您在应用程序的 Configure 页面中没有看到 Keys 部分的最可能原因是,而不是选择 Web 应用程序 and/or Web API 根据示例中的第 7 步,您在首次创建应用程序时选择了 Native Client Application .此 "type" 无法更改,因此您需要创建一个新应用程序。
如果您的情况是后者,那么您的应用程序将被视为 public 客户端 并且不能信任机密,在这种情况下,您唯一的选择是提示用户输入凭据。否则,即使您的应用程序有自己的授权层,也很容易被反编译并提取和使用秘密。
顺便说一下,您的资源 URL 是正确的。
原来真正的问题不在于代码。我不是 AAD 管理员。似乎任何需要在我们的租户中针对 AAD 执行身份验证的应用程序都需要具有 AAD 管理员启用的权限。一旦他们为我的应用程序启用了权限(并且也获得了 AAD 注册的所有权),这就开始工作了。
希望对正在使用 GraphClient 的人有所帮助:
var userPriNam = "johndoe@cloudalloc.com";
var userLookupTask = activeDirectoryClient.Users.Where(
user => user.UserPrincipalName.Equals(userPriNam, StringComparison.CurrentCultureIgnoreCase)).ExecuteSingleAsync();
User userJohnDoe = (User)await userLookupTask;
来自 https://www.simple-talk.com/cloud/security-and-compliance/azure-active-directory-part-5-graph-api/