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

查看:501
本文介绍了如何从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的一些建议),但是通过对代码此处.前述内容是Microsoft的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.

您可以从来自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身份验证发出的默认声明中.因此,就我而言,我要呼叫的网址是

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"

如果您需要更深入地研究目录,建议您使用图形浏览器.这将帮助您构建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天全站免登陆