Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Staging instance of the relay server is live at https://relay-server-staging.her
| --------- | -------------------------------------------- | -------------------------------------------------------------- |
| POST | /auth/google | Mint PKP for authorized Google account |
| POST | /auth/discord | Mint PKP for authorized Discord account |
| POST | /auth/apple | Mint PKP for authorized Apple account |
| POST | /auth/wallet | Mint PKP for verified Eth wallet account |
| GET | /auth/webauthn/generate-registration-options | Register (i.e., create an account) via supported authenticator |
| POST | /auth/webauthn/verify-registration | Verify the authenticator's response |
Expand Down
3 changes: 3 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
otpVerifyToFetchPKPsHandler,
otpVerifyToMintHandler,
} from "./routes/auth/otp";
import { appleOAuthVerifyToMintHandler } from "./routes/auth/apple";

const app = express();

Expand Down Expand Up @@ -222,12 +223,14 @@ app.get("/auth/status/:requestId", getAuthStatusHandler);

app.post("/auth/google", googleOAuthVerifyToMintHandler);
app.post("/auth/discord", discordOAuthVerifyToMintHandler);
app.post("/auth/apple", appleOAuthVerifyToMintHandler);
app.post("/auth/wallet", walletVerifyToMintHandler);
app.post("/auth/otp", otpVerifyToMintHandler);
app.post("/auth/stytch-otp", stytchOtpVerifyToMintHandler);

app.post("/auth/google/userinfo", googleOAuthVerifyToFetchPKPsHandler);
app.post("/auth/discord/userinfo", discordOAuthVerifyToFetchPKPsHandler);
// app.post("/auth/apple/userinfo", appleOAuthVerifyToFetchPKPsHandler);
app.post("/auth/wallet/userinfo", walletVerifyToFetchPKPsHandler);
app.post("/auth/otp/userinfo", otpVerifyToFetchPKPsHandler);
app.post("/auth/stytch-otp/userinfo", stytchOtpVerifyToFetchPKPsHandler);
Expand Down
4 changes: 4 additions & 0 deletions models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import type { RegistrationCredentialJSON } from "@simplewebauthn/typescript-type
export interface GoogleOAuthVerifyRegistrationRequest {
idToken: string;
}
export interface AppleOAuthVerifyRegistrationRequest {
accessToken: string;
}

export interface DiscordOAuthVerifyRegistrationRequest {
accessToken: string;
Expand Down Expand Up @@ -146,6 +149,7 @@ export enum AuthMethodType {
Google,
GoogleJwt,
OTP,
APPLE_JWT = 8,
StytchOtp = 9,
}

Expand Down
79 changes: 79 additions & 0 deletions routes/auth/apple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Request } from "express";
import { Response } from "express-serve-static-core";
import {
AppleOAuthVerifyRegistrationRequest,
AuthMethodType,
AuthMethodVerifyRegistrationResponse,
} from "../../models";
import { ParsedQs } from "qs";
import { utils } from "elliptic";
import { mintPKP } from "../../lit";
import { toUtf8Bytes } from "ethers/lib/utils";

type AppleTokenPayload = any;

// Apple ID Token verification
async function verifyAppleIDToken(idToken: string): Promise<AppleTokenPayload> {
// TODO: Implement Apple ID token verification here
// For example, you might use Apple's public keys to verify the JWT signature
// Then decode the payload and validate the audience and other claims
// Return the payload if it's valid
}

// Mint PKP for verified Apple account
export async function appleOAuthVerifyToMintHandler(
req: Request<
{},
AuthMethodVerifyRegistrationResponse,
AppleOAuthVerifyRegistrationRequest,
ParsedQs,
Record<string, any>
>,
res: Response<
AuthMethodVerifyRegistrationResponse,
Record<string, any>,
number
>,
) {
console.log(req.body);

// get accessToken from body
const { accessToken } = req.body;

// verify Apple ID token
let tokenPayload: AppleTokenPayload | null = null;
try {
tokenPayload = await verifyAppleIDToken(accessToken);
console.info("Successfully verified Apple account", {
userId: tokenPayload.sub,
});
} catch (err) {
console.error("Unable to verify Apple account", { err });
return res.status(400).json({
error: "Unable to verify Apple account",
});
}

// mint PKP for user
try {
const authMethodId = utils.keccak256(
toUtf8Bytes(`${tokenPayload.sub}:${tokenPayload.aud}`),
);
const mintTx = await mintPKP({
authMethodType: AuthMethodType.APPLE_JWT,
authMethodId,
authMethodPubkey: "0x",
});
console.info("Minting PKP with Apple auth", {
requestId: mintTx.hash,
});
return res.status(200).json({
requestId: mintTx.hash,
});
} catch (err) {
console.error("Unable to mint PKP for given Apple account", { err });
return res.status(500).json({
error: "Unable to mint PKP for given Apple account",
});
}
}