DEV Community

Michal Rogowski
Michal Rogowski

Posted on • Updated on • Originally published at rogowski.page

Why sign-in with apple may take you more than 5 minutes and how it works?

I saw a lot of articles why Sign In With Apple (SIWA) is - great, easy etc. Only few of them were showing how difficult it may be and this is one of those. I will list all articles that I found about other problems at the end of the article. I described it from iOS perspective.

BTW! If you like my articles please follow me on Twitter Thanks!

Sign In With Apple 🍏 (SIWA)

UML diagram below shows register / login flow
SIWA-UML

  1. SIWA button register / login - Most important part of request is scope. For test purposes I’ve added email and full name to scope. When pressing button for the first time user is able to decide, does he want to use real email, or “fake” which will forward all messages to and from user.
  2. ASAuthorizationAppleIDCredential - Apple Documentation describes it as A credential that results from a successful Apple ID authentication. You can also check all fields that are returned. Most important for us are:
    • var authorizationCode: Data? - A short-lived token used by your app for proof of authorization when interacting with the app’s server counterpart.
    • var identityToken: Data? - A JSON Web Token (JWT) that securely communicates information about the user to your app.
    • var user: String - An identifier associated with the authenticated user.
    • var fullName: PersonNameComponents? - The user’s name.
    • var email: String?- The user’s email address.

Token authorisation code on the diagram above is authorizationCode from this response - we have to send it to our API Server. Now our server will be able to use it to get JWT as response from Apple /token servers.

To decode JWT (identityToken) use jwt.io - all validation and debugging is done on the client sideđź‘Ś. My register JWT looks like

HEADER

{
  "kid": "AIDOPK1",
  "alg": "RS256"
}
Enter fullscreen mode Exit fullscreen mode

PAYLOAD

{
  "iss": "https://appleid.apple.com",
  "aud": "com.myapp.app",
  "exp": 1571516448,
  "iat": 1571515848,
  "sub": "222222.11111111111111111111111111111111.3334",
  "c_hash": "9C1Y7-lPwAXGTrrAbciP1T",
  "email": "hap41ok9pl@privaterelay.appleid.com",
  "email_verified": "true",
  "is_private_email": "true",
  "auth_time": 1571515848
}
Enter fullscreen mode Exit fullscreen mode

Same JWT will be provided to API Server in step 5 but with different timestamps and hash key and value. sub is my user id associated with this application, aud is bundle of the application. hash, kid and alg are needed to confirm that this JWT was created by apple, and confirm it with Apple public key it will be done in step 6 by API server.

  1. Register / Login - We are sending our authorizationCode as part of our request.
  2. /token - thanks to authorizationCode API Server is able to send /token request with parameters:
{
    grant_type: "authorization_code",
    code: "this is authorizationCode",
    client_id: "bundle id same value as in “aud” in” JWT above",
    client_secret: "client secret generated for this app"
}
Enter fullscreen mode Exit fullscreen mode
  1. TokenResponse - response for /token is
{
    "access_token": "a5a45fnksdnfk83jflkasfksd98c29fdf1.1.pfsmo.bfd324145555r15",
    "token_type": "Bearer",
    "expires_in": 3600,
    "refresh_token": "r7999999999d0485b85a0d6124124140.1.pfsmo.YFFFFFsadazJYjzw",
    "id_token": "JWT token"
}
Enter fullscreen mode Exit fullscreen mode

Put your id_token again to jwt.io and decode - user values are same as in JWT from ASAuthorizationAppleIDCredential- things that are different are described above. We can notice that there is no full name returned by the JWT, so we have to pass it from app.

  1. Register / login user - server needs to confirm with Apple public key that JWT was created by them, and not injected in any way. Now we can create our user we have verified email, userID so everything we need.
  2. Establish user session - response returns our new user with accessToken and refreshToken.

🆒 so where is the problem?

As you may noticed I described register path, with register JWT but how does login JWT looks like?

PAYLOAD STEP 5)

{
  "iss": "https://appleid.apple.com",
  "aud": "com.myapp.app",
  "exp": 1571516802,
  "iat": 1571516202,
  "sub": "222222.11111111111111111111111111111111.3334",
  "at_hash": "ArC1Y7-lPwAXGTrrAbciP1",
  "auth_time": 1571516173
}
Enter fullscreen mode Exit fullscreen mode

Ok so where is the email?

This is our problem, there is no email key in login flow. That means your API Service not only have to implement Apple version of OpenID Connect - main difference for us is no /userinfo endpoint. It’s not OpenID Connect compliant, you can check here how it differs. If you are using email to authenticate a user, you may have a problem. Email shouldn’t be mandatory in your system and not only because you may have problem with SIWA, but if you are supporting other identity providers like Facebook, how do you behave if someone has created an account with his/her phone number?

Summary

Why Sign In With Apple may be problematic? This article was published at 20th of October 2019 - and for that day apple wasn’t OpenID Connect compliant. Email was only returned once during registration flow, if your API Service needs email to authenticate user you got a problem. Is it bad? I don’t think so - email shouldn’t be mandatory, Facebook may not return email as well if someone has created account using phone number. If you have to migrate start it now because Apple Requiring New Apps and App Updates to Be Built With iOS 13 SDK Starting in April 2020 so the clock is ticking.

List of helpful articles:
What the Heck is Sign In with Apple? | Okta Developer
So They’ve Signed in with Apple, Now What? — Curtis Herbert
“Sign in with Apple” implementation hurdles - DEV Community 👩‍💻👨‍💻 by @costerad
How to Add Sign in with Apple to iOS Apps - Getting Started Guide by @auth0

Thanks!

Top comments (1)

Collapse
 
cyupa89 profile image
Ciprian Redinciuc • Edited

You have to use the sub. There's no problem with that once you stored it in your DB.