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 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 quite 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 initialise the map. Apart from the extra load you'll need to create an (API-)endpoint, configure JWT libraries, etc. Especially for static websites this is quite a problem.

What's the alternative?

Luckily, Apple probably realised 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 comitted into source control. No endpoints for JWT-signing required, hurray! Of course these tokens do have an expiry date, you are however free to set this expiry date to 10 years in the future.

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

Also, comitting the token into 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).
  • In order to sign the JWT's 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're going to 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; 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 unauthorised 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 an origin parameter to restrict on 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 export 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:

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IllvdXIgS2V5IElEIn0.eyJpc3MiOiJZb3VyIFRlYW0gSUQiLCJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTUyODQ3NjQzMzcyMywiV2hlbiB5b3UiOiJyZWFkIHRoaXMsIiwieW91IHJlYWxseSB0aG9yb3VnaGx5IjoicmVhZCB5b3VyIGFydGljbGVzLiJ9.yGNubQUo6tzYsArulZgeNZxv7-6anvCC57tfTV0zqK2HN5HbmwLGesIA2nfXktD1UcN3mkGhTaUqxkEnIA5xuQ  

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

An easier way

If you were just looking for a simple Map embed with an API-token this article might have shocked you a bit. Not to worry, there's an easier way.

I created a tool which generates the embed code (and JWT token) for you. You'll still need the requirements as 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


Issues, compliments, comments? Let me know at @rubengmrs or ruben@rubeng.nl.

Apple is a registered trademark of Apple, Inc. This website is not owned by, or affiliated with, Apple Inc.

Ruben Gommers

Read more posts by this author.

Doetinchem, Gelderland https://rubeng.nl

Subscribe to ruben gommers blog

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!