Office 365 API身份验证表单REST API

时间:2017-12-11 07:56:43

标签: c# rest api azure outlook

我正在尝试从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 =“受众声明值无效'https://outlook.office365.com'。”; error_category = “invalid_resource”

这是我的代码:

`

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
}

`

我将此标记放在“承载”字符串后的Authorization标头中。

有什么想法可以解决这类问题吗?我想我需要外部观点:)

由于

2 个答案:

答案 0 :(得分: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验证您的应用。

答案 1 :(得分:0)

谢谢juunas!

这是工作代码:

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;

仅限应用令牌的其他必需步骤,您必须在AAD应用设置中使用授予权限按钮。