Getting started with Apple’s MapKit JS

Getting started with Apple’s MapKit JS
This article mainly explains how to easily get started with MapKit JS. If you already have it set up I’d advise you to check out Apple’s excellent documentation on MapKitJS.

Since I first spotted Apple Maps on the web, I have loved it. You could – with a lot of hassle – run it on your own site (it involved proxying Apple endpoints 🧐).

That’s why I was pretty happy when Apple announced their official MapKit JS library in beta last week!

The new MapKit JS library will make adding Apple Maps to your site very easy – so I thought. In their product demo and on the Developer site, Apple shows the following way to add an authentication token (JWT-token) to your embed code.

mapkit.init({
    authorizationCallback: function(done) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "/services/jwt");
        xhr.addEventListener("load", function() {
            done(this.responseText);
        });
        xhr.send();
    }
});

Just to be clear, you’ll need to host this /services/jwt/ endpoint yourself. The endpoint should return a signed JWT token which can then be used to authenticate against Apple endpoints.

Using this configuration means that every single Map instantiation would cost one call to your own servers before you can initialize the map. Apart from the extra load, you’ll need to create an (API-)endpoint, configure JWT libraries, etc. Primarily for static websites, this is quite a problem.

Just looking for a simple way to embed Apple Maps? Scroll to the bottom of the page for a nifty tool.

What’s the alternative?

Luckily, Apple probably realized this wouldn’t work in every situation. So, hidden in the documentation, they say:

[..]. It can also be run on a development machine to generate a long-lived token to be checked directly into source-code. […] Apple Developer documentation

This JWT token can be committed into source control. No endpoints for JWT signing are required; hurray! Of course, these tokens have an expiry date; however, you are free to set this expiry date to 10 years in the future.

This is precisely what we (I, at least) want. Embedding a token in the code without extra calls or hassle.

Also, committing the token to the source code doesn’t mean you’ve lost all control. Using the Apple Developer portal, you can revoke the private key you used to generate the JWT.

We’ve just gone from the cumbersome authentication flow above to the following:

mapkit.init({
    authorizationCallback: function(done) {
        done('Insert JWT-token here');
    }
});

But first, we’ll need to generate a JWT token.

If you’re not familiar with JWT’s you might want to read this excellent article by Auth0.

Generating the JWT

Before you can get started, we’ll need to gather some requirements.

  • You’ll need to register a Maps ID. This is a unique identifier for your Map instance. Apple specifies that you’ll want to use one Maps ID per environment (for example, development and production).
  • To sign the JWT, you’ll also need a private key with MapKit JS services enabled.

After creating a new private key, you’re provided with a PKCS #8 private key. The private key does not require a password.

Do make sure you save the key somewhere safe, Apple only provides the download once. 🔑

Now that we have all the requirements, we will need to generate a JWT token according to the specifications on Apple’s website.

Apple uses the ES256 algorithm using Ecliptic Curve Cryptography, so you’ll want to have the JWT algorithm header set to ES256.

You’ll also need to add your Key ID to the JWT header using the kid parameter. You’ll find this ID in the Developer portal, where you also created the key.

And, as JWT specifies, you’ll need to add the JWT type to the header.

Putting it all together, your JWT header now looks as follows:

{ "alg": "ES256", "typ": "JWT", "kid": "Your Key ID" }

Next up; is the JWT payload. Nothing fancy here.

Apple specifies that the iss (issuer) parameter should be set to the Team ID with which you have created the Maps ID & key.

Apple recommends – but doesn’t require – using the origin parameter as well. You can specify the origin that is allowed to use the MapKit JS code. This can prevent unauthorized use of your token, which would count towards your daily limit.

Also, following JWT’s specification, you’ll need to set the exp (expiry) and iat(issued at) parameters. These values are specified in seconds since Epoch.

I’d advise you to use a long expiry time; once the token expires, your map won’t load anymore. You can use a value that best suits your needs.

Our payload is now complete and looks like this:

{
    "iss":"Your Team ID",
    "iat":1528473755323,
    "exp":1528476433723,
    "origin":"Your origin header"// Recommended, but not required.
}

Optionally, you can provide a origin parameter to restrict which domains the code can be used. This should match the Origin header passed by your browser.

The final step is signing the first two parts of the JWT token. As I’m not a cryptography expert, I won’t go into detail on this. There are loads of libraries that can do the signing for you.

The signature is made up of the following parts:

ECDSASHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), privateKey )

When we put our JWT together, we’ll get something along the lines of the following:

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IllvdXIgS2V5IElEIn0.eyJpc3MiOiJZb3VyIFRlYW0gSUQiLCJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTUyODQ3NjQzMzcyMywiV2hlbiB5b3UiOiJyZWFkIHRoaXMsIiwieW91IHJlYWxseSB0aG9yb3VnaGx5IjoicmVhZCB5b3VyIGFydGljbGVzLiJ9.yGNubQUo6tzYsArulZgeNZxv7-6anvCC57tfTV0zqK2HN5HbmwLGesIA2nfXktD1UcN3mkGhTaUqxkEnIA5xuQ

You can now use this token to authenticate the Apple endpoints.

An easier way

This article might have shocked you if you were just looking for a simple Map embed with an API token. Not to worry, there’s an easier way.

I created a tool that generates the embed code (and JWT token) for you. You’ll still need the requirements listed above (and on the tool), but the rest is easy as pie.

You just enter your details, and the embed code comes out, easy! Also, your details never leave your browser, so there’s no compromise on security. 🔐

Check it out at mapkitjs.rubeng.nl

The easiest way