npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mantlejs/auth-apple

v0.2.0

Published

Apple Sign-In strategy for Mantle JS

Readme

@mantlejs/auth-apple

Sign in with Apple strategy for Mantle JS. Implements the authorization code flow with response_mode=form_post — no Passport.js dependency. Registers GET /auth/apple and POST /auth/apple/callback on the HTTP transport, then finds or creates a user record and returns a Mantle JWT pair.


Installation

npm install @mantlejs/auth-apple

Concepts

No static client secret

Apple does not issue a client secret. Instead, every token exchange is authenticated with a short-lived ES256 JWT signed from your Sign in with Apple private key (the .p8 file). Arctic signs this JWT per exchange (see ADR-002) — you configure teamId, keyId, and privateKey instead of a clientSecret.

POST callback

Whenever the name/email scopes are requested, Apple requires response_mode=form_post: the callback arrives as an application/x-www-form-urlencoded POST rather than a GET redirect. CSRF protection is the state round-trip through the OAuthStateStore (Apple does not support PKCE for this flow).

Profile from the id_token

Apple has no userinfo endpoint. The profile comes from the id_token returned by the token exchange: sub becomes the user ID, email the email. The id_token is obtained directly from Apple's token endpoint over TLS, so its payload is decoded without a JWKS round-trip.

First-login name capture

Apple sends the user's name in a user form field on the first authorization only. The plugin reads it into the profile, and find-or-create persists it when the user record is created — subsequent logins without the field lose nothing.

Find-or-create

On callback the plugin searches the configured user service for a record where appleId (configurable) matches the id_token's sub claim. If no record is found, it creates one with { appleId, email, name } from the profile. The same user is returned on every subsequent sign-in.


Quick start

import { mantle } from "@mantlejs/mantle";
import { express } from "@mantlejs/express";
import { auth } from "@mantlejs/auth";
import { appleStrategy } from "@mantlejs/auth-apple";

const app = mantle()
  .configure(express())
  .configure(auth({ secret: process.env.JWT_SECRET! }))
  .configure(
    appleStrategy({
      clientId: process.env.APPLE_CLIENT_ID!, // Services ID, e.g. "com.example.app.web"
      teamId: process.env.APPLE_TEAM_ID!,
      keyId: process.env.APPLE_KEY_ID!,
      privateKey: process.env.APPLE_PRIVATE_KEY!, // PEM contents of the .p8 key
    }),
  );

app.listen(3030);

Sign-in flow:

  1. Redirect the browser to GET /auth/apple
  2. User authenticates with Apple and consents
  3. Apple POSTs code, state (and user on first login) to /auth/apple/callback
  4. The plugin exchanges the code, decodes the id_token, finds or creates the user, and responds:
{
  "accessToken": "eyJhbGciOiJIUzI1NiJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiJ9...",
  "user": { "id": "1", "appleId": "001234.abc...", "email": "[email protected]", "name": "Alice Doe" }
}

API

appleStrategy(config)

function appleStrategy(config: AppleStrategyConfig): MantlePlugin;

type AppleStrategyConfig = {
  clientId: string; // Services ID (the OAuth client_id)
  teamId: string; // Apple Developer Team ID
  keyId: string; // Key ID of the Sign in with Apple private key
  privateKey: string; // PKCS#8 PEM contents of the .p8 key
  callbackUrl?: string; // Default: '/auth/apple/callback'
  scope?: string[]; // Default: ['name', 'email']
  entity?: string; // Default: 'users'
  entityIdField?: string; // Default: 'appleId'
};

| Field | Type | Default | Description | | --------------- | ---------- | ------------------------ | ------------------------------------------------------------------- | | clientId | string | — | Services ID from the Apple Developer portal (required) | | teamId | string | — | Apple Developer Team ID (required) | | keyId | string | — | Key ID of the Sign in with Apple key (required) | | privateKey | string | — | PEM contents of the downloaded .p8 private key (required) | | callbackUrl | string | '/auth/apple/callback' | Callback path — must match the Return URL registered with Apple | | scope | string[] | ['name', 'email'] | Sign in with Apple scopes | | entity | string | 'users' | Mantle service used to find or create users | | entityIdField | string | 'appleId' | Field on the user record matched against the id_token's sub claim | | redirectUrl | string | none — returns JSON | Frontend URL to redirect to on completion, with tokens (or an error) in the URL fragment — see @mantlejs/auth-oauth |

Routes registered:

| Method | Path | Description | | ------ | ---------------------- | ---------------------------------------------------- | | GET | /auth/apple | Redirect to Apple's sign-in page | | POST | /auth/apple/callback | Handle the form_post callback, issue Mantle JWT pair |

Must be configured after an HTTP transport and auth().


Types

import type { AppleStrategyConfig } from "@mantlejs/auth-apple";

AppleStrategyConfig extends OAuthPluginConfig from @mantlejs/auth-oauth, replacing clientSecret with teamId/keyId/privateKey.


Apple Developer setup

  1. In developer.apple.com, create an App ID with the Sign in with Apple capability
  2. Create a Services ID (this is your clientId) and enable Sign in with Apple for it
  3. Register your domain and Return URL: https://your-domain.com/auth/apple/callback (Apple requires HTTPS — no localhost)
  4. Create a Sign in with Apple key, download the .p8 file (one-time download), and note its Key ID
  5. Find your Team ID on the membership page

Development

npx nx build auth-apple   # compile
npx nx test auth-apple    # run tests
npx nx lint auth-apple    # lint

Publishing

npx nx build auth-apple
cd packages/auth-apple
npm publish --access public