我正在尝试使用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/

10-15 01:00