Skip to content

Apple Wallet check-in passes - #166

Merged
pradhamk merged 7 commits into
mainfrom
apple-wallet-passes
Sep 27, 2026
Merged

pradhamk merged 7 commits into
mainfrom
apple-wallet-passes

Conversation

@pradhamk

Copy link
Copy Markdown
Member

What

RSVPed hackers can add their check-in QR code to Apple Wallet and pull it up from the lock screen at the door, even offline.

The pass encodes the bare user id, exactly what the dashboard QR shows, so /checkin and checkInAttendee accept it with no changes.

  • GET /wallet/pass signs a .pkpass with passkit-generator. It serves the signed-in user's pass, or the user named in a signed, expiring ?t= link, so emailed links work without logging in. /wallet is a public path in the proxy, and the route does its own auth.
  • Eligibility matches getAttendeeQrEligibility: an RSVP row and a confirmed decision. It's re-checked on every download, so a deleted RSVP can't be used with an old link.
  • Dashboard: the check-in panel has an "Add to Apple Wallet" button.
  • Email campaigns: audiences have a new wallet_pass_url column, filled in only for RSVPed rows. Links expire when the event ends (Oct 6).
  • Pass: event ticket in the sky theme with the attendee name, dates and Ann Arbor. It shows on the lock screen during the event and near North Campus, and expires Oct 6. Images are generated by scripts/generate-wallet-assets.ts.
  • Hidden until configured. Every Wallet entry point checks for APPLE_WALLET_* and WALLET_LINK_SECRET.

⚠️ Before merging: create the SSM parameters

task-definition.json now reads these from SSM. If they don't exist, new ECS tasks can't start and the deploy fails. Create them in us-east-2 as SecureString:

Parameter Value
/mhacks-secrets/APPLE_WALLET_PASS_TYPE_ID pass.org.mhacks
/mhacks-secrets/APPLE_WALLET_TEAM_ID 8679469T3A
/mhacks-secrets/APPLE_WALLET_SIGNER_CERT base64 of the pass certificate PEM
/mhacks-secrets/APPLE_WALLET_SIGNER_KEY base64 of the private key PEM
/mhacks-secrets/APPLE_WALLET_SIGNER_KEY_PASSPHRASE key passphrase (still required; use an unencrypted key and any placeholder if you prefer)
/mhacks-secrets/APPLE_WALLET_WWDR_CERT base64 of the Apple WWDR G4 PEM
/mhacks-secrets/WALLET_LINK_SECRET a new openssl rand -hex 32, not the local one

The mhacks-ecs-exec role also needs read access to them, like the existing /mhacks-secrets/* parameters.

Testing

  • Typecheck, lint, Prettier, migration check and pnpm build pass.
  • Against the local Supabase stack, using the standalone build and the real pass.org.mhacks certificate:
    • A user who is RSVPed gets 200 application/vnd.apple.pkpass. The pass is signed, and its QR payload is the user id.
    • A user who is accepted but not RSVPed gets 403.
    • A tampered or garbage token gets 403. No token and no session redirects to /login. Missing config returns 503.
  • The pass was added to Wallet on a real device and opened.

After deploy

To reach people who've already RSVPed: in /admin/email-campaigns, pick the RSVPed audience and put [Add to Apple Wallet]({{wallet_pass_url}}) in a section body. It can't go in the CTA button, because the CTA URL is validated before merge fields are filled in.

🤖 Generated with Claude Code

RSVPed hackers can add their check-in QR to Apple Wallet. The pass encodes
the bare user id, exactly what the dashboard QR shows, so the scanner and
check-in action accept it unchanged.

- GET /wallet/pass signs a .pkpass (passkit-generator) for the session user,
  or for the user in a signed, expiring ?t= link so emailed links work
  without signing in. Eligibility matches getAttendeeQrEligibility: an RSVP
  row and a confirmed decision, re-checked on every download.
- Dashboard check-in panel gains an "Add to Apple Wallet" button.
- Email campaign audiences gain a wallet_pass_url merge column for RSVPed
  rows, for a bulk send via [Add to Apple Wallet]({{wallet_pass_url}}).
- Every Wallet entry point stays hidden unless the APPLE_WALLET_* and
  WALLET_LINK_SECRET env vars are set; task-definition.json reads them
  from SSM.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread lib/email/campaigns/audience-service.ts
Comment thread lib/wallet/config.ts Outdated
Comment thread lib/supabase/proxy.ts Outdated
@HangYeung1

Copy link
Copy Markdown
Contributor

secrets have been added to aws.

pradhamk and others added 6 commits September 24, 2026 21:17
- Drop markdown links whose merged URL is blank instead of shipping the
  literal "[text]()" (test sends, non-RSVPed recipients).
- Validate Wallet PEMs at config time: reject raw/undecodable PEMs, parse
  both certificates, and open the signer key with its passphrase, so
  isWalletConfigured() hides entry points instead of 500ing downloads.
- Narrow the proxy auth exemption from /wallet to /wallet/pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The dashboard button now needs the flag as well as valid credentials,
matching GOOGLE_WALLET_PUBLISHED. /wallet/pass and emailed links still
work, so the pass can be tested before hackers see it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@pradhamk
pradhamk merged commit be26f97 into main Sep 27, 2026
1 check passed
@pradhamk
pradhamk deleted the apple-wallet-passes branch September 27, 2026 02:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants