capacitor-community / apple-sign-in

Sign in with Apple Support
MIT License
143 stars 60 forks source link

Make documentation more beginner-friendly #48

Open merganon opened 3 years ago

merganon commented 3 years ago

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.

TheNemus commented 3 years ago

Yes please, give more info about the options.

timeisgolden commented 2 years ago

yeah, @mlynch Hello, Can you remind the explanation for clientId, redirectURI, scopes, state, and nonce params in detail? Thanks support.

DanielClyde commented 2 years ago

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?

serebrov commented 2 years ago

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:

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:

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:

Authentication Response Data

We have following fields returned:

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.

piotr-cz commented 7 months ago

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

  1. Submit the await SignInWithApple.authorize(options) result reponse to your backend.
  2. 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
  3. Extract payload.sub and payload.email from identityToken
  4. Extract givenName and familyName (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