【问题标题】:Apple iCloud access to contacts in uwp applicationApple iCloud 访问 uwp 应用程序中的联系人
【发布时间】:2019-01-15 05:25:57
【问题描述】:

我正在尝试在我的 UWP 应用程序中访问我的 iCloud 联系人。 我知道我可以通过Google's People API 访问我的Gmail 联系人,并通过Microsoft 的Graph apiOutook people api 访问我的Outlook 联系人。

Apple 是否提供可用于获取、更新、添加、删除联系人的 API(Rest 或其他)?如果是,是否有教程介绍如何设置访问 iCloud api?

【问题讨论】:

  • 如果对您有帮助,请查看this SO 帖子。
  • @Dishant 是的,我在发布这个问题之前看过它。我仍然继续发布这个问题,因为我想知道是否有官方文档,因为正在讨论的内容 here 看起来像是获取联系人列表的黑客。我仍然会尝试让这个今晚工作肯定......谢谢:)
  • 这是我目前所拥有的:
  • String id = "id"; String pwd = "pwd"; String url = "https://setup.icloud.com/setup/ws/1/accountLogin"; String data = "{\"apple_id\":" + appleId + ", \"password\":" + password + ", \"extended_login\":false}"; HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Add("Origin", "https://www.icloud.com"); Uri requestUri = new Uri(url); HttpStringContent content = new HttpStringContent(data, UnicodeEncoding.Utf8); HttpResponseMessage hrm = await client.PostAsync(requestUri, content); String responseBody = await hrm.Content.ReadAsStringAsync();
  • 我得到了正确的 dsid 响应,但由于两因素身份验证没有令牌。因此,我没有使用苹果帐户密码,而是创建了一个应用程序专用密码来规避该问题。使用相同的逻辑,只是用应用程序特定密码替换苹果 id 密码,但是给我以下错误:{"success":false,"error":1} 如何使应用程序特定密码工作?或者,如何使用两因素身份验证?

标签: c# .net uwp icloud


【解决方案1】:

因此,如果您只想获取联系人(并以 JSON 格式),那么我之前的答案可能是可行的方法。 但是,由于我希望能够对 Apple 帐户执行 CRUD 操作,因此更好的方法是使用 icloud/apple 支持的 CARDDAV 协议。

  1. 通过在https://contacts.icloud.com 使用请求内容触发 PROPFIND 请求,使用基本身份验证获取主要用户(此基本身份验证中使用的密码必须生成为应用密码here):
<propfind
xmlns="DAV:">
<prop>
    <current-user-principal/>
</prop>
</propfind> 
  1. 上一步将让您检索格式为 /1437425399/principal/ 的原则。现在,您可以在链接https://contacts.icloud.com/1437425399/principal 上为该用户触发 PROPFIND 查询,请求内容如下:
<propfind
    xmlns="DAV:"
    xmlns:c="urn:ietf:params:xml:ns:carddav">
    <prop>
        <c:addressbook-home-set/>
    </prop>
</propfind>
  1. 从上一个请求中,您将获得格式为 https://p48-contacts.icloud.com:443/1437425399/carddavhome/ 的 homeset 链接。您可以在 home-set 链接上使用以下 PROPFIND 请求查询用户的电子名片存在于何处:
<propfind
xmlns="DAV:">
<prop>
    <resourcetype/>
</prop>
</propfind>
  1. 您将收到放置所有卡片的地方(例如/1437425399/carddavhome/card/)。使用地址簿查询,现在您可以在上一个链接中收到的端点发起 REPORT 请求:
<c:addressbook-query
    xmlns="DAV:"
    xmlns:c="urn:ietf:params:xml:ns:carddav">
    <prop>
    </prop>
</c:addressbook-query>
  1. 这将为您提供标签中的所有卡片。然后,您可以使用 addressbook-multiget REPORT 请求获取多张卡片:
<c:addressbook-multiget
    xmlns="DAV:"
    xmlns:c="urn:ietf:params:xml:ns:carddav">
    <prop>
        <getetag />
        <c:address-data />
    </prop>
    <href>/1437425399/carddavhome/card/somecard.vcf</href>
    <href>/1437425399/carddavhome/card/anothercard.vcf</href>
</c:addressbook-multiget>

对于更新、创建和删除卡片,您可以在“/1437425399/carddavhome/card/cardtobemanipulated.vcf”端点使用我们之前讨论过的基本身份验证在将 Content-Type 设置为“text/vcard”后触发 PUT 请求并在内容中发送 VCard 以进行更新和创建请求。

