[英]Azure AD - Pull All Users Basic Info into a List
我是Azure的所有新手,並且正在使用ASP.NET MVC 4模板項目。
我的目標是將所有來自Azure AD的用戶拉入一個可枚舉的列表,以后可以在其中進行搜索。
目前,我收到以下錯誤之一:
Server Error in '/' Application
Object reference not set to an instance of an object
...
Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.
還是這個,取決於我注釋掉哪個.Where(...)
子句:
The token for accessing the Graph API has expired. Click here to sign-in and get a new access token.
單擊鏈接將調用以下URL:
https://login.microsoftonline.com/<MY TENANT GUID>/oauth2/authorize?client_id=<MY APP ID>&response_mode=form_post&response_type=code+id_token&scope=openid+profile&state=OpenIdConnect.AuthenticationProperties%<Bunch of alphanumeric gibberish>&nonce=<More alphanumeric gibberish>-client-SKU=ID_NET&x-client-ver=1.0.40306.1554
單擊該鏈接會嘗試執行某些操作,但只會使我回到具有相同錯誤的同一頁面上,並且不會執行其他任何操作。
UserProfileController.cs
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";
public async Task<Collection<IUser>> GetAllUsers()
{
var userList = new Collection<IUser>();
try
{
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
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.JobTitle.Equals("Cool Dudes")) // Works fine when uncommented, otherwise gives me a server error
.ExecuteAsync();
while (result.MorePagesAvailable)
{
userList = userList.Concat(result.CurrentPage.ToList()) as Collection<IUser>;
await result.GetNextPageAsync();
}
}
catch (Exception e)
{
if (Request.QueryString["reauth"] == "True")
{
// Send an OpenID Connect sign-on request to get a new set of tokens.
// If the user still has a valid session with Azure AD, they will not
// be prompted for their credentials.
// The OpenID Connect middleware will return to this controller after
// the sign-in response has been handled.
HttpContext.GetOwinContext()
.Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
return userList;
}
return userList;
}
public async Task<ActionResult> Admin()
{
try
{
var user = await GetAllUsers();
return View(user
//.Where(u => u.JobTitle.Equals("Cool Dudes")) // When this is uncommented and the one in GetAllUsers is commented out, I get an error saying "The token for accessing the Graph API has expired. Click here to sign-in and get a new access token."
);
}
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;
}
Admin.cshtml
@using Microsoft.Azure.ActiveDirectory.GraphClient
@model IEnumerable<IUser>
@{
ViewBag.Title = "Admin";
}
<h2>@ViewBag.Title.</h2>
<table class="table table-bordered table-striped">
@foreach (var user in Model)
{
<tr>
<td>Display Name</td>
<td>@user.DisplayName</td>
<td>Job Title</td>
<td>@user.JobTitle</td>
</tr>
}
</table>
我在這里想念什么? 我的while循環邏輯錯了嗎? 我也許正在使用一種過時的方式來讀取此信息? 是權限問題嗎?
編輯:
縮小范圍:
GetAllUsers
(以及可選的Admin
)具有Where
子句時, Admin
返回一個空頁面,但沒有錯誤 Admin
具有Where
子句時,將返回圖形錯誤 Where
子句時,將返回服務器錯誤 因此,我認為GetAllUsers
無法正確返回數據。
根據喬納森·胡斯(Jonathan Huss)的這篇博客文章 ,我能夠將代碼的這一部分從項目默認的Azure AD Graph API
轉換為較新的Microsoft Graph API
在我的Models文件夾(可能放置在Utility文件夾中)中,添加以下代碼:
AzureAuthenticationProvider.cs
using System.Configuration;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace <PROJECT_NAME>.Models
{
class AzureAuthenticationProvider : IAuthenticationProvider
{
private string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
private string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
// get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
ClientCredential creds = 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 authResult = await authenticationContext.AcquireTokenAsync("https://graph.microsoft.com/", creds);
request.Headers.Add("Authorization", "Bearer " + authResult.AccessToken);
}
}
}
回到UserProfileController.cs中,我們有:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Linq;
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;
using System.Threading.Tasks;
using Microsoft.Azure.ActiveDirectory.GraphClient; // Will eventually be removed
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OpenIdConnect;
using <PROJECT_NAME>.Models;
using Microsoft.Graph;
using User = Microsoft.Graph.User; // This is only here while I work on removing references to Microsoft.Azure.ActiveDirectory.GraphClient
namespace <PROJECT_NAME>.Controllers
{
[Authorize]
public class UserProfileController : Controller
{
public async Task<List<User>> GetAllUsers()
{
List<User> userResult = new List<User>();
GraphServiceClient graphClient = new GraphServiceClient(new AzureAuthenticationProvider());
IGraphServiceUsersCollectionPage users = await graphClient.Users.Request().Top(500).GetAsync(); // The hard coded Top(500) is what allows me to pull all the users, the blog post did this on a param passed in
userResult.AddRange(users);
while (users.NextPageRequest != null)
{
users = await users.NextPageRequest.GetAsync();
userResult.AddRange(users);
}
return userResult;
}
// Return all users from Azure AD as a proof of concept
public async Task<ActionResult> Admin()
{
try
{
var user = await GetAllUsers();
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");
}
}
}
}
我的原始帖子中的RefreshSession
和GetTokenForApplication
方法仍然存在,但是當我重新編寫代碼時,可能會被AzureAuthenticationProvider
類替換。
最后,我對Admin.cshtml進行了小改動
@using Microsoft.Azure.ActiveDirectory.GraphClient
@model IEnumerable<IUser>
至
@using Microsoft.Graph
@model List<User>
根據錯誤消息,它不應該與where
原因有關。 該問題是由於令牌已過期引起的。
在這種情況下,由於不使用當前用戶的上下文,因此您可以使用客戶端憑據流獲取應用程序令牌而不是委托令牌。 對於此流程,可以使用AcquireTokenAsync(string resource, ClientCredential clientCredential)
。
請告訴我們是否有幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.