我正在尝试使用Graph API在Web应用程序的导航栏中创建自己的“用户个人资料”部分。为此,我需要对UserProfile Controller的GetUser Action进行AJAX调用:
$.ajax({
type: "GET",
url: "@Url.Action("GetUser", "UserProfile", null)",
dataType: "json",
success: function (data, status, xhr) {
console.log("in AJAX");
$(".img-circle, .user-image").attr("src", data.Picture);
$("#user-menu-expanded").text(data.User.DisplayName + " - " + data.User.JobTitle);
$("#user-menu-spinner").remove();
console.log(data);
},
error: function (ex) {
console.log(ex);
}
});
控制器将我的UserProfileViewModel作为Json返回,我用它来替换上述元素,如AJAX成功函数中所示。
UserProfile控制器:
public JsonResult GetUser()
{
var model = new UserProfileViewModel();
return Json(model, JsonRequestBehavior.AllowGet);
}
我的UserProfileViewModel看起来像这样:
public UserProfileViewModel()
{
var graphClient = GetAuthGraphClient();
GetPicture(graphClient);
GetUserProfile(graphClient);
}
public GraphServiceClient GetAuthGraphClient()
{
string graphResourceID = "https://graph.microsoft.com/";
return new GraphServiceClient(
new DelegateAuthenticationProvider((requestMessage) =>
{
string accessToken = GetTokenForApplication(graphResourceID);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
return Task.FromResult(0);
}
));
}
public string GetTokenForApplication(string graphResourceID)
{
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;
string authority = "https://login.microsoftonline.com/" + tenantID;
try {
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(authority);
var token = authenticationContext.AcquireTokenAsync(graphResourceID, clientcred).Result.AccessToken;
return token;
}
catch (Exception e)
{
// Capture error for handling outside of catch block
ErrorMessage = e.Message;
return null;
}
}
public void GetPicture(GraphServiceClient graphClient)
{
Stream photo = Task.Run(async () => { return await graphClient.Me.Photo.Content.Request().GetAsync(); }).Result;
using (var memoryStream = new MemoryStream())
{
photo.CopyTo(memoryStream);
var base64pic = Convert.ToBase64String(memoryStream.ToArray());
this.Picture = "data:image;base64," + base64pic;
HttpContext.Current.Cache.Add("Pic", this.Picture, null, DateTime.Now.AddHours(5), Cache.NoSlidingExpiration, CacheItemPriority.AboveNormal, null);
}
}
public void GetUserProfile(GraphServiceClient graphClient)
{
this.User = Task.Run(async () => { return await graphClient.Me.Request().GetAsync(); }).Result;
}
我已成功获取访问令牌,但是我的AJAX调用未返回任何数据。
Access Token from IIS Log
Console Log
我有两个问题(可能是3个):
我究竟做错了什么?
是否可以使用访问令牌
从我的Startup.Auth创建经过身份验证的Graph Client?如果是这样的话,
我将如何去做?
// This is the resource ID of the AAD Graph API. We'll need this to request a token to call the Graph API.
string graphResourceId = "https://graph.microsoft.com"; //https://graph.windows.net
public void ConfigureAuth(IAppBuilder app)
{
ApplicationDbContext db = new ApplicationDbContext();
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseKentorOwinCookieSaver();
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
HttpContext.Current.Cache.Add("Token", result.AccessToken, null, DateTime.Now.AddHours(5), Cache.NoSlidingExpiration, CacheItemPriority.AboveNormal, null);
return Task.FromResult(0);
}
}
});
}
}
以下每个注释的更新代码
public string GetTokenForApplication(string graphResourceID)
{
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;
string authority = "https://login.microsoftonline.com/" + tenantID;
try {
// 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(Startup.Authority, new ADALTokenCache(userObjectID));
var result = authenticationContext.AcquireTokenSilent(graphResourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
return result.AccessToken;
}
catch (Exception e)
{
// Capture error for handling outside of catch block
ErrorMessage = e.Message;
return null;
}
}
更新2:修复..
感谢@Fei Xue,我找出了问题..有点。这解决了我在本地运行时的问题,但是当发布到我的舞台应用程序时,我仍然无法以静默方式获取令牌。当我首次创建该应用程序时,我包括了工作/学校身份验证,即Azure AD。这将创建用于ADAL令牌缓存的本地数据库上下文。在开发应用程序时,我为我为该应用程序创建的Azure SQL数据库创建了另一个数据库上下文。我必须更新我的AdalTokenCache.cs以反映我的应用程序的数据库上下文和新模型。我更新了这一行:
private ApplicationDbContext db = new ApplicationDbContext();
使用我自己的上下文,并将UserTokenCache模型更新为新上下文的UserTokenCache模型。在这种情况下,我更改了:
private UserTokenCache Cache;
至:
private UserTokenCach Cache;
然后,我更新了其余CS,以匹配应用程序数据库上下文中的UserTokenCach。
然后,我只使用了UserProfile控制器中OOB附带的AcquireToken方法来获取令牌。这就是它看起来像的样子(注意:我也将startup.auth中的字符串从私有更新为公共,以便可以在视图模型中使用它们):
public string GetTokenForApplication(string graphResourceID)
{
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;
string authority = "https://login.microsoftonline.com/" + tenantID;
try {
// get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
ClientCredential clientcred = new ClientCredential(Startup.clientId, Startup.appKey);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's database
AuthenticationContext authenticationContext = new AuthenticationContext(Startup.Authority, new ADALTokenCache(signedInUserID));
var result = authenticationContext.AcquireTokenSilent(graphResourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
return result.AccessToken;
}
catch (Exception e)
{
// Capture error for handling outside of catch block
ErrorMessage = e.Message;
return null;
}
}
我会随着其他游戏的更新而更新。
最佳答案
Azure Active Directory发出两种访问令牌。
第一个是委托令牌,用于委托用户操作用户资源。
另一个是应用程序令牌,通常用于对所有组织的资源执行操作,并且该令牌中没有用户上下文。因此,我们不应使用此令牌将资源作为me
执行,这需要用户上下文。
帖子中的代码使用客户端凭证流(即应用程序令牌)获取访问令牌。因此,当您基于用户的上下文使用这种标记来获取用户或图片时,您将获得错误。
在这种情况下,您应该在发布时使用AuthorizationCodeReceived
事件获取访问令牌。此事件使用授权码授予流来获取用户的委托令牌。然后在控制器中,您可以使用方法AcquireTokenSilentAsync
获取令牌,该方法将从catch中获取访问令牌。
下面的代码示例对于在Web应用程序中调用Microsoft Graph委派登录用户的方案非常有帮助:
active-directory-dotnet-graphapi-web
关于c# - 通过MVC Web App中的ViewModel调用Graph API,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/44722002/