passport-apple
Passport.js OAuth2 strategy for Sign in with Apple, handling Apple's JWT client-secret generation and POST-based callback flow.
Repository Health
Technical Analysis
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 existingpassport.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_postcallback, remappingreq.bodyintoreq.queryso Passport’s OAuth2Strategy machinery works unmodified - Capture of the one-time
userpayload (first name/last name) Apple sends only on a user’s first authorization, exposed asreq.appleProfile - Configurable
passReqToCallback, customauthorizationURL/tokenURLoverrides, and access to the decodedidTokenclaims (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.
Used by 4 apps in this directory
Blinko
Knowledge Management · Note Taking
A self-hosted, AI-powered card note-taking tool that lets you capture fleeting thoughts instantly and retrieve them with natural language search.
LibreChat
Developer Tools · AI Assistants
Unite every major AI model in one self-hosted chat platform with agents, code execution, MCP tools, and enterprise authentication.
Rocket.Chat
Team Chat
The secure, self-hosted team communications platform for organizations that cannot compromise on data sovereignty.
Worklenz
Project Management · Product Management · Collaboration
All-in-one open source project management for teams who want full control — plan projects, track tasks, manage resources, and monitor finances without the SaaS lock-in.