apple-sign-in
apple-sign-in copied to clipboard
Make documentation more beginner-friendly
I feel like this repo is missing a lot of documentation.
No explanation for clientId, redirectURI, scopes, state and nonce params.
Also it's not clear that you have to create a new App ID on https://developer.apple.com/ with "Sign in with Apple" capability enabled etc.
Yes please, give more info about the options.
yeah, @mlynch Hello, Can you remind the explanation for clientId, redirectURI, scopes, state, and nonce params in detail? Thanks support.
Also I had a question related to this. I'd like to know if there is any additional verification that I need to do with the returned "authorizationCode". Or if I get an "authorizationCode" and an "identityToken" back then can I assume that it is a verified and valid login?
Below is the information I gathered while working on the Apple Sign In integration. This is compiled from Apple docs and external sources and might not be 100% correct, use with care.
Authentication Request Options
Options look like this:
const options: SignInWithAppleOptions = {
clientId: 'com.myapp.app',
redirectURI: 'https://www.myapp.com/login',
scopes: 'email name',
state: '12345',
nonce: 'nonce',
}
clientId: For native app: the application ID, same as “appId” in capacitor config.
For web app: a client ID assigned to it after creating a new identity record for the website in Certificates, Identifiers & Profiles, see also About Sign in with Apple.
redirectUri: As I understand, redirectUri
is only relevant for the web app, the URL to redirect to from the Apple Sign In dialog.
state: used as CSRF token:
- App backend generates “state” value when we display the login form
- Frontend adds “state” to the auth request
- Apple sends back the response with “state” in it
- Response is forwarded to the backend
- Backend compares initial “state” with the one in the response
Note: probably this is more relevant for the sign in process in the web app, not sure if there is a way to hack into the sign in when it is performed in the native app (as request/response here happen on the level of the Apple platform code).
Note: plugin actually does not return state
, so at the moment it is not possible to verify it, see #66.
nonce: it is encoded into the “identityToken” that can be decoded (JWT token) and compared on the server:
- App backend (or frontend?) generates unique “nonce” value when we display the login form
- Frontend adds “nonce” to the auth request
- Apple sends back the response with “identityToken” with “nonce” encoded in it
- Frontend resends apple response to the backend
- Backend decodes “identityToken” and makes sure that “nonce” value is the same as was initially sent.
This is to prevent replay attacks: a network sniffer records Apple response and then a recorded response is sent to the app to get the account access.
References:
- Authenticating Users with Sign in with Apple | Apple Developer Documentation
- Generate and Validate Tokens | Apple Developer Documentation
- Verifying a User | Apple Developer Documentation
- ASAuthorizationOpenIDRequest | Apple Developer Documentation
- Difference between OAuth 2.0 "state" and OpenID "nonce" parameter? Why state could not be reused? - Stack Overflow
- Get the most out of Sign in with Apple | by Jinha Park | The Startup | Medium
- Sign in with Apple Tutorial, Part 1: Apps | Sarunw
Authentication Response Data
We have following fields returned:
- familyName, givenName, email - user name and email
- user - unique Apple user ID
- authorizationCode
- identityToken
familyName, givenName, email user name and email, returned only on first sign in request ("null"s here after that)
user: The unique Apple’s user ID (also it is unique for your development team, so other developers/app would have a different ID for the same user).
The user identifier should be used instead of an email address to identify the user. The app backend should save this user ID and use it to find the account on future sign ups.
See: Authenticating Users with Sign in with Apple
Note: for the user identification we are actually forced to use user ID as familyName, givenName and email are only returned on first sign in. Following sign ins have “null”s here.
Update: it looks like email is also present inside the identityToken, see Retrieve the User’s Information from Apple ID Servers:
email A String value representing the user’s email address. The email address is either the user’s real email address or the proxy address, depending on their status private email relay service.
identityToken: should be verified by the app backend, see: Verifying a User. Can be used to request user information from the Apple server.
authorizationCode: can be used by the app backend for additional validation or to get new access tokens (to talk to Apple servers from the backend).
See Verifying a User and Generate and Validate Tokens: Use this endpoint to either authorize a user by validating the authorization code received by your app, or by validating an existing refresh token to verify a user session or obtain access tokens.
This is actually pretty simple, but it takes few hours to pick up pieces.
Frontend integration
iOS platform
import { SignInWithAppleOptions } from '@capacitor-community/apple-sign-in'
const options: SignInWithAppleOptions = {
// The appId defined in capacitor.config.ts
clientId: 'com.your.webservice',
// Ignored for iOS Platform
redirectURI: '',
scopes: 'email name',
};
Web platform
import { SignInWithAppleOptions } from '@capacitor-community/apple-sign-in'
const options: SignInWithAppleOptions = {
// Service ID in your Apple Developer Account
clientId: 'com.your.webservice',
// This should be window.location origin because this plugin uses usePopup: true and data are sent back to opener
redirectURI: 'https://www.yourfrontend.com/login',
scopes: 'email name',
};
Android platform
Not implemented, see https://github.com/capacitor-community/apple-sign-in/issues/13
Backend integration
- Submit the
await SignInWithApple.authorize(options)
result reponse to your backend. - Verify
identityToken
as in example here: https://www.npmjs.com/package/react-apple-signin-auth#server-side-authentication-nodejs-backend but note, that payload format is slightly different: https://github.com/capacitor-community/apple-sign-in/blob/6237a49a0fd45a9d8a69672e722a71deaf26a831/src/definitions.ts#L13-L22 - Extract
payload.sub
andpayload.email
fromidentityToken
- Extract
givenName
andfamilyName
(you'll receive these only once). Probably it's good idea to use state or nonce to make sure that data has not been tampered