AppleAuth

Elixir library for Apple Sign in with Apple (SIWA) authentication. Validates Apple identity tokens against Apple's JWKS public keys and exchanges authorization codes for tokens.

Installation

Add apple_auth to your dependencies in mix.exs:

def deps do
  [
    {:apple_auth, "~> 0.1.0"}          # or from Hex once published
  ]
end

Configuration

# config/runtime.exs or config/config.exs
config :apple_auth,
  bundle_id: System.get_env("APPLE_BUNDLE_ID") || "com.example.app",
  team_id: "YOUR_TEAM_ID",
  key_id: "YOUR_KEY_ID",
  client_secret_pem: System.get_env("APPLE_CLIENT_SECRET_PEM")  # ES256 private key
Key Env var Required for
bundle_idAPPLE_BUNDLE_ID Token validation
team_idAPPLE_TEAM_ID Code exchange
key_idAPPLE_KEY_ID Code exchange
client_secret_pemAPPLE_CLIENT_SECRET_PEM Code exchange

Config resolution order: opts keyword > Application env > System env var.

Usage

Validate an identity token

case AppleAuth.validate_id_token(token) do
  {:ok, %AppleAuth.Claims{sub: user_id, email: email}} ->
    # user_id is the stable Apple user identifier
    {:ok, user_id}

  {:error, reason} ->
    {:error, reason}
end

Exchange an authorization code for tokens

case AppleAuth.exchange_authorization_code(authorization_code) do
  {:ok, %{id_token: id_token, access_token: access_token, refresh_token: refresh_token}} ->
    # Validate the id_token next
    AppleAuth.validate_id_token(id_token)

  {:error, reason} ->
    {:error, reason}
end

Phoenix Plug

Protect routes by adding the plug to your pipeline:

# In your router
pipeline :apple_authenticated do
  plug AppleAuth.Plug, on_invalid_token: :unauthorized
end

scope "/api" do
  pipe_through [:api, :apple_authenticated]
  # ...
end

The plug extracts the Bearer token from the Authorization header, validates it, and assigns the result to conn.assigns.apple_auth.

Options for on_invalid_token:

Access claims in your controller:

def index(conn, _params) do
  %AppleAuth.Claims{sub: user_id} = conn.assigns.apple_auth
  # ...
end

License

MIT