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 |