如何从HttpContext获取JWT(使用OpenIdConnect),并传递给Azure AD Graph API

问题描述 投票:5回答:1

背景

我们在2016年开发了一个使用WS-Federation进行身份验证的应用程序,用于从内部部署AD获取声明。 IT战略的方向已经发生变化,并且正朝着Azure AD(目前托管混合环境)发展。

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

问题

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

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

问题

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

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

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
                }

            }
            );
    }

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

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

        }
asp.net-mvc jwt azure-active-directory openid-connect azure-ad-graph-api
1个回答
5
投票

好吧,我花了几天的时间来研究(以及Juunas的一些指示),但这对于代码here的一些细微修改肯定是可行的。前面提到的是Microsoft的OpenId指南。

我肯定会建议您阅读特定的authentication scenario,并查看相关样本。

以上内容将为您提供帮助,但要从Graph API获取JWT(不要与Microsoft Graph混淆),您需要在进行身份验证时获取身份验证代码,并将其存储在令牌缓存中。

您可以从Microsoft(MIT许可证)的this示例中获取可用的令牌缓存。现在,就我个人而言,我发现这些样本过于复杂,使用复杂的用例,实际上他们应该概述基础知识,但那只是我。不过,这些足以让你走近。

现在为一些代码。请允许我提请您注意'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");
    }

这将为您的令牌缓存提供可以传递给Graph 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 Request将其传递给Graph 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();

从这里开始,您有一个数据流,您可以将其传递到流读取器,从中获取JSON,并根据需要执行任何操作。就我而言,我只是在查找目录中的用户数据,但不包含在Azure AD身份验证中的默认声明中。所以在我的情况下,我正在调用的URL是

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

如果您需要深入了解目录,我建议您使用Graph Explorer。这将有助于您构建API调用。现在再一次,我发现微软文档有点迟钝(如果你想看到一些光滑的东西,请查看Twilio API)。但是一旦你弄明白,它实际上并没有那么糟糕。

© www.soinside.com 2019 - 2024. All rights reserved.