我有一个应用程序设置,使用Azure AD(工作或学校帐户)将用户登录到应用程序。我还希望能够使用委托权限从此应用程序查询Microsoft Graph。我相信我设置的是使用应用程序权限,但我没有看到我错过了正确设置此设置。该应用程序是ASP.NET Core 2应用程序。
在我的Startup.cs中,我有以下代码。
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddAzureAd(options => Configuration.Bind("AzureAd", options))
.AddCookie();
services.AddMvc();
services.AddSingleton(Configuration);
services.AddSingleton<IGraphAuthProvider, GraphAuthProvider>();
services.AddTransient<IGraphSDKHelper, GraphSDKHelper>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
AddAzureAd是这里定义的扩展方法:
与Marc合作后 我发现问题是下面的OnAuthorizationCodeReceived事件永远不会触发。我不知道为什么,但这是问题的根源。
public static class AzureAdAuthenticationBuilderExtensions
{
public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder)
=> builder.AddAzureAd(_ => { });
public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder, Action<AzureAdOptions> configureOptions)
{
builder.Services.Configure(configureOptions);
builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, ConfigureAzureOptions>();
builder.AddOpenIdConnect(opts =>
{
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async ctx =>
{
var request = ctx.HttpContext.Request;
var currentUri =
UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
var credential = new ClientCredential(ctx.Options.ClientId, ctx.Options.ClientSecret);
var distributedCache = ctx.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
var userId = ctx.Principal
.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")
.Value;
var cache = new AdalDistributedTokenCache(distributedCache, userId);
var authContext = new AuthenticationContext(ctx.Options.Authority, cache);
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(ctx.ProtocolMessage.Code,
new Uri(currentUri), credential, ctx.Options.Resource);
ctx.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
};
});
return builder;
}
private class ConfigureAzureOptions: IConfigureNamedOptions<OpenIdConnectOptions>
{
private readonly AzureAdOptions _azureOptions;
public ConfigureAzureOptions(IOptions<AzureAdOptions> azureOptions)
{
_azureOptions = azureOptions.Value;
}
public void Configure(string name, OpenIdConnectOptions options)
{
options.ClientId = _azureOptions.ClientId;
options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
options.UseTokenLifetime = true;
options.CallbackPath = _azureOptions.CallbackPath;
options.RequireHttpsMetadata = false;
}
public void Configure(OpenIdConnectOptions options)
{
Configure(Options.DefaultName, options);
}
}
}
AdalDistributeTokenCache类如下所示:
internal class AdalDistributedTokenCache : TokenCache
{
private readonly IDistributedCache cache;
private readonly string userId;
public AdalDistributedTokenCache(IDistributedCache cache, string userId)
{
this.cache = cache;
this.userId = userId;
BeforeAccess = OnBeforeAccess;
AfterAccess = OnAfterAccess;
}
private void OnBeforeAccess(TokenCacheNotificationArgs args)
{
var userTokenCachePayload = cache.Get(CacheKey);
if (userTokenCachePayload != null)
{
Deserialize(userTokenCachePayload);
}
}
private void OnAfterAccess(TokenCacheNotificationArgs args)
{
if (HasStateChanged)
{
var cacheOptions = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(14)
};
cache.Set(CacheKey, Serialize(), cacheOptions);
HasStateChanged = false;
}
}
private string CacheKey => $"TokenCache_{userId}";
}
}
从我读过的所有内容来看,这似乎是我正在尝试做的正确设置。现在在主页控制器中我有以下代码
public async Task<IActionResult> Index(string email)
{
// Get users's email.
email = email ?? User.Identity.Name;
ViewData["Email"] = email;
// Get user's id for token cache.
var identifier = User.FindFirst(Startup.ObjectIdentifierType)?.Value;
// Initialize the GraphServiceClient.
var graphClient = _graphSdkHelper.GetAuthenticatedClient(identifier);
ViewData["Response"] = await GraphService.GetUserJson(graphClient, email);
ViewData["Response"] = await GraphService.GetMeJson(graphClient, email);
//ViewData["Picture"] = await GraphService.GetPictureBase64(graphClient, email);
var groups = await GraphService.GetGroups(graphClient);
return View();
}
表示GetUserJson工作的行并调用一个看起来像这样的方法
var user = await graphClient.Users[email].Request().GetAsync();
return JsonConvert.SerializeObject(user, Formatting.Indented);
说GetMeJson的行确实起作用并且通过错误看起来像
var user = await graphClient.Me.Request().GetAsync();
return JsonConvert.SerializeObject(user, Formatting.Indented);
最后一段代码是GetAuthenticatedClient,它看起来像
public GraphServiceClient GetAuthenticatedClient(string userId)
{
_graphClient = new GraphServiceClient("https://graph.microsoft.com/beta", new DelegateAuthenticationProvider(
async (requestMessage) =>
{
// Passing tenant ID to the sample auth provider to use as a cache key
AccessToken = await _authProvider.GetUserAccessTokenAsync(userId);
// Append the access token to the request
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
}));
return _graphClient;
}
我得到的错误信息是:
Microsoft.Graph.ServiceException:
Code: Request_ResourceNotFound
Message: Resource '40f381c1-55f3-4295-99e3-0309a704374e' does not
exist or one of its queried reference-property objects
are not present.
有人可以帮助我克服这个障碍,所以我可以将Graph API称为经过身份验证的用户。
*********** Marc *************的其他信息请求
该应用程序通过portal.azure.com注册,并具有以下权限。
在Microsoft Graph下 在应用程序权限下,User.Read.All,Directory.Read.All,Group.Read.All 在委派权限,个人资料(查看用户&#39;基本个人资料),电子邮件(查看用户&#39;电子邮件地址),Directory.Read.All。 Group.Read.All,User.Read.All,User.ReadBasic.All
在Windows Azure Active Directory下 在委派权限下:User.Read(登录并阅读用户配置文件)
我现在知道我不应该需要应用程序权限,并且可以在某些时候删除它们,但我没有权限这样做,需要让其他人为我这样做。< / p>
**********调试令牌********* 我在第
行放了一个断点AccessToken = await _authProvider.GetUserAccessTokenAsync(userId);
然后拿起它到达的AccessToken并将其放入calebb.net并获得以下结果。
{
typ: "JWT",
nonce: "bunch of letters",
alg: "RS256",
x5t: "bunch of letters",
kid: "bunch of letters"
}.
{
aud: "https://graph.microsoft.com/",
iss: "https://sts.windows.net/dde15eeb-a6bd-46a2-9f03-d44429074ecf/",
iat: 1503942101,
nbf: 1503942101,
exp: 1503946001,
aio: "bunch of letters",
app_displayname: "Test App",
appid: "Guid",
appidacr: "1",
e_exp: 262800,
idp: "https://sts.windows.net/dde15eeb-a6bd-46a2-9f03-d44429074ecf/",
oid: "Guid",
roles: [
"Group.Read.All",
"Directory.Read.All",
"User.Read.All"
],
sub: "Guid",
tid: "Guid",
uti: "bunch of letters",
ver: "1.0"
}.
[signature]
您怀疑它仅作为应用程序权限而不是委托权限。那么它获取令牌的这一部分有什么问题。我在这里显然没有做正确的身份验证,但我不知道自己做错了什么。
public async Task<string> GetUserAccessTokenAsync(string userId)
{
TokenCache userTokenCache = new SessionTokenCache(userId, _memoryCache).GetCacheInstance();
try
{
AuthenticationContext authContext = new AuthenticationContext(_aadInstance, userTokenCache);
ClientCredential credential = new ClientCredential(_appId, _appSecret);
AuthenticationResult result = await authContext.AcquireTokenAsync(_graphResourceId, credential);
return result.AccessToken;
}
catch (Exception ex)
{
return null;
}
}