Node API for Apple's iCloud services
This is an unofficial iCloud API that provides useful methods to interact with some services of iCloud.
Please make sure that this API is my private work and therefore it will never be supported by Apple. iCloud's endpoints and functionality can be changed every time by Apple. Using this project is on your risk and it's possible that apple bans your account. They can do whatever they want and because of the fact that it doesn't fits in to Apples agreements, I can not ensure using this API without risks.
The API's I've implemented are more less the same as Apple provides. Sometimes, the objects are simplified or a little bit restructured because the requirements of such a Javascript API are different from the original API Apple provides. But all in all it's Apple's data and I'm not the author of the idea how an API's result works in detail.
I can not be sure that Apple does not provide any kind of changes in their API. That means, it is possible that something does not work as expected although you did it as it is described in the Documentation here.
npm install apple-icloud…Basically, your instance always tries to use an existing session. You can pass a session by an object or a file path that points to a JSON file. If the passed session is invalid because of any reason, your instance will use your given credentials to re-login. If you did not gave any credentials to the constructor, your instance will try to get the credentials from the given (and obviously broken) session that are normally stored within.
// This creates your iCloud instance
var myCloud = new iCloud("path/to/my/session.json", username, password);This way is more practicable because it does a lot of stuff automatically. E.g. if your session is too old or invalid, this method just creates a new one. If you use this way of logging in, you will be save that everything works fine. Therefore that is the recommended way.
Now you have a working session and you should continue at API
I already explained that the way I use, is the recommended one but theoretically you can do everything manually.
The arguments I use here are all optional but if you don't use them you have to initialize your instance manually. That is possible but not easy as the way above with only three arguments.
As I already explained, you can do everything manually.
// This creates your iCloud instance without any arguments
var myCloud = new iCloud();
// This creates a new session with your credentials. This is very slow if you do it every time and also not recommended because apple bans your account if you login too often in a small time area
myCloud.login(username, password, function(err) {
if (err) {
// An error occured
}
// You logged in successfully!
});This is maybe interesting if you have an existing session but you want to re-login your account because of some reason I don't know. But please keep in mind that you really should use existing sessions much as possible because you save a lot of time and avoid being banned by Apple.
If you have an existing session, you can export it just calling exportSession() in iCloud instance. This returns a session object that can be loaded the next time you create an iCloud instance new iCloud(session, username, password).
var newSession = myCloud.exportSession();Your iCloud instance (myCloud) also contains a method called saveSession that saves the session directly to a file path.
myCloud.saveSession("/path/to/my/session.json");If you don't put a file path as argument, the method will use the path you used with new iCloud(path, username, password). If you did not used such a file path there, an error occurs. In practise, that means you can do the following successfully:
var myCloud = new iCloud("path/to/my/session.json", username, password);
/*
Do your cool stuff.
*/
// Saves session to 'path/to/my/session.json' because your instance knows this path already and you used no custom one as argument
myCloud.saveSession();To save the session automatically when something changes, just listen to the sessionUpdate event and apply saveSession().
myCloud.on("sessionUpdate", function() {
myCloud.saveSession();
});Please. Save your sessions and use them often as possible. This avoids problems with the API. If you do not use session saving, your client has to do the complete login process every time you start your script. This takes a lot of time and destroys functionality on Apples's servers because they may stop sending important cookies. Therefore: Just do it.
If the two-factor-authentication is required, your login works basically as expected. You get the ready event on your instance and so on.
But you cannot use the most API's such as Contacts, Mail, Notes and so on unless you type in the security code. The only API's that are available are the ones that are also available on iCloud.com without typing in a security code, such as FindMe.
To check wether the two-factor-authentication is required, you can do 2 things.
err event and check the error with the error code 15 and the message Two-factor-authentication is required..twoFactorAuthenticationIsRequired of your iCloud instance.// ... Logged in successfully
const needsSecurityCode = myCloud.twoFactorAuthenticationIsRequired;
if (needsSecurityCode) {
console.error("Two Factor Authentication is required. Type in your security code!");
}
else {
console.log("Everything okay. Go on!");
}To enable the other API's, you need to type in a security code that will be displayed on your devices.
// Sample code '123456'
myCloud.securityCode = "123456";
// When finished, a `ready` event will be triggered.
myCloud.on("ready", function() {
// Ready event
});Of course, the ready event now will fire the second time because it already fired after first login (This returned the requirement of two-factor-authentication). To check wether the code is typed in now, look up the twoFactorAuthenticationIsRequired property that returns wether you need the security code (Should be false after you passed in a security code, otherwise the code seems to be invalid).
Everything sounds complicated?
No problem, just look at this short example for a login that supports two-factor-authentication. (Please keep in mind, that I am using prompt here, to get user's input. Of course, you can get your security code as you want).
…When you are trying to login but two-factor-authentication is required, a security code will normally be sent to your devices automatically.
But sometimes, you need a new one, an SMS or a voice call. To request this programmatically, the API provides the method sendSecurityCode(mode) that sends a security code to your devices directly, via SMS or via Voice (phone call).
There exist 3 mode's. If you don't set a mode, the default one is message.
message -> Sends a security code directly to your devicessms -> Sends an SMS containing a security code to your phonevoice -> Calls your phone and speaks out a security codeOf course, the SMS will be sent to the phone number connected with your AppleID...
// Request SMS security code
myCloud.sendSecurityCode("sms");
// Request voice security code
myCloud.sendSecurityCode("voice");Sometimes you run your script & give your credentials but no security code comes in? This mostly happens because you set up a session that is partial completed but not fully complete. Try to delete your existing session file and try it again.
Also try to not wonder why you do not have to type in a security code a second time after using test.js or any other test environment. They save your session at /tmp/icloud-session.json. If you use it a second time, it just uses the old session.
This is the basic iCloud API that you can use to interact with iCloud services.
This method lists your account contacts data like groups and persons.
myCloud.Contacts.list(function(err, data) {
if (err) return console.error(err);
// Successfully your contacts :)
console.log(data);
});Demo: demo/contacts/listContacts.js
To use change(), new() & delete() you firstly have to list() the contacts one time to become a valid syncToken!
To change a contact's properties, just change the object's properties and call change(myContact).
myContact.lastName = "NewLastName";
const changeset = await myCloud.Contacts.change(myContact);Demo: demo/contacts/changeContact.js
Deleting a contact is also easy like changing. Just use a contact's object as argument when calling delete()
const delChangeset = await myCloud.Contacts.delete(myContactIDontLike);Demo: demo/contacts/deleteContact.js
Creating a new contact is also really simple. You put a self-made contact-object as argument to new(). What a kind of properties a contact can have, is Apples work and this API just use apples JSON structures. If you need to know it, just loadan existing contact and have a look at it's object structure.
const createChangeset = await myCloud.Contacts.new({
firstName: 'New',
lastName: 'Guy',
emailAddresses: [
{
label: "Privat",
field: "[email protected]"
}
],
isCompany: false
});Demo: demo/contacts/newContact.js
const locations = await myCloud.Friends.getLocations();Please also remember that the fist requests maybe doesn't returns a position for the devices (Same thing when using FindMe) because the position is still in progress. Sometimes you have to wait a few seconds.
Demo: demo/friends/getLocations.js
The drive services are not completed yet.
This API is a little bit more complex because it implements a really useful implementation of unix paths in icloud. Please read the following paragraph to the end before using the API.
iCloud Drive's endpoints doesn't work with UNIX paths like /path/to/my/file. But iCloud Drive use something called drivewsid. That is an id that points on your items in iCloud Drive.
If you list a folder, it returns the drivewsid's of it's sub items. You can request them than.
The drivewsid of the root folder of iCloud Drive is FOLDER::com.apple.CloudDocs::root.
All methods of this API accept as first argument such a drivewsid.
const itemInfo = await myCloud.Drive.getItem("FOLDER::com.apple.CloudDocs::root");Demo: demo/drive/getItem.js
May you see the error "No PCS cookies". This is because you logged in too often and icloud servers does not send them to you anymore or your actual session just does not have any PCS cookies. In any case, wait a few seconds up to minutes and reset your session. And please store your sessions! This will avoid such problems!
If you do his, you will see that you become a list with root's sub items and the related drivewsid
No open issues yet, or sync has not completed.