passport-apple

Passport.js OAuth2 strategy for Sign in with Apple, handling Apple's JWT client-secret generation and POST-based callback flow.

SDK
npm
v2.0.2
145stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
39/100Needs Attention
Development Activity4
Maintenance20
Community52
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
45/100Fair
Architecture60
Code Quality25
Innovation40
Learning Curve55

passport-apple is a Passport.js strategy that implements Sign in with Apple, Apple’s OAuth2/OpenID Connect identity provider used for “Sign in with Apple” buttons in web and mobile apps. Rather than reimplementing passport-oauth2 from scratch, it extends OAuth2Strategy and patches in the two behaviors Apple’s implementation requires that most OAuth2 providers don’t: signing a short-lived ES256 JWT as the client_secret on every token exchange, and normalizing Apple’s POST-based callback (delivered via form_post) into the query-parameter shape Passport expects.

The library also captures the one-time name/email payload Apple sends only on a user’s very first authorization, exposing it as req.appleProfile so the consuming app can persist it before Apple stops sending it on subsequent logins.

What You Get

  • A drop-in Passport.js Strategy class (passport-apple) that plugs into any existing passport.use(...) setup alongside other OAuth strategies
  • Automatic generation of Apple’s required ES256-signed JWT client_secret on every token exchange, using either a private key file path or an in-memory key string
  • Built-in handling of Apple’s POST-based form_post callback, remapping req.body into req.query so Passport’s OAuth2Strategy machinery works unmodified
  • Capture of the one-time user payload (first name/last name) Apple sends only on a user’s first authorization, exposed as req.appleProfile
  • Configurable passReqToCallback, custom authorizationURL/tokenURL overrides, and access to the decoded idToken claims (sub, email, email_verified) in the verify callback

Common Use Cases

  • Adding “Sign in with Apple” as a login option alongside Google/Facebook/GitHub OAuth in an Express app that already uses Passport.js
  • Satisfying Apple App Store Guideline 4.8, which requires apps offering third-party login to also offer Sign in with Apple
  • Implementing account creation flows that must capture a user’s name on first Apple sign-in before Apple stops including it in later logins
  • Building a unified SSO/auth server that normalizes Apple’s non-standard OAuth callback behavior alongside standard OAuth2 providers

Under The Hood

Architecture The package is a thin two-file module: src/strategy.js defines a Strategy constructor that extends passport-oauth2’s OAuth2Strategy via util.inherits, overriding three methods — it monkey-patches this._oauth2.getOAuthAccessToken to inject a freshly generated JWT client secret into the token-exchange POST, overrides authenticate() to merge req.body into req.query so Apple’s form_post callback works with Passport’s query-based flow, and overrides authorizationParams() to set Apple’s required response_type, scope, and response_mode. Cryptographic concerns are delegated entirely to src/token.js’s AppleClientSecret class, which reads a private key (from a file path or string) and signs a short-lived ES256 JWT per request. The design is tightly coupled to passport-oauth2’s internal call shape — reassigning this._oauth2.getOAuthAccessToken directly rather than using a documented extension point — so an upstream signature change in passport-oauth2 would break the override silently.

Tech Stack A plain CommonJS Node.js module with no build step or bundler — package.json’s main points straight at src/strategy.js, consumed via require() in Express/Passport apps. Its only two runtime dependencies are jsonwebtoken (^9.0.0), used for ES256-signing the Apple client secret, and passport-oauth2 (^1.6.1), the base strategy class it extends using pre-ES6 util.inherits-style prototypal inheritance rather than JavaScript classes. There is no database layer, no CLI, and no bundler config; the module is distributed purely as an npm package consumed at the source level.

Code Quality No test files or test framework exist in the repository — package.json’s test script is a stub that echoes an error and exits 1 — and no CI configuration (.github/workflows or similar) is present. Error handling is inconsistent: token.js rejects promises with plain strings ("AppleAuth Error – ...") rather than Error objects, while strategy.js relies on node-style callback error-first conventions. There is no TypeScript, no visible linter/formatter config, and no static type checking, though JSDoc-style comments document the public constructor options reasonably well.

API Design The module’s public surface is minimal and idiomatic for Passport: a single Strategy class registered via passport.use(new AppleStrategy({...}, verifyCallback)), matching the shape developers already know from other OAuth strategies, so switching providers requires no route-level rewrites. Getting started demands assembling Apple-specific configuration (team ID, key ID, PEM private key) that has nothing to do with the library itself, and the README documents this setup path together with the req.appleProfile one-time-name quirk and the POST-to-GET redirect workaround needed to preserve session cookies across Apple’s cross-site callback.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search