Apple OAuth provider
Before starting make sure you have an paid apple dev account.
OAuth integration for Apple. Refer to Apple Docs:
- Creating App ID
 - Creating Service ID
 - Enable “Sign In with Apple” Capability
 - Creating Private Key
 - Locate the keyId
 - How to locate your teamId
 - Requesting Access Token
 - How to validate tokens
 
Provider id is apple.
import { apple } from "@lucia-auth/oauth/providers";
import { auth } from "./lucia.js";
const appleAuth = apple(auth, configs);
apple()#
const apple: (
	auth: Auth,
	config: {
		clientId: string;
		redirectUri: string;
		teamId: string;
		keyId: string;
		certificate: string;
		scope?: string[];
		responseMode?: "query" | "form_post";
	}
) => AppleProvider;
Parameters#
| name | type | description | default | 
|---|---|---|---|
auth | Auth | Lucia instance | |
config.clientId | string | Apple service identifier | |
config.redirectUri | string | an authorized redirect URI | |
config.teamId | string | Apple teamId | |
config.keyId  | string | Apple private keyId | |
config.certificate | string | p8 certificate as string See how | |
config.scope | string[] | an array of scopes | [] | 
config.responseMode | "query" | "form_post" | OIDC response mode - must be "form_post" when requesting scopes | "query" | 
Returns#
| type | description | 
|---|---|
AppleProvider | Apple provider | 
Import certificate#
Example using Node.js:
import fs from "fs";
import path from "path";
const certificatePath = path.join(
	process.cwd(),
	process.env.APPLE_CERT_PATH ?? ""
);
const certificate = fs.readFileSync(certificatePath, "utf-8");
export const appleAuth = apple(auth, {
	teamId: process.env.APPLE_TEAM_ID ?? "",
	keyId: process.env.APPLE_KEY_ID ?? "",
	certificate: certificate,
	redirectUri: process.env.APPLE_REDIRECT_URI ?? "",
	clientId: process.env.APPLE_CLIENT_ID ?? ""
});
Requesting scopes#
When requesting scopes (email and name), the options.responseMode must be set to "form_post". Unlike the default "query" response mode, **Apple will send an application/x-www-form-urlencoded POST request. You can retrieve the code by parsing the search queries or the form data.
post("/login/apple/callback", async (request) => {
	const url = new URL(request.url)
	const code = url.searchParams.get("code");
	if (!isValidState(request, code)) {
		// ...
	}
	const appleUserAuth = await
	// ...
})
Apple will also include a user field only in the first response, where you can access the user’s name.
const url = new URL(request.url);
const userJSON = url.searchParams.get("user");
if (userJSON) {
	const user = JSON.parse(userJSON);
	const { firstName, lastName, email } = user;
}
Interfaces#
AppleAuth#
See OAuth2ProviderAuth.
// implements OAuth2ProviderAuth<AppleAuth<_Auth>>
interface AppleAuth<_Auth extends Auth> {
	getAuthorizationUrl: () => Promise<readonly [url: URL, state: string]>;
	validateCallback: (code: string) => Promise<AppleUserAuth<_Auth>>;
}
| type | 
|---|
AppleUserAuth | 
Generics#
| name | extends | default | 
|---|---|---|
_Auth | Auth | Auth | 
AppleTokens#
type AppleTokens = {
	accessToken: string;
	refreshToken: string | null;
	accessTokenExpiresIn: number;
	idToken: string;
};
AppleUser#
type AppleUser = {
	email?: string;
	email_verified?: boolean;
	sub: string;
};
AppleUserAuth#
Extends ProviderUserAuth.
interface AppleUserAuth<_Auth extends Auth> extends ProviderUserAuth<_Auth> {
	appleUser: AppleUser;
	appleTokens: AppleTokens;
}
| properties | type | description | 
|---|---|---|
appleUser | AppleUser | Apple user | 
appleTokens | AppleTokens | Access tokens etc | 
Generics#
| name | extends | 
|---|---|
_Auth | Auth |