oskar.makarov
portfolio / case 002 · free · open source

Sign in with
Apple

secret auto-rotation · GitHub Action · Supabase

Sign in with Apple stops working roughly every six months. The secret expires, Apple never warns you — your users tell you instead. This Action rotates it on a schedule: set it up once and forget it.

[x] key read
[x] secret signed
[x] Supabase updated
[ ] next run — in 5 months
6
months is all Apple's secret ever lives
0
dependencies: only what Node already ships
1
file holds the logic — read it end to end

How it breaks

▍six months after launch
day 1 — Apple sign-in works
day 90 — still works
[~] day 180 — secret expired
[x] sign-in returns invalid_client
emails from Apple: none
▍and here's how you find out
User: I can't sign in with Apple. The button does nothing.
You: It worked for six months. Nothing changed, nothing shipped.
The official advice for this is to set a calendar reminder every six months.
[x] schedule
Rotates itself every 5 months

Five, not six — on purpose. Apple's ceiling is about six months, so the spare month is your margin if a run ever fails.

[x] supabase
Writes the new secret for you

Your Supabase project is updated through its own API. No dashboard visit required — not now, not in six months.

[~] any other stack
Firebase, Auth0, self-hosted

Not on Supabase? Drop two inputs and the Action simply hands you the fresh secret as an output. Pipe it wherever your stack needs it.

Full breakdown of the invalid_client error: how to confirm it is the secret, and fix it by hand →

The whole setup

One file in your repo and four secrets in your settings. Run it manually once — and never think about it again.

▍.github/workflows/rotate-apple-secret.yml
on:
  schedule:
    - cron: "0 6 1 */5 *"   # every 5 months, with margin
  workflow_dispatch: {}      # first run — manually

jobs:
  rotate:
    runs-on: ubuntu-latest
    steps:
      - uses: oskar-makarov/apple-client-secret-rotator@v1
        with:
          apple_team_id: ${{ secrets.APPLE_TEAM_ID }}
          apple_key_id: ${{ secrets.APPLE_KEY_ID }}
          apple_services_id: com.example.app.web
          apple_p8: ${{ secrets.APPLE_P8 }}
          supabase_project_ref: your-project-ref
          supabase_access_token: ${{ secrets.SUPABASE_ACCESS_TOKEN }}

Why you can trust it with your key

You hand this Action your Apple private key — so being verifiable matters more here than any promise.

01
Your key never leaves your repo

It lives in your own GitHub Secrets and is used inside your own run. It isn't sent to me, and there's nowhere to send it — this Action has no server.

02
Zero dependencies

Not a single third-party library — only what Node already ships. Nothing can update itself and swap the contents behind your back.

03
All the logic is one file

rotate.js reads end to end in a few minutes. Don't trust the description — open it and see what it does before you install.

04
MIT licensed

Take it, change it, use it commercially. Fork it and run from your own copy if that feels safer.

FAQ

Why every 5 months and not 6?

Apple's hard ceiling is around six months. Running every five leaves a month of margin: if one run fails, sign-in doesn't break.

Is my .p8 key safe?

It stays in your repository's secrets and never leaves your own workflow run. The Action has no server and no dependencies — the entire logic is one file you can read before installing.

Does this affect sign-in inside my iPhone app?

No. Native in-app sign-in doesn't use the client secret — expiry only affects sign-in through the web.

What if I'm not on Supabase?

Then the Action just hands you the fresh secret and you pass it to your own service. Works with Firebase, Auth0, self-hosted — anything.

Oskar Makarov

Author

Oskar Makarov. This isn't theory: I hit this failure myself and wrote the rotation so I'd never hit it again. You don't have to take my word for it — the code is fully open and the Marketplace listing is one click away.

Free, forever. If it helps, take a look at Pipeline.md — it's about the same kind of trap, the kind that only shows up after launch.

MIT · open source Pipeline.md →
finale

Set it
up once —
then forget it.

Get it on GitHub Marketplace → free · MIT · zero dependencies · one-time setup
© 2026 Oskar Makarov ▍apple-client-secret-rotator · MIT