Skip to content
Nov Matake edited this page Jul 12, 2022 · 10 revisions

AppleID::Client

Use this class to obtain Access Token & ID Token.

Registration

You need to register OAuth Client at Apple Developer Site.

Basically, you need

  • Register an App ID with "Sign in with Apple" capability
  • Register a Service ID with Primary App ID set as the above App ID
  • Verify your REDIRECT_URI domain by uploading /.well-known/apple-developer-domain-association.txt to your server
  • Register a Key for the Primary App ID

During registration, you can get

  • CLIENT_ID a.k.a. "Service ID"
  • KEY_ID which is displayed at the key details page

For more details, read this official document.

Initialize Client Instance

private_key_pem = <<-PEM
-----BEGIN PRIVATE KEY-----
  :
-----END PRIVATE KEY-----
PEM

client = AppleID::Client.new(
  identifier:   CLIENT_ID,
  team_id:      TEAM_ID,
  key_id:       KEY_ID,
  redirect_uri: REDIRECT_URI,
  private_key:  OpenSSL::PKey::EC.new(private_key_pem)
)

Send Authorization Request

session[:state] = SecureRandom.hex(16)
session[:nonce] = SecureRandom.hex(16)
authorization_uri = client.authorization_uri(
  state:         session[:state],
  nonce:         session[:nonce],
  scope:         [:email, :name],
  response_mode: :form_post
)
redirect_to authorization_uri

Receive Authorization Response

CSRF Protection

Confirm params[:state] is the same value with session[:state] which you generated at sending Authorization Request. If those are different, it's CSRF attack.

unless params[:state] == session[:state]
  raise 'CSRF Attack Detected'
end

Exchange Authorization Code with Access Token & ID Token

Once you get params[:code], you can exchange it with OAuth Access Token & ID Token.

if params[:code].present?
  client.authorization_code = params[:code]
  token_response = client.access_token!
  token_response # => AppleID::AccessToken
  token_response.access_token # => String
  token_response.refresh_token # => String
  token_response.id_token # => AppleID::IdToken
end

See AppleID::AccessToken and AppleID::IdToken for details.

OAuth Error Handling

User rejected to sign-in, or some other reason.

if params[:error]
  raise "OAuth Error: #{params[:error]} #{params[:error_description]}"
end

Obtain new Access Token using existing Refresh Token

client.refresh_token = "<existing-refresh-token>"
token_response = client.access_token!
token_response # => AppleID::AccessToken
token_response.access_token # => String

# NOTE: Apple issues no refresh_token nor id_token when refreshing access token
token_response.refresh_token # => nil
token_response.id_token # => nil

Revoke Tokens

Revoke Access Token

client.revoke!(access_token: '<existing-access-token>')

Revoke Refresh Token

client.revoke!(refresh_token: '<existing-refresh-token>')

or

client.refresh_token = '<existing-refresh-token>'
client.revoke!