We're hiring! Check out our currently open positions »

tech.CurrencyFair

Introduction

Nearing the end of 2019 Apple updated their App Store guidelines with following information regarding social login services:

Apps that use a third-party or social login service (such as Facebook Login, Google Sign-In, Sign in with Twitter, Sign In with LinkedIn, Login with Amazon, or WeChat Login) to set up or authenticate the user’s primary account with the app must also offer Sign in with Apple as an equivalent option.

This left CurrencyFair with the decision to either implement Apple Sign-In or remove other social login providers. As we have many active users availing of social login options we decided to go with the former.

While gathering requirements we discovered there weren’t many PHP libraries which could handle every aspect of implementing Apple Sign-In. Specifically we required the following features:

  • Generating Apple authorisation links to use with their Sign-In button
  • Verifying and decoding Apple JWTs
  • Verifying Apple Authorisation Codes and exchanging them with Apple’s API for access/refresh tokens
  • Automatic fetching of Apple’s public keys and generating of client secrets

Rather than use multiple libraries to achieve the above, we opted to create our own Apple Sign-In client with a focus on simplicity and ease of use.

Example Usage

Below you can see a simple end-to-end example of creating a Sign-In button and verifying the response from Apple.

For detailed information on installation, configuration and usage you can refer to the GitHub page.

your-sign-in-page.php

<?php

include './vendor/autoload.php';

use CurrencyFair\AppleId\ClientFactory;
use CurrencyFair\AppleId\Config;

$config = new Config(
    [
        Config::REDIRECT_URI => 'https://example.com/your-return-page.php',
        Config::CLIENT_ID => 'XXX',
    ]
);

// We will use this to verify the request came from Apple on
// the return page
$_SESSION['state'] = 'Something Random';
 
$client = ClientFactory::create($config);
$authorisationUrl = $client->getAuthoriseUrl($_SESSION['state']);

echo "<a href='{$authorisationUrl}'> Sign-In With Apple</a>";

your-return-page.php

<?php

include './vendor/autoload.php';

use CurrencyFair\AppleId\ClientFactory;
use CurrencyFair\AppleId\Config;

$state    = $_POST['state'];
$code     = $_POST['code'];
$idToken  = $_POST['id_token'];
$user     = isset($_POST['user']) ? json_decode($_POST['user'], true) : null;

// Check if the state returned from Apple matches the state we passed to 
// Apple from the Sign-In button
if ($_SESSION['state'] !== $state) {
    throw new Exception('State is invalid');
}

// The user will be available the first time a user registers and *not* passed
// on subsequent Sign-Ins 
if ($user) {
    $fullName = $user['name']['firstName'] . ' ' . $user['name']['lastName'];
    $email = $user['email'];
}

$config = new Config(
    [
        Config::REDIRECT_URI => 'https://example.com/your-return-page.php',
        Config::CLIENT_ID => 'XXX',
        Config::TEAM_ID => 'XXX',
        Config::KEY_ID => 'XXX',
        Config::PRIVATE_KEY => '/full/path/to/key'
    ]
);

$client = ClientFactory::create($config);

$authCodeResponse = $client->verifyAuthCode($code);

echo $authCodeResponse->getAccessToken() . PHP_EOL;
echo $authCodeResponse->getExpiresIn() . PHP_EOL;
echo $authCodeResponse->getRefreshToken() . PHP_EOL;

$jwtResponse = $client->verifyAndDecodeJwt($idToken);

echo $jwtResponse->getEmail() . PHP_EOL;
echo $jwtResponse->getSubject() . PHP_EOL; // Unique user ID provided by Apple
echo $jwtResponse->getIsPrivateEmail() . PHP_EOL;

  • AppleID
  • Apple Sign-In
  • Open-Source

blog comments powered by Disqus