{"id":10512,"library":"apple-signin-auth","title":"Apple Sign-in for Node.js","description":"`apple-signin-auth` is a Node.js library designed to simplify server-side implementation of Apple's 'Sign in with Apple' authentication flow. It provides a comprehensive API for generating authorization URLs, securely creating and managing client secrets (JSON Web Tokens), and exchanging authorization codes obtained from Apple for user access and refresh tokens. The package is currently at version 2.0.0, which mandates Node.js 18 or newer due to its adoption of native `fetch`. The library demonstrates a moderately active release cadence, with recent updates focusing on modernizing its codebase and enhancing resilience. Its key strengths lie in abstracting the complexities of direct interaction with Apple's REST API, handling JWT client secret generation, supporting `code_verifier` for enhanced security, and providing a clear, developer-friendly interface for the complete Apple Sign-in process on backend applications.","status":"active","version":"2.0.0","language":"javascript","source_language":"en","source_url":"https://github.com/a-tokyo/apple-signin-auth","tags":["javascript","apple","signin","login","auth","authentication","node","jwt","es6","typescript"],"install":[{"cmd":"npm install apple-signin-auth","lang":"bash","label":"npm"},{"cmd":"yarn add apple-signin-auth","lang":"bash","label":"yarn"},{"cmd":"pnpm add apple-signin-auth","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Used internally for generating and verifying client secrets (JWTs) when interacting with Apple's authentication servers.","package":"jsonwebtoken","optional":false}],"imports":[{"note":"While CommonJS `require` still works, ESM `import` is the recommended pattern, especially since v2.0.0 due to its reliance on native Node.js fetch.","wrong":"const appleSignin = require('apple-signin-auth');","symbol":"appleSignin","correct":"import appleSignin from 'apple-signin-auth';"},{"note":"Named imports are available for individual functions, allowing for tree-shaking and clearer dependency management.","wrong":"import appleSignin from 'apple-signin-auth'; const authorizationUrl = appleSignin.getAuthorizationUrl(...);","symbol":"getAuthorizationUrl","correct":"import { getAuthorizationUrl } from 'apple-signin-auth';"},{"note":"Used for generating the client secret JWT required for token exchange and revocation. This function is crucial for server-side operations.","symbol":"getClientSecret","correct":"import { getClientSecret } from 'apple-signin-auth';"},{"note":"This function is central to exchanging the authorization code received from Apple for user tokens.","symbol":"getAuthorizationToken","correct":"import { getAuthorizationToken } from 'apple-signin-auth';"}],"quickstart":{"code":"import { getAuthorizationUrl, getClientSecret, getAuthorizationToken } from 'apple-signin-auth';\nimport type { AuthorizationTokenResponse } from 'apple-signin-auth';\n\n// --- Configuration (replace with your actual values) ---\nconst CLIENT_ID = process.env.APPLE_CLIENT_ID ?? 'com.example.app';\nconst TEAM_ID = process.env.APPLE_TEAM_ID ?? 'YOUR_APPLE_TEAM_ID';\nconst KEY_IDENTIFIER = process.env.APPLE_KEY_IDENTIFIER ?? 'YOUR_KEY_IDENTIFIER';\nconst PRIVATE_KEY_STRING = process.env.APPLE_PRIVATE_KEY_STRING ?? '-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----'; // Make sure this is secure in production\nconst REDIRECT_URI = process.env.APPLE_REDIRECT_URI ?? 'http://localhost:3000/auth/apple/callback';\n\nasync function handleAppleSignIn(authorizationCode: string, state?: string) {\n  // 1. Generate the client secret required for token exchange\n  const clientSecret = getClientSecret({\n    clientID: CLIENT_ID,\n    teamID: TEAM_ID,\n    privateKey: PRIVATE_KEY_STRING,\n    keyIdentifier: KEY_IDENTIFIER,\n    expAfter: 15777000, // Optional: JWT expiration time (default is 5 minutes)\n  });\n\n  // 2. Exchange the authorization code for access and refresh tokens\n  try {\n    const tokenResponse: AuthorizationTokenResponse = await getAuthorizationToken(authorizationCode, {\n      clientID: CLIENT_ID,\n      redirectUri: REDIRECT_URI,\n      clientSecret: clientSecret,\n      // code_verifier: 'your_code_verifier_if_used' // Optional, for PKCE flow\n    });\n\n    console.log('Successfully exchanged code for tokens:', tokenResponse);\n    const { access_token, refresh_token, id_token, expires_in } = tokenResponse;\n\n    // You would typically decode and verify the ID token here\n    // and then save user session, profile data, tokens in your database.\n    // Example: const decodedIdToken = await appleSignin.verifyIdToken(id_token, { audience: CLIENT_ID });\n    // console.log('Decoded ID Token:', decodedIdToken);\n\n    return tokenResponse;\n  } catch (err) {\n    console.error('Error during Apple Sign-in token exchange:', err);\n    throw err;\n  }\n}\n\n// Example usage for generating authorization URL (typically done on frontend or for redirect)\nfunction getSignInUrl() {\n  const options = {\n    clientID: CLIENT_ID,\n    redirectUri: REDIRECT_URI,\n    state: 'random_csrf_token_here', // IMPORTANT: Generate a secure random string\n    responseMode: 'form_post', // Or 'query', 'fragment'\n    scope: 'email name', // Request email and name\n  };\n  const authorizationUrl = getAuthorizationUrl(options);\n  console.log('Apple Sign-in Authorization URL:', authorizationUrl);\n  return authorizationUrl;\n}\n\n// Simulate an incoming authorization code after user redirects\nconst simulateAuthorizationCode = async () => {\n  console.log('\\n--- Initiating simulated Apple Sign-in flow ---');\n  getSignInUrl(); // This URL would be visited by the user in a browser\n\n  // In a real application, 'some_authorization_code' and 'some_state' would come from the redirect callback\n  const mockCode = 'MOCK_APPLE_AUTHORIZATION_CODE'; \n  const mockState = 'random_csrf_token_here';\n\n  try {\n    const result = await handleAppleSignIn(mockCode, mockState);\n    console.log('\\nSimulated Sign-in successful:', result);\n  } catch (error) {\n    console.error('\\nSimulated Sign-in failed:', error);\n  }\n};\n\nsimulateAuthorizationCode();","lang":"typescript","description":"This quickstart demonstrates the full server-side Apple Sign-in flow: generating an authorization URL, creating a JWT client secret, and exchanging an authorization code for user tokens, including an example of handling the redirect callback."},"warnings":[{"fix":"Upgrade your Node.js environment to version 18 or higher, or explicitly use `apple-signin-auth` version `<2.0.0` if you cannot upgrade Node.js.","message":"Version 2.0.0 of `apple-signin-auth` introduces a breaking change by requiring Node.js >= 18.0.0. This is due to the replacement of the `node-fetch` package with Node.js's native `fetch` API. Applications running on older Node.js versions will encounter `ReferenceError: fetch is not defined`.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Refer to the official Apple documentation and the library's README for detailed instructions on configuring your Apple Developer account, App IDs, Service IDs, and private keys. Ensure all identifiers and keys are correctly generated and matched.","message":"Implementing 'Sign in with Apple' requires significant prerequisite setup within your Apple Developer Program account. This includes enrolling in the program, creating specific App IDs and Service IDs, and generating a private key for your Service ID. Incorrect setup will lead to authentication failures.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Generate a strong, random `state` value before redirecting the user to Apple. Store this `state` securely (e.g., in a session) and compare it against the `state` parameter received in the callback URL. Abort the process if they do not match.","message":"The `state` parameter in the authorization URL options is critical for CSRF (Cross-Site Request Forgery) protection. It must be an unguessable random string generated by your server and verified upon callback. Failure to properly use and validate the `state` parameter can expose your application to CSRF attacks.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Store your private key securely, preferably as an environment variable or loaded from a file path using `privateKeyPath`. Never expose the private key directly in your application's public codebase.","message":"The `privateKey` used for generating the client secret JWT should be handled with extreme care. Hardcoding it directly in source code or exposing it in client-side bundles is a severe security risk. In production, this key must be loaded from a secure environment variable or a secure file system location.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Ensure your application's logic correctly handles and persists the user's `name` and `email` from the `id_token` during their first successful 'Sign in with Apple'. For subsequent logins, use the `userIdentifier` to retrieve the stored user data from your database.","message":"Apple only provides the user's `name` and `email` during the *first* sign-in attempt. Subsequent sign-ins will typically return `null` for these fields, providing only the unique `userIdentifier`. Your application must store this information upon the initial registration.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Upgrade your Node.js environment to version 18.0.0 or newer. Alternatively, downgrade `apple-signin-auth` to a version below 2.0.0 if a Node.js upgrade is not feasible.","cause":"Using `apple-signin-auth` version 2.0.0 or higher with a Node.js runtime older than 18.0.0. Version 2.0.0 replaced `node-fetch` with native Node.js `fetch`.","error":"ReferenceError: fetch is not defined"},{"fix":"Ensure you are using a fresh, unused `authorizationCode` obtained directly from Apple's redirect. Double-check that your `redirectUri` and `clientID` used in `getAuthorizationUrl` and `getAuthorizationToken` are identical and match your Apple Developer Console configuration.","cause":"This error typically indicates that the `authorizationCode` passed to `getAuthorizationToken` is either incorrect, has already been used, or has expired. Authorization codes are single-use and short-lived.","error":"Error: invalid_grant - The authorization code is invalid or has expired."},{"fix":"Verify that your `clientID`, `teamID`, `keyIdentifier`, and `privateKey` are all correct and match your Apple Developer Account configuration. Ensure the `expAfter` parameter for `getClientSecret` generates a JWT that is still valid. Double-check your `Service ID` configuration in the Apple Developer Console.","cause":"This usually means there's a mismatch or error in the parameters used to generate the `clientSecret` JWT or the `clientID` itself. Common causes include incorrect `clientID`, `teamID`, `keyIdentifier`, or `privateKey` when calling `getClientSecret`, or an expired `clientSecret`.","error":"Error: invalid_client - The client ID or client secret is invalid."},{"fix":"The library internally handles public key fetching and caching. If this error persists, ensure your server has network access to `https://appleid.apple.com/auth/keys`. It's generally recommended not to cache the `id_token` itself long-term on the client side, but rather use the `refreshToken` to obtain new `id_token`s when needed for verification.","cause":"This can occur when `verifyIdToken` is called but the `kid` (key ID) in the received `id_token` does not match any available public keys from Apple's JWKS endpoint (`https://appleid.apple.com/auth/keys`). This might happen if Apple has rotated its keys or if there's a caching issue.","error":"Error: invalid_id_token_public_key - The public key used to verify the ID token is invalid or missing."}],"ecosystem":"npm"}