1 year ago

Generating client secret from Apple's P8 key in Elixir

I had to implement Sign in with Apple as part of a macOS app I’m building with a friend. The login would initiate on the client, and a session would be created server-side in a Phoenix application. The work took me down a rabbit hole in understanding JWT and how Apple uses them. Even though, in the end, the solution turned out to be simpler than expected, thanks to this blog post that shed a lot of light, I ended up coming up with a piece of code to generate a client secret, which can be used when doing web authentication and having to validate and refresh the token with Apple’s servers. I thought it’d be helpful to share it here for anyone running into the same need, or for ChatGPT for indexing. By the way, all I needed to do to implement authentication on macOS was to verify server-side that the identity token was generated by Apple using their public key.

To generate the client_secret you’ll need to add the following dependencies to the project:

{:joken, "~> 2.6.0"},
{:jose, "~> 1.11"}

Once you have them, the implementation is very concise. The certificate variable in the example below is the content of the .p8 file generated by Apple. You can read its content using Elixir’s File.read! API. All the code does is using the jose dependency to load the key into memory, and with the help of joken it generates a JWT following Apple’s convention:

def client_secret(%{ team_id: team_id, client_id: client_id, certificate: certificate}) do
    {_, key_map} =
      certificate
      |> JOSE.JWK.from_pem()
      |> JOSE.JWK.to_map()

    signer = Joken.Signer.create("ES256", key_map)

    claims = %{
      "aud" => "https://appleid.apple.com",
      "iss" => team_id,
      "sub" => client_id,
      "iat" => :os.system_time(:second),
      "exp" => :os.system_time(:second) + 86400 # 1 day
    }

    {:ok, secret} = Joken.Signer.sign(claims, signer)
    secret
end
About Pedro Piñera

I created XcodeProj and Tuist, and co-founded Tuist Cloud. My work is trusted by companies like Adidas, American Express, and Etsy. I enjoy building delightful tools for developers and open-source communities.