{"id":15649573,"url":"https://github.com/a-tokyo/apple-signin-auth","last_synced_at":"2025-05-15T11:05:31.324Z","repository":{"id":37010160,"uuid":"242808771","full_name":"a-tokyo/apple-signin-auth","owner":"a-tokyo","description":" Apple signin for nodeJS.","archived":false,"fork":false,"pushed_at":"2025-04-11T01:09:26.000Z","size":620,"stargazers_count":157,"open_issues_count":9,"forks_count":35,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-14T19:59:37.451Z","etag":null,"topics":["apple","apple-id","apple-signin","appleid","auth","authentication","client","flow","id-token","ios","jwt","login","node","nodejs","react","server-notifications","signin","typescript","vue","webhook"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/a-tokyo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["a-tokyo"],"buy_me_a_coffee":"ahmedtokyo"}},"created_at":"2020-02-24T18:16:28.000Z","updated_at":"2025-04-11T01:00:15.000Z","dependencies_parsed_at":"2024-06-18T13:38:26.466Z","dependency_job_id":"dec71354-1140-4f51-a847-e5e97638a09b","html_url":"https://github.com/a-tokyo/apple-signin-auth","commit_stats":{"total_commits":99,"total_committers":12,"mean_commits":8.25,"dds":"0.31313131313131315","last_synced_commit":"7cad3a63fbe37b923704f44f5e612abd3a787d48"},"previous_names":[],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-tokyo%2Fapple-signin-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-tokyo%2Fapple-signin-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-tokyo%2Fapple-signin-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-tokyo%2Fapple-signin-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/a-tokyo","download_url":"https://codeload.github.com/a-tokyo/apple-signin-auth/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254328385,"owners_count":22052632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple","apple-id","apple-signin","appleid","auth","authentication","client","flow","id-token","ios","jwt","login","node","nodejs","react","server-notifications","signin","typescript","vue","webhook"],"created_at":"2024-10-03T12:30:19.509Z","updated_at":"2025-05-15T11:05:31.318Z","avatar_url":"https://github.com/a-tokyo.png","language":"JavaScript","funding_links":["https://github.com/sponsors/a-tokyo","https://buymeacoffee.com/ahmedtokyo"],"categories":[],"sub_categories":[],"readme":"# apple-signin-auth\n\n Apple signin for Node.js.\n\n\u003ca href=\"https://npmjs.com/package/apple-signin-auth\"\u003e\n  \u003cimg src=\"https://img.shields.io/npm/v/apple-signin-auth.svg\"\u003e\u003c/img\u003e\n  \u003cimg src=\"https://img.shields.io/npm/dt/apple-signin-auth.svg\"\u003e\u003c/img\u003e\n\u003c/a\u003e\n\u003ca href=\"https://twitter.com/intent/follow?screen_name=ahmad_tokyo\"\u003e\u003cimg src=\"https://img.shields.io/twitter/follow/ahmad_tokyo.svg?label=Follow%20@ahmad_tokyo\" alt=\"Follow @ahmad_tokyo\"\u003e\u003c/img\u003e\u003c/a\u003e\n\n\n## Prerequisites\n1. You should be enrolled in [Apple Developer Program](https://developer.apple.com/programs/).\n2. Please have a look at [Apple documentation](\nhttps://developer.apple.com/sign-in-with-apple/get-started/) related to \"Sign in with Apple\" feature.\n3. You should create App ID and Service ID in your Apple Developer Account.\n4. You should generate private key for your Service ID in your Apple Developer Account.\n\n- Node.js \u003e= 18.0.0 (for native fetch support). If you need support for Node versions less than 18, use v \u003c=2.0.0 of this library\n\n## Apple Signin Setup\nDeatiled confuguration instructions can be found at [blog post](https://medium.com/@artyomefremov/add-sign-in-with-apple-button-to-your-website-today-part-1-12ed1444623a?postPublishedType=initial) and [Apple docs](https://help.apple.com/developer-account/#/dev1c0e25352).\n\n## Installation\n\n```bash\nnpm install --save apple-signin-auth\n```\nOR\n```bash\nyarn add apple-signin-auth\n```\n\n## Usage\n\n### 1. Get authorization URL\nStart \"Sign in with Apple\" flow by redirecting user to the authorization URL.\n```js\nimport appleSignin from 'apple-signin-auth';\n// OR const appleSignin = require('apple-signin-auth');\n// OR import { getAuthorizationUrl } from 'apple-signin-auth';\n\nconst options = {\n  clientID: 'com.company.app', // Apple Client ID\n  redirectUri: 'http://localhost:3000/auth/apple/callback',\n  // OPTIONAL\n  state: 'state', // optional, An unguessable random string. It is primarily used to protect against CSRF attacks.\n  responseMode: 'query' | 'fragment' | 'form_post', // Force set to form_post if scope includes 'email'\n  scope: 'email' // optional\n};\n\nconst authorizationUrl = appleSignin.getAuthorizationUrl(options);\n```\nAlternatively, you can use [Sign In with Apple](https://developer.apple.com/documentation/signinwithapplejs) browser javascript library.\n\n### 2. Get access token\n2.1. Retrieve \"code\" query param from URL string when user is redirected to your site after successful sign in with Apple. Example:\nhttp://localhost:3000/auth/apple/callback?code=somecode\u0026state=123.\n\n2.2. Exchange retrieved \"code\" to user's access token.\n\nMore detail can be found in [Apple docs](https://developer.apple.com/documentation/signinwithapplerestapi/generate_and_validate_tokens).\n\n```js\n\nconst clientSecret = appleSignin.getClientSecret({\n  clientID: 'com.company.app', // Apple Client ID\n  teamID: 'teamID', // Apple Developer Team ID.\n  privateKey: 'PRIVATE_KEY_STRING', // private key associated with your client ID. -- Or provide a `privateKeyPath` property instead.\n  keyIdentifier: 'XXX', // identifier of the private key.\n  // OPTIONAL\n  expAfter: 15777000, // Unix time in seconds after which to expire the clientSecret JWT. Default is now+5 minutes.\n});\n\nconst options = {\n  clientID: 'com.company.app', // Apple Client ID\n  redirectUri: 'http://localhost:3000/auth/apple/callback', // use the same value which you passed to authorisation URL.\n  clientSecret: clientSecret\n};\n\ntry {\n  const tokenResponse = await appleSignin.getAuthorizationToken(code, options);\n} catch (err) {\n  console.error(err);\n}\n```\n\nResult of ```getAuthorizationToken``` command is a JSON object representing Apple's [TokenResponse](https://developer.apple.com/documentation/signinwithapplerestapi/tokenresponse):\n```js\n{\n    access_token: 'ACCESS_TOKEN', // A token used to access allowed data.\n    token_type: 'Bearer', // It will always be Bearer.\n    expires_in: 300, // The amount of time, in seconds, before the access token expires.\n    refresh_token: 'REFRESH_TOKEN', // used to regenerate new access tokens. Store this token securely on your server.\n    id_token: 'ID_TOKEN' // A JSON Web Token that contains the user's identity information.\n}\n```\n\n### 3. Verify token signature and get unique user's identifier\n```js\ntry {\n  const { sub: userAppleId } = await appleSignin.verifyIdToken(tokenResponse.id_token, {\n    // Optional Options for further verification - Full list can be found here https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback\n    audience: 'com.company.app', // client id - can also be an array\n    nonce: 'NONCE', // nonce // Check this note if coming from React Native AS RN automatically SHA256-hashes the nonce https://github.com/invertase/react-native-apple-authentication#nonce\n    // If you want to handle expiration on your own, or if you want the expired tokens decoded\n    ignoreExpiration: true, // default is false\n  });\n} catch (err) {\n  // Token is not verified\n  console.error(err);\n}\n```\n\n### 4. Refresh access token after expiration\n```js\n\nconst clientSecret = appleSignin.getClientSecret({\n  clientID: 'com.company.app', // Apple Client ID\n  teamID: 'teamID', // Apple Developer Team ID.\n  privateKey: 'PRIVATE_KEY_STRING', // private key associated with your client ID. -- Or provide a `privateKeyPath` property instead.\n  keyIdentifier: 'XXXXXXXXXX', // identifier of the private key. - can be found here https://developer.apple.com/account/resources/authkeys/list\n  // OPTIONAL\n  expAfter: 15777000, // Duration after which to expire JWT\n});\n\nconst options = {\n  clientID: 'com.company.app', // Apple Client ID\n  clientSecret\n};\n\ntry {\n  const {\n    access_token\n  } = appleSignin.refreshAuthorizationToken(refreshToken, options);\n} catch (err) {\n  console.error(err);\n}\n```\n\n### 5. a, Revoke tokens with refresh_token\n```js\n\nconst clientSecret = appleSignin.getClientSecret({\n  clientID: 'com.company.app', // Apple Client ID\n  teamID: 'teamID', // Apple Developer Team ID.\n  privateKey: 'PRIVATE_KEY_STRING', // private key associated with your client ID. -- Or provide a `privateKeyPath` property instead.\n  keyIdentifier: 'XXXXXXXXXX', // identifier of the private key. - can be found here https://developer.apple.com/account/resources/authkeys/list\n  // OPTIONAL\n  expAfter: 15777000, // Duration after which to expire JWT\n});\n\nconst options = {\n  clientID: 'com.company.app', // Apple Client ID\n  clientSecret,\n  tokenTypeHint: 'refresh_token'\n};\n\ntry {\n  await appleSignin.revokeAuthorizationToken(refreshToken, options);\n} catch (err) {\n  console.error(err);\n}\n```\n\n### 5. b, Revoke tokens with access_token\n```js\n\nconst clientSecret = appleSignin.getClientSecret({\n  clientID: 'com.company.app', // Apple Client ID\n  teamID: 'teamID', // Apple Developer Team ID.\n  privateKey: 'PRIVATE_KEY_STRING', // private key associated with your client ID. -- Or provide a `privateKeyPath` property instead.\n  keyIdentifier: 'XXXXXXXXXX', // identifier of the private key. - can be found here https://developer.apple.com/account/resources/authkeys/list\n  // OPTIONAL\n  expAfter: 15777000, // Duration after which to expire JWT\n});\n\nconst options = {\n  clientID: 'com.company.app', // Apple Client ID\n  clientSecret,\n  tokenTypeHint: 'access_token'\n};\n\ntry {\n  await appleSignin.revokeAuthorizationToken(accessToken, options);\n} catch (err) {\n  console.error(err);\n}\n```\n\n### Optional: Server-to-Server Notifications\n\nApple provides realtime server-to-server notifications of several user lifecycle\nevents:\n\n- `email-disabled`: The user hides their email behind Apple's private email\n  relay, and has opted to stop having emails forwarded by the private relay\n  service.\n- `email-enabled`: The user hides their email behind Apple's private email\n  relay, and has opted to resume having emails forwarded by the private relay\n  service.\n- `consent-revoked`: The user has decided to stop using Apple ID with your\n  application, e.g. by disconnecting the application from Settings. This should\n  be treated as a sign-out out by the user.\n- `account-delete`: The user has asked Apple to permanently delete their Apple\n  ID. The user identifier is no longer valid.\n\nNotifications are sent for each app group.\n\nThe notification is sent as a `POST` request with a JSON body. The request body\ncontains a JWT, with the event description on the JWT payload.\n\n```json\n{\n  \"payload\": \"\u003cserver-to-server notification JWT\u003e\"\n}\n```\n\nTo receive these notifications, you must do the following steps.\n\n#### 1. Host the webhook\n\n```js\napp.get(\"/apple-signin-webhook\", async (req, res) =\u003e {\n  try {\n    const { events } = await appleSignin.verifyWebhookToken(\n      req.body.payload,\n      {\n        // Optional Options for further verification - Full list can be found here https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback\n        audience: 'com.company.app', // client id - can also be an array\n      },\n    );\n    const {\n      sub: userAppleId,\n      type,\n      email // Only provided for email events\n    } = events;\n\n    switch (type) {\n      case 'email-disabled':\n        // Email will no longer be forwarded to the user via the private relay service\n        break;\n      case 'email-enabled':\n        // Email will be forwarded to the user again\n        break;\n      case 'consent-revoked':\n        // The user has decided to stop using Apple ID with this application - log them out\n        break;\n      case 'account-delete':\n        // The user has deleted their Apple ID\n        break;\n    }\n\n    res.sendStatus(200);\n} catch (e) {\n  // Event token is not verified\n  console.error(err)\n  res.sendStatus(500);\n});\n```\n\nNote:\n\n- TLS 1.2 is required to receive notifications at the specified endpoint.\n\n#### 2. Configure the webhook URL in the Apple Developer console\n\n2.1. Sign in to Apple Developer, go to \"Certificates, Identifiers \u0026 Profiles\",\nand select the Primary App ID for your application.\n\n2.2 Enable the \"Sign in with Apple\" capability (if not already enabled) and\nclick \"Configure\" (or \"Edit\").\n\n2.3 Under \"Server to Server Notification Endpoint\", enter the fully-qualified\nURL for your webhook, e.g. `https://example.com/api/apple-signin-webhook`,\nand save the changes.\n\nNotes:\n\n- A server-to-server webhook can only be configured for a Primary App ID.\n- The Apple docs for this step are located [here](https://help.apple.com/developer-account/?lang=en#/dev217f824b6).\n\n### Extra API functions\n- _setFetch: `(fetchFn: function) =\u003e void` - Sets the fetch function, defaults to native Node.js fetch. eg: appleSigninAuth._setFetch(fetchWithProxy);\n\n## Extras\n- Handles apple public keys switching solving this issue https://forums.developer.apple.com/thread/129047\n- Caches Apple's public keys and only refetches when needed\n- ES6 (Can be imported using `import appleSigning from 'apple-signin-auth/src'`)\n- Flow and TypeScript Types\n\n## Related Projects\n- [Apple Signin for web (React/Vue)](https://github.com/A-Tokyo/react-apple-signin)\n- [Apple Signin for React Native](https://github.com/invertase/react-native-apple-authentication)\n\n## Helpful resources\n- [React Native: Sign in with Apple by Ross Bulat](https://medium.com/@rossbulat/react-native-sign-in-with-apple-75733d3fbc3)\n- [Web: Signin with Apple](https://dev.to/onygami/how-to-add-signin-with-apple-on-your-website-43m9)\n  - Note that the frontend implementation can be replaced with [Apple Signin for web (React/Vue)](https://github.com/A-Tokyo/react-apple-signin).\n\n\n## Contributing\nPull requests are highly appreciated! For major changes, please open an issue first to discuss what you would like to change.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa-tokyo%2Fapple-signin-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fa-tokyo%2Fapple-signin-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa-tokyo%2Fapple-signin-auth/lists"}