【问题标题】:Office 365 API authentication form REST APIOffice 365 API 身份验证表单 REST API
【发布时间】:2017-12-15 00:00:13
【问题描述】:

我正在尝试从 Office 365 获取日历以在 REST API (WEB API 2) 中使用它们。 我已经尝试了很多方法来生成 JWT,但每次尝试都会出现另一个错误。

我的应用程序已在 Azure AAD 中正确注册,公钥已上传。

我尝试的最后一件事来自这篇文章:https://blogs.msdn.microsoft.com/exchangedev/2015/01/21/building-daemon-or-service-apps-with-office-365-mail-calendar-and-contacts-apis-oauth2-client-credential-flow/

在他的示例中,我可以通过两种不同的方式生成 JWT,但我得到了错误:x-ms-diagnostics: 2000003;reason="The audience claim value is invalid 'https://outlook.office365.com'.";error_category= “无效资源”

这是我的代码:

`

string tenantId = ConfigurationManager.AppSettings.Get("ida:TenantId");
            /**
             * use the tenant specific endpoint for requesting the app-only access token
             */
            string tokenIssueEndpoint = "https://login.windows.net/" + tenantId + "/oauth2/authorize";
            string clientId = ConfigurationManager.AppSettings.Get("ida:ClientId");




            /**
             * sign the assertion with the private key
             */
            String certPath = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/cert.pfx");
            X509Certificate2 cert = new X509Certificate2(
                certPath,
                "lol",
                X509KeyStorageFlags.MachineKeySet);

            /**
             * Example building assertion using Json Tokenhandler. 
             * Sort of cheating, but just if someone wonders ... there are always more ways to do something :-)
             */
            Dictionary<string, string> claims = new Dictionary<string, string>()
            {
                { "sub", clientId },
                { "jti", Guid.NewGuid().ToString() },
            };

            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
            X509SigningCredentials signingCredentials = new X509SigningCredentials(cert, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest);

            JwtSecurityToken selfSignedToken = new JwtSecurityToken(
                clientId,
                tokenIssueEndpoint,
                claims.Select(c => new Claim(c.Key, c.Value)),
                DateTime.UtcNow,
                DateTime.UtcNow.Add(TimeSpan.FromMinutes(15)),
                signingCredentials);

            string signedAssertion = tokenHandler.WriteToken(selfSignedToken);

            //---- End example with Json Tokenhandler... now to the fun part doing it all ourselves ...

            /**
              * Example building assertion from scratch with Crypto APIs
            */
            JObject clientAssertion = new JObject();
            clientAssertion.Add("aud", "https://outlook.office365.com");
            clientAssertion.Add("iss", clientId);
            clientAssertion.Add("sub", clientId);
            clientAssertion.Add("jti", Guid.NewGuid().ToString());
            clientAssertion.Add("scp", "Calendars.Read");
            clientAssertion.Add("nbf", WebConvert.EpocTime(DateTime.UtcNow + TimeSpan.FromMinutes(-5)));
            clientAssertion.Add("exp", WebConvert.EpocTime(DateTime.UtcNow + TimeSpan.FromMinutes(15)));

            string assertionPayload = clientAssertion.ToString(Newtonsoft.Json.Formatting.None);

            X509AsymmetricSecurityKey x509Key = new X509AsymmetricSecurityKey(cert);
            RSACryptoServiceProvider rsa = x509Key.GetAsymmetricAlgorithm(SecurityAlgorithms.RsaSha256Signature, true) as RSACryptoServiceProvider;
            RSACryptoServiceProvider newRsa = GetCryptoProviderForSha256(rsa);
            SHA256Cng sha = new SHA256Cng();

            JObject header = new JObject(new JProperty("alg", "RS256"));
            string thumbprint = WebConvert.Base64UrlEncoded(WebConvert.HexStringToBytes(cert.Thumbprint));
            header.Add(new JProperty("x5t", thumbprint));

            string encodedHeader = WebConvert.Base64UrlEncoded(header.ToString());
            string encodedPayload = WebConvert.Base64UrlEncoded(assertionPayload);

            string signingInput = String.Concat(encodedHeader, ".", encodedPayload);

            byte[] signature = newRsa.SignData(Encoding.UTF8.GetBytes(signingInput), sha);

            signedAssertion = string.Format("{0}.{1}.{2}",
                encodedHeader,
                encodedPayload,
                WebConvert.Base64UrlEncoded(signature));

`

我的 JWT 看起来像这样:

`

{
 alg: "RS256",
 x5t: "8WkmVEiCU9mHkshRp65lyowGOAk"
}.
{
 aud: "https://outlook.office365.com",
 iss: "clientId",
 sub: "clientId",
 jti: "38a34d8a-0764-434f-8e1d-c5774cf37007",
 scp: "Calendars.Read",
 nbf: 1512977093,
 exp: 1512978293
}

`

我将此令牌放在“Bearer”字符串之后的 Authorization 标头中。

有解决此类问题的想法吗?我想我需要一个外部的观点:)

谢谢

【问题讨论】:

    标签: c# rest api azure outlook


    【解决方案1】:

    您不生成 JWT,Azure AD 会这样做。

    您将使用您的证书来获取访问令牌。从您链接的文章中借用的示例:

    string authority = appConfig.AuthorizationUri.Replace("common", tenantId);
    AuthenticationContext authenticationContext = new AuthenticationContext(
                   authority,
                   false);
    
    string certfile = Server.MapPath(appConfig.ClientCertificatePfx);
    
    X509Certificate2 cert = new X509Certificate2(
        certfile,
        appConfig.ClientCertificatePfxPassword, // password for the cert file containing private key
        X509KeyStorageFlags.MachineKeySet);
    
    ClientAssertionCertificate cac = new ClientAssertionCertificate(
        appConfig.ClientId, cert);
    
    var authenticationResult = await authenticationContext.AcquireTokenAsync(
         resource,   // always https://outlook.office365.com for Mail, Calendar, Contacts API
         cac);
    return authenticationResult.AccessToken;
    

    然后可以将生成的访问令牌附加到对 API 的请求。

    它不起作用的原因是 Outlook API 不认为您是有效的令牌颁发者。它只接受使用 Azure AD 的私钥签名的令牌。你显然没有。

    您生成的密钥对中的私钥只能用于向 Azure AD 验证您的应用。

    【讨论】:

    • 感谢您的回复。我也尝试过这种方式,但是当我调用 Outlook API 时,我得到了完全相同的错误。
    • Hmm.. 您应该检查访问令牌,例如jwt.ms。检查 aud 声明是否正确。
    【解决方案2】:

    谢谢你!

    这是工作代码:

    var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.microsoftonline.com/tenantId");
    
                string tenantId = ConfigurationManager.AppSettings.Get("ida:TenantId");
                string clientId = ConfigurationManager.AppSettings.Get("ida:ClientId");
    
                String certPath = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/cert.pfx");
                X509Certificate2 cert = new X509Certificate2(
                    certPath,
                    "keyPwd",
                    X509KeyStorageFlags.MachineKeySet);
    
                ClientAssertionCertificate cac = new ClientAssertionCertificate(clientId, cert);
    
                var result = (AuthenticationResult)authContext
                    .AcquireTokenAsync("https://outlook.office.com", cac)
                    .Result;
                var token = result.AccessToken;
    
                return token;
    

    App-only 令牌的其他必需步骤,您必须使用 AAD 应用程序设置中的授予权限按钮。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-05
      • 2017-07-24
      • 2016-03-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多