Apple
Emulated Sign in with Apple / Apple OIDC for local development and testing. Use when the user needs to test Apple sign-in locally, emulate Apple OIDC…
Install
Quick install
npx skills add https://github.com/vercel-labs/emulate/tree/HEAD/skills/applenpx skills add vercel-labs/emulate --skill apple --agent claude-codenpx skills add vercel-labs/emulate --skill apple --agent cursornpx skills add vercel-labs/emulate --skill apple --agent codexnpx skills add vercel-labs/emulate --skill apple --agent opencodenpx skills add vercel-labs/emulate --skill apple --agent github-copilotnpx skills add vercel-labs/emulate --skill apple --agent windsurfMore install options
Shorthand — useful for multi-skill repos:
npx skills add vercel-labs/emulate --skill appleManual — clone the repo and drop the folder into your agent's skills directory:
git clone https://github.com/vercel-labs/emulate.gitcp -r emulate/skills/apple ~/.claude/skills/apple
Emulated Sign in with Apple / Apple OIDC for local development and testing. Use when the user needs to test Apple sign-in locally, emulate Apple OIDC…
appleby vercel
Emulated Sign in with Apple / Apple OIDC for local development and testing. Use when the user needs to test Apple sign-in locally, emulate Apple OIDC…npx skills add https://github.com/vercel-labs/emulate --skill appleDownload ZIPGitHub
Apple Sign In Emulator
Sign in with Apple emulation with authorization code flow, PKCE support, RS256 ID tokens, and OIDC discovery.
Start
`# Apple only
npx emulate --service apple
# Default port (when run alone)
# http://localhost:4000
`
Or programmatically:
`import { createEmulator } from 'emulate'
const apple = await createEmulator({ service: 'apple', port: 4004 })
// apple.url === 'http://localhost:4004'
`
Pointing Your App at the Emulator
Environment Variable
`APPLE_EMULATOR_URL=http://localhost:4004
`
OAuth URL Mapping
Real Apple URLEmulator URLhttps://appleid.apple.com/.well-known/openid-configuration$APPLE_EMULATOR_URL/.well-known/openid-configurationhttps://appleid.apple.com/auth/authorize$APPLE_EMULATOR_URL/auth/authorizehttps://appleid.apple.com/auth/token$APPLE_EMULATOR_URL/auth/tokenhttps://appleid.apple.com/auth/keys$APPLE_EMULATOR_URL/auth/keyshttps://appleid.apple.com/auth/revoke$APPLE_EMULATOR_URL/auth/revoke
Auth.js / NextAuth.js
`import Apple from '@auth/core/providers/apple'
Apple({
clientId: process.env.APPLE_CLIENT_ID,
clientSecret: process.env.APPLE_CLIENT_SECRET,
authorization: {
url: `${process.env.APPLE_EMULATOR_URL}/auth/authorize`,
params: { scope: 'openid email name', response_mode: 'form_post' },
},
token: {
url: `${process.env.APPLE_EMULATOR_URL}/auth/token`,
},
jwks_endpoint: `${process.env.APPLE_EMULATOR_URL}/auth/keys`,
})
`
Passport.js
`import { Strategy as AppleStrategy } from 'passport-apple'
const APPLE_URL = process.env.APPLE_EMULATOR_URL ?? 'https://appleid.apple.com'
new AppleStrategy({
clientID: process.env.APPLE_CLIENT_ID,
teamID: process.env.APPLE_TEAM_ID,
keyID: process.env.APPLE_KEY_ID,
callbackURL: 'http://localhost:3000/api/auth/callback/apple',
authorizationURL: `${APPLE_URL}/auth/authorize`,
tokenURL: `${APPLE_URL}/auth/token`,
}, verifyCallback)
`
Seed Config
`apple:
users:
- email: [email protected]
name: Test User
given_name: Test
family_name: User
- email: [email protected]
name: Private User
is_private_email: true
oauth_clients:
- client_id: com.example.app
team_id: TEAM001
name: My Apple App
redirect_uris:
- http://localhost:3000/api/auth/callback/apple
`
When no OAuth clients are configured, the emulator accepts any client_id. With clients configured, strict validation is enforced for client_id and redirect_uri.
Users with is_private_email: true get a generated @privaterelay.appleid.com email in the id_token instead of their real email.
API Endpoints
OIDC Discovery
`curl http://localhost:4004/.well-known/openid-configuration
`
Returns the standard OIDC discovery document with all endpoints pointing to the emulator:
`{
"issuer": "http://localhost:4004",
"authorization_endpoint": "http://localhost:4004/auth/authorize",
"token_endpoint": "http://localhost:4004/auth/token",
"jwks_uri": "http://localhost:4004/auth/keys",
"revocation_endpoint": "http://localhost:4004/auth/revoke",
"response_types_supported": ["code"],
"subject_types_supported": ["pairwise"],
"id_token_signing_alg_values_supported": ["RS256"],
"scopes_supported": ["openid", "email", "name"],
"token_endpoint_auth_methods_supported": ["client_secret_post"],
"response_modes_supported": ["query", "fragment", "form_post"]
}
`
JWKS
`curl http://localhost:4004/auth/keys
`
Returns an RSA public key (kid: emulate-apple-1) for verifying id_token signatures.
Authorization
`# Browser flow: redirects to a user picker page
curl -v "http://localhost:4004/auth/authorize?\
client_id=com.example.app&\
redirect_uri=http://localhost:3000/api/auth/callback/apple&\
scope=openid+email+name&\
response_type=code&\
state=random-state&\
nonce=random-nonce&\
response_mode=form_post"
`
Query parameters:
ParamDescriptionclient_idOAuth client ID (Apple Services ID)redirect_uriCallback URLscopeSpace-separated scopes (openid email name)stateOpaque state for CSRF protectionnonceNonce for ID token (optional)response_modequery (default), form_post, or fragment
The emulator renders an HTML page where you select a seeded user. After selection, it redirects (or auto-submits a form for form_post) to redirect_uri with code and state. On the first authorization per user/client pair, a user JSON blob is also included (matching Apple's real behavior).
Token Exchange
`curl -X POST http://localhost:4004/auth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "code=<authorization_code>&\
client_id=com.example.app&\
client_secret=<client_secret>&\
grant_type=authorization_code"
`
Returns:
`{
"access_token": "apple_...",
"refresh_token": "r_apple_...",
"id_token": "<jwt>",
"token_type": "Bearer",
"expires_in": 3600
}
`
The id_token is an RS256 JWT containing sub, email, email_verified (string), is_private_email (string), real_user_status, auth_time, and optional nonce.
Refresh Token
`curl -X POST http://localhost:4004/auth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "refresh_token=r_apple_...&\
client_id=com.example.app&\
grant_type=refresh_token"
`
Returns a new access_token and id_token. No new refresh_token is issued on refresh (matching Apple's behavior).
Token Revocation
`curl -X POST http://localhost:4004/auth/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=apple_..."
`
Returns 200 OK. The token is removed from the emulator's token map.
Common Patterns
Full Authorization Code Flow
`APPLE_URL="http://localhost:4004"
CLIENT_ID="com.example.app"
REDIRECT_URI="http://localhost:3000/api/auth/callback/apple"
# 1. Open in browser (user picks a seeded account)
# $APPLE_URL/auth/authorize?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&scope=openid+email+name&response_type=code&state=abc&response_mode=form_post
# 2. After user selection, emulator posts to:
# $REDIRECT_URI with code=<code>&state=abc (and user JSON on first auth)
# 3. Exchange code for tokens
curl -X POST $APPLE_URL/auth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "code=<code>&client_id=$CLIENT_ID&grant_type=authorization_code"
# 4. Decode the id_token JWT to get user info
`
Private Relay Email
When a user has is_private_email: true in the seed config, the id_token will contain a generated @privaterelay.appleid.com email instead of the user's real email. This matches Apple's Hide My Email behavior.
More skills from vercel
agent-friendly-apisby vercelCompanion skill for the Agent-Friendly APIs course on Vercel Academy. Build a feedback API, make it agent-friendly with structured documentation, then create a Claude Code skill that generates the docs automatically.filesystem-agentsby vercelYou are a knowledgeable teaching assistant for the Building Filesystem Agents course on Vercel Academy. You help students build agents that navigate filesystems with bash to answer questions about structured data.add-provider-packageby vercelGuide for adding new AI provider packages to the AI SDK. Use when creating a new @ai-sdk/<provider> package to integrate an AI service into the SDK.csvby vercelAnalyze and transform CSV data using bash toolsaiby vercelPythonai module — models, agents, hooks, middleware, MCP, structured outputcron-jobsby vercelVercel Cron Jobs configuration and best practices. Use when adding, editing, or debugging scheduled tasks in vercel.json.frontend-designby vercelCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts,…vercel-react-best-practicesby vercelReact and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js…
---
Source: https://github.com/vercel-labs/emulate/tree/HEAD/skills/apple
Author: vercel
Discovered via: mcpservers.org
SKILL.md source
---
name: apple
description: Emulated Sign in with Apple / Apple OIDC for local development and testing. Use when the user needs to test Apple sign-in locally, emulate Apple OIDC…
---
# apple
Emulated Sign in with Apple / Apple OIDC for local development and testing. Use when the user needs to test Apple sign-in locally, emulate Apple OIDC…
# appleby vercel
Emulated Sign in with Apple / Apple OIDC for local development and testing. Use when the user needs to test Apple sign-in locally, emulate Apple OIDC…
`npx skills add https://github.com/vercel-labs/emulate --skill apple`Download ZIPGitHub
## Apple Sign In Emulator
Sign in with Apple emulation with authorization code flow, PKCE support, RS256 ID tokens, and OIDC discovery.
## Start
```
`# Apple only
npx emulate --service apple
# Default port (when run alone)
# http://localhost:4000
`
```
Or programmatically:
```
`import { createEmulator } from 'emulate'
const apple = await createEmulator({ service: 'apple', port: 4004 })
// apple.url === 'http://localhost:4004'
`
```
## Pointing Your App at the Emulator
### Environment Variable
```
`APPLE_EMULATOR_URL=http://localhost:4004
`
```
### OAuth URL Mapping
Real Apple URLEmulator URL`https://appleid.apple.com/.well-known/openid-configuration``$APPLE_EMULATOR_URL/.well-known/openid-configuration``https://appleid.apple.com/auth/authorize``$APPLE_EMULATOR_URL/auth/authorize``https://appleid.apple.com/auth/token``$APPLE_EMULATOR_URL/auth/token``https://appleid.apple.com/auth/keys``$APPLE_EMULATOR_URL/auth/keys``https://appleid.apple.com/auth/revoke``$APPLE_EMULATOR_URL/auth/revoke`
### Auth.js / NextAuth.js
```
`import Apple from '@auth/core/providers/apple'
Apple({
clientId: process.env.APPLE_CLIENT_ID,
clientSecret: process.env.APPLE_CLIENT_SECRET,
authorization: {
url: `${process.env.APPLE_EMULATOR_URL}/auth/authorize`,
params: { scope: 'openid email name', response_mode: 'form_post' },
},
token: {
url: `${process.env.APPLE_EMULATOR_URL}/auth/token`,
},
jwks_endpoint: `${process.env.APPLE_EMULATOR_URL}/auth/keys`,
})
`
```
### Passport.js
```
`import { Strategy as AppleStrategy } from 'passport-apple'
const APPLE_URL = process.env.APPLE_EMULATOR_URL ?? 'https://appleid.apple.com'
new AppleStrategy({
clientID: process.env.APPLE_CLIENT_ID,
teamID: process.env.APPLE_TEAM_ID,
keyID: process.env.APPLE_KEY_ID,
callbackURL: 'http://localhost:3000/api/auth/callback/apple',
authorizationURL: `${APPLE_URL}/auth/authorize`,
tokenURL: `${APPLE_URL}/auth/token`,
}, verifyCallback)
`
```
## Seed Config
```
`apple:
users:
- email: [email protected]
name: Test User
given_name: Test
family_name: User
- email: [email protected]
name: Private User
is_private_email: true
oauth_clients:
- client_id: com.example.app
team_id: TEAM001
name: My Apple App
redirect_uris:
- http://localhost:3000/api/auth/callback/apple
`
```
When no OAuth clients are configured, the emulator accepts any `client_id`. With clients configured, strict validation is enforced for `client_id` and `redirect_uri`.
Users with `is_private_email: true` get a generated `@privaterelay.appleid.com` email in the `id_token` instead of their real email.
## API Endpoints
### OIDC Discovery
```
`curl http://localhost:4004/.well-known/openid-configuration
`
```
Returns the standard OIDC discovery document with all endpoints pointing to the emulator:
```
`{
"issuer": "http://localhost:4004",
"authorization_endpoint": "http://localhost:4004/auth/authorize",
"token_endpoint": "http://localhost:4004/auth/token",
"jwks_uri": "http://localhost:4004/auth/keys",
"revocation_endpoint": "http://localhost:4004/auth/revoke",
"response_types_supported": ["code"],
"subject_types_supported": ["pairwise"],
"id_token_signing_alg_values_supported": ["RS256"],
"scopes_supported": ["openid", "email", "name"],
"token_endpoint_auth_methods_supported": ["client_secret_post"],
"response_modes_supported": ["query", "fragment", "form_post"]
}
`
```
### JWKS
```
`curl http://localhost:4004/auth/keys
`
```
Returns an RSA public key (`kid`: `emulate-apple-1`) for verifying `id_token` signatures.
### Authorization
```
`# Browser flow: redirects to a user picker page
curl -v "http://localhost:4004/auth/authorize?\
client_id=com.example.app&\
redirect_uri=http://localhost:3000/api/auth/callback/apple&\
scope=openid+email+name&\
response_type=code&\
state=random-state&\
nonce=random-nonce&\
response_mode=form_post"
`
```
Query parameters:
ParamDescription`client_id`OAuth client ID (Apple Services ID)`redirect_uri`Callback URL`scope`Space-separated scopes (`openid email name`)`state`Opaque state for CSRF protection`nonce`Nonce for ID token (optional)`response_mode``query` (default), `form_post`, or `fragment`
The emulator renders an HTML page where you select a seeded user. After selection, it redirects (or auto-submits a form for `form_post`) to `redirect_uri` with `code` and `state`. On the first authorization per user/client pair, a `user` JSON blob is also included (matching Apple's real behavior).
### Token Exchange
```
`curl -X POST http://localhost:4004/auth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "code=<authorization_code>&\
client_id=com.example.app&\
client_secret=<client_secret>&\
grant_type=authorization_code"
`
```
Returns:
```
`{
"access_token": "apple_...",
"refresh_token": "r_apple_...",
"id_token": "<jwt>",
"token_type": "Bearer",
"expires_in": 3600
}
`
```
The `id_token` is an RS256 JWT containing `sub`, `email`, `email_verified` (string), `is_private_email` (string), `real_user_status`, `auth_time`, and optional `nonce`.
### Refresh Token
```
`curl -X POST http://localhost:4004/auth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "refresh_token=r_apple_...&\
client_id=com.example.app&\
grant_type=refresh_token"
`
```
Returns a new `access_token` and `id_token`. No new `refresh_token` is issued on refresh (matching Apple's behavior).
### Token Revocation
```
`curl -X POST http://localhost:4004/auth/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=apple_..."
`
```
Returns `200 OK`. The token is removed from the emulator's token map.
## Common Patterns
### Full Authorization Code Flow
```
`APPLE_URL="http://localhost:4004"
CLIENT_ID="com.example.app"
REDIRECT_URI="http://localhost:3000/api/auth/callback/apple"
# 1. Open in browser (user picks a seeded account)
# $APPLE_URL/auth/authorize?client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI&scope=openid+email+name&response_type=code&state=abc&response_mode=form_post
# 2. After user selection, emulator posts to:
# $REDIRECT_URI with code=<code>&state=abc (and user JSON on first auth)
# 3. Exchange code for tokens
curl -X POST $APPLE_URL/auth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "code=<code>&client_id=$CLIENT_ID&grant_type=authorization_code"
# 4. Decode the id_token JWT to get user info
`
```
### Private Relay Email
When a user has `is_private_email: true` in the seed config, the `id_token` will contain a generated `@privaterelay.appleid.com` email instead of the user's real email. This matches Apple's Hide My Email behavior.
## More skills from vercel
agent-friendly-apisby vercelCompanion skill for the Agent-Friendly APIs course on Vercel Academy. Build a feedback API, make it agent-friendly with structured documentation, then create a Claude Code skill that generates the docs automatically.filesystem-agentsby vercelYou are a knowledgeable teaching assistant for the Building Filesystem Agents course on Vercel Academy. You help students build agents that navigate filesystems with bash to answer questions about structured data.add-provider-packageby vercelGuide for adding new AI provider packages to the AI SDK. Use when creating a new @ai-sdk/<provider> package to integrate an AI service into the SDK.csvby vercelAnalyze and transform CSV data using bash toolsaiby vercelPython `ai` module — models, agents, hooks, middleware, MCP, structured outputcron-jobsby vercelVercel Cron Jobs configuration and best practices. Use when adding, editing, or debugging scheduled tasks in vercel.json.frontend-designby vercelCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts,…vercel-react-best-practicesby vercelReact and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js…
---
**Source**: https://github.com/vercel-labs/emulate/tree/HEAD/skills/apple
**Author**: vercel
**Discovered via**: mcpservers.org
Related skills 6
caveman
Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra, wenyan-lite, wenyan-full, wenyan-ultra. Use when user says "caveman mode", "talk like caveman", "use caveman", "less tokens", "be brief", or invokes /caveman. Also auto-triggers when token efficiency is requested.
secure-linux-web-hosting
Use when setting up, hardening, or reviewing a cloud server for self-hosting, including DNS, SSH, firewalls, Nginx, static-site hosting, reverse-proxying an app, HTTPS with Let's Encrypt or ACME clients, safe HTTP-to-HTTPS redirects, or optional post-launch network tuning such as BBR.
readme-i18n
Use when the user wants to translate a repository README, make a repo multilingual, localize docs, add a language switcher, internationalize the README, or update localized README variants in a GitHub-style repository.
lark-shared
Use when first setting up lark-cli, running auth login, switching user/bot identity (--as), handling permission denied or scope errors, needing to update lark-cli, or seeing _notice in JSON output.
improve-codebase-architecture
Find deepening opportunities in a codebase, informed by the domain language in CONTEXT.md and the decisions in docs/adr/. Use when the user wants to improve architecture, find refactoring opportunities, consolidate tightly-coupled modules, or make a codebase more testable and AI-navigable.
paper-context-resolver
Optional RigorPilot helper for README-first deep learning repo reproduction. Use only when the README and repository files leave a narrow reproduction-critical gap and the task is to resolve a specific paper detail such as dataset split, preprocessing, evaluation protocol, checkpoint mapping, or runtime assumption from primary paper sources while recording conflicts. Do not use for general paper summary, repo scanning, environment setup, command execution, title-only paper lookup, or replacin...