如何从 HttpContext 获取 JWT(使用 OpenIdConnect),并传递给 Azure AD Graph API [英] How to get the JWT (using OpenIdConnect) from HttpContext, and pass to Azure AD Graph API

查看:17
本文介绍了如何从 HttpContext 获取 JWT(使用 OpenIdConnect),并传递给 Azure AD Graph API的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我们在 2016 年开发了一个使用 WS-Federation 进行身份验证的应用程序,以从本地 AD 中获取声明.IT 战略的方向发生了变化,并且正在转向 Azure AD(当前托管混合环境).

We developed an application in 2016 that authenticated using WS-Federation, to grab claims from the on-premises AD. The direction of the IT strategy has changed, and is moving toward Azure AD (currently hosting a hybrid environment).

我们正在使用 OpenIDConnect 将身份验证从 WS-Fed 迁移到 AAD.让用户登录并使用新方法进行身份验证非常简单 - 正确进行配置,并发出身份验证挑战,罗伯特是你母亲的兄弟.

We're in the process of migrating the authentication from WS-Fed, to AAD, using OpenIDConnect. Getting the user signed in and authenticated with the new method was surprisingly straightforward - do the config properly, and issue the authenticate challenge, and Robert is your mother's brother.

问题

如果我在这里弄错了术语,请纠正我;我们需要从 Active Directory 中获取一些无法通过默认 JWT 访问的属性(据我所知).因此,我们需要通过 HTTP 将 JWT 传递给 Graph API,以从活动目录中获取我们想要的属性.

Please correct me if I'm getting my terminology wrong here; we need to grab some attributes from Active Directory that aren't accessible (as far as I can tell) via the default JWT. So, we need to pass the JWT to the Graph API, via HTTP, to get the attributes we want from active directory.

我知道格式正确且经过身份验证的请求可以提取必要的数据,因为我已经设法使用图形浏览器(AAD 浏览器,而不是 Microsoft Graph 浏览器)查看了它.

I know that a properly formatted and authenticated request can pull the necessary data, because I've managed to see it using the graph explorer (the AAD one, not the Microsoft Graph one).

问题

如果我上面的理解是正确的,我该如何从 ASP.Net 的 HttpContext 中拉取 JWT?如果我正确掌握了所有这些较低级别的 HTTP 内容,我需要在 Graph API 请求的请求标头中包含 JWT,并且我应该得到我需要的 JSON 文档作为响应.

If my understanding above is correct, how do I pull the JWT from the HttpContext in ASP.Net? If I've grasped all this lower level HTTP stuff correctly, I need to include the JWT in the request header for the Graph API request, and I should get the JSON document I need as a response.

(编辑,为了未来读者的利益:您实际上需要为您尝试访问的特定服务获取一个新令牌,在本例中为 Azure AD.您可以使用代表流程执行此操作,或使用作为应用程序流程).

(Edit, for the benefit of future readers: You actually need to acquire a new token for the specific service you're trying to access, in this case Azure AD. You can do this using the on-behalf-of flow, or using the as-an-application flow).

Request.Headers["IdToken"] 返回 null,所以我想知道这里出了什么问题.

Request.Headers["IdToken"] is returning null, so I'm wondering what's going wrong here.

守则这是我们在服务器启动时运行的身份验证配置:

The Code Here's our Authentication config that runs on server startup:

    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
                }

            }
            );
    }

这是我制作 GraphAPI 请求的部分完整代码:

Here's my partially complete code for crafting the GraphAPI request:

        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 的一些指针),但这绝对是可行的,只需对代码稍作修改 这里.前面提到的是微软的 OpenId 指南.

Okay it took me a few days to work out (and some pointers from Juunas), but this is definitely doable with some slight modifications to the code here. The aforementioned being the OpenId guide from Microsoft.

我绝对会推荐阅读您的具体 认证场景,并查看相关示例.

I would definitely recommend reading up on your specific authentication scenario, and having a look at the relevant samples.

以上将带您入门,但是要从 Graph API 获取 JWT(不要与 Microsoft Graph 混淆),您需要在进行身份验证时获取身份验证代码,并将其存储在令牌缓存中.

The above will get you in the door, but to get a JWT from the Graph API, (not to be confused with Microsoft Graph), you need to get an authentication code when you authenticate, and store it in a token cache.

您可以从 this 来自 Microsoft 的示例(MIT 许可证).现在,就个人而言,我发现这些示例被复杂的用例过度混淆了,而实际上它们应该概述基础知识,但这只是我.不过,这些足以让您接近.

You can get a usable token cache out of this sample from Microsoft (MIT License). Now, personally, I find those samples to be overly obfuscated with complicated use-cases, when really they should be outlining the basics, but that's just me. Nevertheless, these are enough to get you close.

现在是一些代码.请允许我提请您注意ResponseType= CodeIdToken".

Now for some code. Allow me to draw your attention to the '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,
                    }

                }
                );
        }

当提供上述参数时,验证时将运行以下代码:

When the above parameter is supplied, the following code will run when you authenticate:

        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");
    }

这将为您的令牌缓存提供可以传递给 Graph API 的代码.从这里,我们可以尝试使用 Graph API 进行身份验证.

This will supply your token cache with a code that you can pass to the Graph API. From here, we can attempt to authenticate with the 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 Request 将其传递给 Graph API(这是最简单的部分).

Once you have the authentication token, you can pass it to the Graph API via Http Request (this is the easy part).

    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();

从这里,您有一个数据流,您可以将其传递给流阅读器,从中取出 JSON,然后做任何您想做的事情.就我而言,我只是在查找目录中的用户数据,但不包含在 Azure AD 身份验证的默认声明中.所以就我而言,我调用的 URL 是

From here, you have a datastream that you can pass into a stream reader, get the JSON out of, and do whatever you want with. In my case, I'm simply looking for user data that's in the directory, but is not contained in the default claims that come out of Azure AD Authentication. So in my case, the URL I'm calling is

"https://graph.windows.net/me?api-version=1.6"

如果您需要更深入地了解您的目录,我建议您使用 Graph Explorer.这将帮助您构建 API 调用.现在,我再次发现 Microsoft 文档有点迟钝(如果您想了解一些漂亮的东西,请查看 Twilio API).不过弄明白了其实也没那么糟糕.

If you need to do a deeper dive on your directory, I'd recommend playing with the Graph Explorer. That will help you structure your API calls. Now again, I find the Microsoft documentation a little obtuse (go look at the Twilio API if you want to see something slick). But it's actually not that bad once you figure it out.

这篇关于如何从 HttpContext 获取 JWT(使用 OpenIdConnect),并传递给 Azure AD Graph API的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