如何从 HttpContext 获取 JWT(使用 OpenIdConnect),并传递给 Azure AD Graph API
How to get the JWT (using OpenIdConnect) from HttpContext, and pass to Azure AD Graph API
背景
我们在 2016 年开发了一个应用程序,该应用程序使用 WS-Federation 进行身份验证,以从 on-premises AD 中获取声明。 IT 战略的方向发生了变化,正在转向 Azure AD(目前托管混合环境)。
我们正在使用 OpenIDConnect 将身份验证从 WS-Fed 迁移到 AAD。使用新方法让用户登录并进行身份验证非常简单 - 正确进行配置,然后发出身份验证质询,罗伯特是你母亲的兄弟。
问题
如果我的术语有误,请纠正我;我们需要从 Active Directory 中获取一些无法通过默认 JWT 访问(据我所知)的属性。因此,我们需要通过 HTTP 将 JWT 传递给 Graph API,以从活动目录中获取我们想要的属性。
我知道格式正确且经过身份验证的请求可以提取必要的数据,因为我已经设法使用图形资源管理器(AAD 资源管理器,而不是 Microsoft Graph 资源管理器)查看它。
问题
如果我上面的理解是正确的,我如何从 ASP.Net 中的 HttpContext 中提取 JWT?如果我正确掌握了所有这些较低级别的 HTTP 内容,我需要在请求 header 中为 Graph API 请求包含 JWT,并且我应该获得我需要的 JSON 文档作为回应。
(编辑,为了未来读者的利益:您实际上需要为您尝试访问的特定服务获取一个新令牌,在本例中为 Azure AD。您可以使用 on-behalf-of 流,或使用 as-an-application 流)。
Request.Headers["IdToken"] 返回 null,所以我想知道这里出了什么问题。
代码
这是我们在服务器启动时运行的身份验证配置:
public void Configuration(IAppBuilder app)
{
AntiForgeryConfig.SuppressIdentityHeuristicChecks = true;
//ConfigureAuth(app); //Old WsFed Auth Code
//start the quartz task scheduler
//RCHTaskScheduler.Start();
//Azure AD Configuration
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
//sets client ID, authority, and RedirectUri as obtained from web config
ClientId = clientId,
ClientSecret = appKey,
Authority = authority,
RedirectUri = redirectUrl,
//page that users are redirected to on logout
PostLogoutRedirectUri = redirectUrl,
//scope - the claims that the app will make
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
//setup multi-tennant support here, or set ValidateIssuer = true to config for single tennancy
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
SaveSigninToken = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
这是我制作图表的部分完整代码API 请求:
public static async Task<int> getEmployeeNumber(HttpContextBase context)
{
string token;
int employeeId = -1;
string path = "https://graph.windows.net/<domain>/users/<AAD_USER_ID>?api-version=1.6";
HttpWebRequest request = null;
request = (HttpWebRequest)HttpWebRequest.Create(path);
request.Method = "GET";
request.Headers.Add(context.GetOwinContext().Request.Headers["IdToken"]);
WebResponse response = await request.GetResponseAsync();
throw new NotImplementedException();
}
好吧,我花了几天时间才弄明白(还有 Juunas 的一些指点),但只要对代码稍作修改,这绝对是可行的 here。前面提到的是 Microsoft 的 OpenId 指南。
我肯定会建议您阅读您的具体 authentication scenario,并查看相关样本。
上面的内容会让你进入大门,但是要从 Graph API 中获取 JWT,(不要与 Microsoft Graph 混淆),你需要在验证时获取验证码,并且将其存储在令牌缓存中。
您可以从 Microsoft 的 this 示例中获取可用的令牌缓存(MIT 许可证)。现在,就我个人而言,我发现这些示例过度混淆了复杂的用例,而实际上它们应该概述基础知识,但那只是我。尽管如此,这些足以让你接近。
现在来写一些代码。请允许我提请您注意 'ResponseType= CodeIdToken'。
public void ConfigureAuth(IAppBuilder app)
{
//Azure AD Configuration
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
//sets client ID, authority, and RedirectUri as obtained from web config
ClientId = clientId,
ClientSecret = appKey,
Authority = authority,
RedirectUri = redirectUrl,
//page that users are redirected to on logout
PostLogoutRedirectUri = redirectUrl,
//scope - the claims that the app will make
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
//setup multi-tennant support here, or set ValidateIssuer = true to config for single tennancy
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
//SaveSigninToken = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
}
}
);
}
当提供上述参数时,以下代码将在您进行身份验证时运行:
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
ClientCredential cred = new ClientCredential(clientId, appKey);
string userObjectId = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(authority, new NaiveSessionCache(userObjectId));
// If you create the redirectUri this way, it will contain a trailing slash.
// Make sure you've registered the same exact Uri in the Azure Portal (including the slash).
Uri uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, cred, "https://graph.windows.net");
}
这将为您的令牌缓存提供一个代码,您可以将该代码传递给图形 API。从这里,我们可以尝试使用 Graph API.
进行身份验证
string path = "https://graph.windows.net/me?api-version=1.6";
string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
string userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
string resource = "https://graph.windows.net";
AuthenticationResult result = null;
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
ClientCredential cc = new ClientCredential(ConfigurationManager.AppSettings["ClientId"], ConfigurationManager.AppSettings["ClientSecret"]);
AuthenticationContext auth = new AuthenticationContext(authority, new NaiveSessionCache(userObjectId));
try
{
result = await auth.AcquireTokenSilentAsync(resource,
ConfigurationManager.AppSettings["ClientId"],
new UserIdentifier(userObjectId, UserIdentifierType.UniqueId)).ConfigureAwait(false);
}
catch (AdalSilentTokenAcquisitionException e)
{
result = await auth.AcquireTokenAsync(resource, cc, new UserAssertion(userObjectId));
}
获得身份验证令牌后,您可以通过 Http 请求将其传递给图形 API(这是简单的部分)。
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(path);
request.Method = "GET";
request.Headers.Set(HttpRequestHeader.Authorization, "Bearer " + result.AccessToken);
WebResponse response = request.GetResponse();
System.IO.Stream dataStream = response.GetResponseStream();
从这里开始,您有一个数据流,您可以将其传递到流 reader 中,从中取出 JSON,然后随心所欲。在我的例子中,我只是在目录中查找用户数据,但不包含在 Azure AD 身份验证的默认声明中。所以就我而言,我要呼叫的 URL 是
"https://graph.windows.net/me?api-version=1.6"
如果您需要更深入地了解您的目录,我建议您使用 Graph Explorer。这将帮助您构建 API 调用。现在,我再次发现 Microsoft 文档有点迟钝(如果你想看一些光滑的东西,请去看看 Twilio API)。但是只要你想通了,其实并没有那么糟糕。
背景
我们在 2016 年开发了一个应用程序,该应用程序使用 WS-Federation 进行身份验证,以从 on-premises AD 中获取声明。 IT 战略的方向发生了变化,正在转向 Azure AD(目前托管混合环境)。
我们正在使用 OpenIDConnect 将身份验证从 WS-Fed 迁移到 AAD。使用新方法让用户登录并进行身份验证非常简单 - 正确进行配置,然后发出身份验证质询,罗伯特是你母亲的兄弟。
问题
如果我的术语有误,请纠正我;我们需要从 Active Directory 中获取一些无法通过默认 JWT 访问(据我所知)的属性。因此,我们需要通过 HTTP 将 JWT 传递给 Graph API,以从活动目录中获取我们想要的属性。
我知道格式正确且经过身份验证的请求可以提取必要的数据,因为我已经设法使用图形资源管理器(AAD 资源管理器,而不是 Microsoft Graph 资源管理器)查看它。
问题
如果我上面的理解是正确的,我如何从 ASP.Net 中的 HttpContext 中提取 JWT?如果我正确掌握了所有这些较低级别的 HTTP 内容,我需要在请求 header 中为 Graph API 请求包含 JWT,并且我应该获得我需要的 JSON 文档作为回应。
(编辑,为了未来读者的利益:您实际上需要为您尝试访问的特定服务获取一个新令牌,在本例中为 Azure AD。您可以使用 on-behalf-of 流,或使用 as-an-application 流)。
Request.Headers["IdToken"] 返回 null,所以我想知道这里出了什么问题。
代码 这是我们在服务器启动时运行的身份验证配置:
public void Configuration(IAppBuilder app)
{
AntiForgeryConfig.SuppressIdentityHeuristicChecks = true;
//ConfigureAuth(app); //Old WsFed Auth Code
//start the quartz task scheduler
//RCHTaskScheduler.Start();
//Azure AD Configuration
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
//sets client ID, authority, and RedirectUri as obtained from web config
ClientId = clientId,
ClientSecret = appKey,
Authority = authority,
RedirectUri = redirectUrl,
//page that users are redirected to on logout
PostLogoutRedirectUri = redirectUrl,
//scope - the claims that the app will make
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
//setup multi-tennant support here, or set ValidateIssuer = true to config for single tennancy
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
SaveSigninToken = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed
}
}
);
}
这是我制作图表的部分完整代码API 请求:
public static async Task<int> getEmployeeNumber(HttpContextBase context)
{
string token;
int employeeId = -1;
string path = "https://graph.windows.net/<domain>/users/<AAD_USER_ID>?api-version=1.6";
HttpWebRequest request = null;
request = (HttpWebRequest)HttpWebRequest.Create(path);
request.Method = "GET";
request.Headers.Add(context.GetOwinContext().Request.Headers["IdToken"]);
WebResponse response = await request.GetResponseAsync();
throw new NotImplementedException();
}
好吧,我花了几天时间才弄明白(还有 Juunas 的一些指点),但只要对代码稍作修改,这绝对是可行的 here。前面提到的是 Microsoft 的 OpenId 指南。
我肯定会建议您阅读您的具体 authentication scenario,并查看相关样本。
上面的内容会让你进入大门,但是要从 Graph API 中获取 JWT,(不要与 Microsoft Graph 混淆),你需要在验证时获取验证码,并且将其存储在令牌缓存中。
您可以从 Microsoft 的 this 示例中获取可用的令牌缓存(MIT 许可证)。现在,就我个人而言,我发现这些示例过度混淆了复杂的用例,而实际上它们应该概述基础知识,但那只是我。尽管如此,这些足以让你接近。
现在来写一些代码。请允许我提请您注意 'ResponseType= CodeIdToken'。
public void ConfigureAuth(IAppBuilder app)
{
//Azure AD Configuration
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
//sets client ID, authority, and RedirectUri as obtained from web config
ClientId = clientId,
ClientSecret = appKey,
Authority = authority,
RedirectUri = redirectUrl,
//page that users are redirected to on logout
PostLogoutRedirectUri = redirectUrl,
//scope - the claims that the app will make
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
//setup multi-tennant support here, or set ValidateIssuer = true to config for single tennancy
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
//SaveSigninToken = true
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnAuthenticationFailed,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
}
}
);
}
当提供上述参数时,以下代码将在您进行身份验证时运行:
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
ClientCredential cred = new ClientCredential(clientId, appKey);
string userObjectId = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(authority, new NaiveSessionCache(userObjectId));
// If you create the redirectUri this way, it will contain a trailing slash.
// Make sure you've registered the same exact Uri in the Azure Portal (including the slash).
Uri uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, cred, "https://graph.windows.net");
}
这将为您的令牌缓存提供一个代码,您可以将该代码传递给图形 API。从这里,我们可以尝试使用 Graph API.
进行身份验证 string path = "https://graph.windows.net/me?api-version=1.6";
string tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
string userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
string resource = "https://graph.windows.net";
AuthenticationResult result = null;
string authority = String.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
ClientCredential cc = new ClientCredential(ConfigurationManager.AppSettings["ClientId"], ConfigurationManager.AppSettings["ClientSecret"]);
AuthenticationContext auth = new AuthenticationContext(authority, new NaiveSessionCache(userObjectId));
try
{
result = await auth.AcquireTokenSilentAsync(resource,
ConfigurationManager.AppSettings["ClientId"],
new UserIdentifier(userObjectId, UserIdentifierType.UniqueId)).ConfigureAwait(false);
}
catch (AdalSilentTokenAcquisitionException e)
{
result = await auth.AcquireTokenAsync(resource, cc, new UserAssertion(userObjectId));
}
获得身份验证令牌后,您可以通过 Http 请求将其传递给图形 API(这是简单的部分)。
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(path);
request.Method = "GET";
request.Headers.Set(HttpRequestHeader.Authorization, "Bearer " + result.AccessToken);
WebResponse response = request.GetResponse();
System.IO.Stream dataStream = response.GetResponseStream();
从这里开始,您有一个数据流,您可以将其传递到流 reader 中,从中取出 JSON,然后随心所欲。在我的例子中,我只是在目录中查找用户数据,但不包含在 Azure AD 身份验证的默认声明中。所以就我而言,我要呼叫的 URL 是
"https://graph.windows.net/me?api-version=1.6"
如果您需要更深入地了解您的目录,我建议您使用 Graph Explorer。这将帮助您构建 API 调用。现在,我再次发现 Microsoft 文档有点迟钝(如果你想看一些光滑的东西,请去看看 Twilio API)。但是只要你想通了,其实并没有那么糟糕。