Вызов Graph API через ViewModel в веб-приложении MVCC#

Место общения программистов C#
Ответить
Anonymous
 Вызов Graph API через ViewModel в веб-приложении MVC

Сообщение Anonymous »

Я пытаюсь использовать Graph API для создания собственного раздела «Профиль пользователя» на панели навигации моего веб-приложения. Для этого у меня есть AJAX-вызов действия GetUser моего контроллера UserProfile:

Код: Выделить всё

$.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 не возвращает никаких данных.

Токен доступа из журнала IIS
Журнал консоли
У меня есть два вопроса (возможно, 3):
  • Что я делаю неправильно?
  • Можно ли использовать токен доступа из моего Startup.Auth для создания аутентифицированного клиента Graph? Если да, то как бы я это сделал?

    Код: Выделить всё

    // 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. При разработке приложения я создал еще один контекст БД для базы данных SQL Azure, которую я создал для приложения. Мне пришлось обновить файл AdalTokenCache.cs, чтобы отразить контекст БД моего приложения и новую модель. Я обновил строку:

Код: Выделить всё

private ApplicationDbContext db = new ApplicationDbContext();
с моим собственным контекстом и обновил модель UserTokenCache до модели UserTokenCache моего нового контекста. В данном случае я изменил:

Код: Выделить всё

private UserTokenCache Cache;
кому:

Код: Выделить всё

private UserTokenCach Cache;
Затем я обновил остальную часть CS, чтобы она соответствовала UserTokenCache из контекста БД приложения.
Затем я просто использовал метод AcquireToken, который появился OOB в контроллере UserProfile, чтобы получить токен. Вот как это выглядело в итоге (Примечание: я также обновил строки в файле start.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;
}
}
Я буду обновлять информацию по мере того, как поиграюсь.

Подробнее здесь: https://stackoverflow.com/questions/447 ... vc-web-app
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»