【讨论】:

    【解决方案2】:

    在花了很多天试图解决这个问题之后,我想我开始破解这个问题了。我不确定(也许我是)Apple 是否故意让这变得困难,但这比它应该的更困难。 这是我什至获取联系人必须做的事情(完全不知道如何添加、更新、删除它们):

    步骤 1> 使用 Apple id 和密码请求令牌。如果 2FA 被激活,您将不会在响应标头中收到身份验证令牌和身份验证用户 cookie。但这没关系,我们将在下一步中讨论。请注意,现在很可能提取 AUTH-TOKEN 和 AUTH-USER 将失败。此外,我们将在稍后需要的响应中收到地区信息和联系链接。

    public sealed partial class MainPage : Page
    {
        String contactLink = "";
        String authToken = "";
        String authUser = "";
    
        public MainPage()
        {
            this.InitializeComponent();
        }
    
        private async void IcloudBtn_Click(object sender, RoutedEventArgs e)
        {
            HttpResponseHeaderCollection respHeaders = await ContactApple();
            ContactsBlock.Text += "CONTACT LINK: " + contactLink + "\n";
            ContactsBlock.Text += authToken + "\n";
            ContactsBlock.Text += authUser + "\n";
        }
    
        private async void GetToken_Click(object sender, RoutedEventArgs e)
        {
            HttpResponseHeaderCollection respHeaders = await ContactApple(AuthenticationCode.Text);
            ContactsBlock.Text += "CONTACT LINK: " + contactLink + "\n";
            ContactsBlock.Text += authToken + "\n";
            ContactsBlock.Text += authUser + "\n";
        }
    
        private async void GetContacts_Click(object sender, RoutedEventArgs e)
        {
            Uri contactUri = new Uri(contactLink + "/co/startup?locale=en_US&order=last%2Cfirst");
            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Add("Origin", "https://www.icloud.com");
            client.DefaultRequestHeaders.Add("Cookie", authToken + ";" + authUser + ";");
    
            HttpResponseMessage hrm = await client.GetAsync(contactUri);
            JObject contactJson = JObject.Parse(await hrm.Content.ReadAsStringAsync());
            ContactsBlock.Text += contactJson["contacts"];
        }
    
    
        private async Task<HttpResponseHeaderCollection> ContactApple(String authentication = "")
        {
            // The apple id and the password
            String appleId = "AppleID";
            String password = "Password" + authentication;
    
            // https://stackoverflow.com/questions/31457068/get-icloud-contact-list-in-c-sharp?noredirect=1&lq=1
            // Post request will have this in the content
            String data = "{\"apple_id\":" + appleId + ", \"password\":" + password + ", \"extended_login\":true}";
            HttpStringContent content = new HttpStringContent(data, UnicodeEncoding.Utf8);
    
            // The URI to get the tokens from:
            Uri requestUri = new Uri("https://setup.icloud.com/setup/ws/1/accountLogin");
    
            // Create an instance of the HttpClient (Windows.Web.Http)
            HttpClient client = new HttpClient();
    
            // Add Origin = https://www.icloud.com in the header.
            client.DefaultRequestHeaders.Add("Origin", "https://www.icloud.com");
    
            // Post request and read response as JSON object (NewtonSoft)
            HttpResponseMessage hrm = await client.PostAsync(requestUri, content);
            JObject resp = JObject.Parse(await hrm.Content.ReadAsStringAsync());
    
            // Get the URL to the contacts
            contactLink = (String)resp["webservices"]["contacts"]["url"];
    
            // Read the headers for AUTH-TOKEN and AUTH-USER Cookies,
            HttpResponseHeaderCollection headers = hrm.Headers;
            if (headers.ContainsKey("Set-Cookie"))
            {
                String cookie = headers["Set-Cookie"];
                char[] separators = { ';', ',' };
                String[] tokens = cookie.Split(separators);
                foreach (String token in tokens)
                {
                    int length = token.Length;
                    if (token.Contains("X-APPLE-WEBAUTH-TOKEN"))
                    {
                        authToken = token;
                    }
                    if (token.Contains("X-APPLE-WEBAUTH-USER"))
                    {
                        authUser = token;
                    }
                }
            }
            return headers;
        }
    }
    

    步骤 2> 如果 2FA 被激活,您将在您的 Apple 设备上收到授权登录并使用提供的代码登录的请求。获得代码后,重复与上述相同的步骤,只需附加通过在授权代码文本字段中提供您的密码。

    请注意,您需要输入授权码。

    步骤 3> 希望您现在已经收到获取联系人所需的令牌。现在,您可以在获取令牌并附加“/co/startup?locale=XXXXX&order=last%2Cfirst”时使用响应正文中获取的联系人链接来获取联系人

    请注意,您必须使用适合您需要的语言环境。我需要使用“en_US”,因为这是我在响应中的语言环境中的内容。您需要使用请求中返回的语言环境。

    XAML:

    <Page
        x:Class="AddressBook.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:AddressBook"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    
        <StackPanel x:Name="MainStack">
            <Button x:Name="IcloudBtn" Content="Request Code" Click="IcloudBtn_Click"/>
            <TextBox x:Name="AuthenticationCode" Text="Authentication Code here." TextWrapping="Wrap"/>
            <Button x:Name="GetToken" Content="Get Token" Click="GetToken_Click"/>
            <Button x:Name="GetContacts" Content="Get Contacts" Click="GetContacts_Click"/>
            <TextBox x:Name="ContactsBlock" TextWrapping="Wrap"/>
        </StackPanel>
    </Page>
    

    但是,我仍然不知道如何实际操作联系人列表。我想如果我发现了,我会在这里更新。

    【讨论】:

      猜你喜欢
      • 2018-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多