From b3bc532ae072e48d74219479628b3f13ba5204ff Mon Sep 17 00:00:00 2001 From: Ansonhkg Date: Thu, 24 Aug 2023 12:40:57 +0100 Subject: [PATCH] feat: signInWithApple --- README.md | 1 + index.ts | 3 ++ models/index.ts | 4 +++ routes/auth/apple.ts | 79 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 routes/auth/apple.ts diff --git a/README.md b/README.md index 12ef303..294998c 100644 --- a/README.md +++ b/README.md @@ -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 | diff --git a/index.ts b/index.ts index e549ea4..5fa0f48 100644 --- a/index.ts +++ b/index.ts @@ -64,6 +64,7 @@ import { otpVerifyToFetchPKPsHandler, otpVerifyToMintHandler, } from "./routes/auth/otp"; +import { appleOAuthVerifyToMintHandler } from "./routes/auth/apple"; const app = express(); @@ -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); diff --git a/models/index.ts b/models/index.ts index 531e843..17a6ab8 100644 --- a/models/index.ts +++ b/models/index.ts @@ -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; @@ -146,6 +149,7 @@ export enum AuthMethodType { Google, GoogleJwt, OTP, + APPLE_JWT = 8, StytchOtp = 9, } diff --git a/routes/auth/apple.ts b/routes/auth/apple.ts new file mode 100644 index 0000000..fc53212 --- /dev/null +++ b/routes/auth/apple.ts @@ -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 { + // 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 + >, + res: Response< + AuthMethodVerifyRegistrationResponse, + Record, + 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", + }); + } +}