From che-apple-dev
Set up Apple Sign In for Web on Apple Developer Portal via Safari + AppleScript. Use when user says "Apple Sign In", "Apple OAuth", "設定 Apple 登入", "建立 Services ID", "generate .p8 key", "Apple client secret", "developer.apple.com 設定", or needs to configure Apple OAuth for Supabase/web apps. Also handles JWT client secret generation from .p8 keys.
npx claudepluginhub psychquant/psychquant-claude-plugins --plugin che-apple-devThis skill is limited to using the following tools:
Automate Apple Sign In for Web configuration via Safari + AppleScript.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Automate Apple Sign In for Web configuration via Safari + AppleScript. Covers the full chain: App ID → Services ID → Key (.p8) → JWT client secret.
PyJWT for JWT generation (pip install pyjwt)Apple Sign In for Web requires four things registered on Apple Developer Portal:
client_id for web OAuthtell application "Safari" to activate
tell application "Safari" to open location "https://developer.apple.com/account"
Wait for user to complete login + 2FA, then verify:
tell application "Safari" to do JavaScript "document.title" in front document
Should contain "Account - Apple Developer" or similar.
# From Xcode provisioning
defaults read com.apple.dt.Xcode IDEProvisioningTeams 2>/dev/null
# Or from signing identity
security find-identity -v -p codesigning 2>/dev/null
Note the Team ID (10-character alphanumeric like 6W377FS7BS).
Navigate to register App ID:
tell application "Safari" to set URL of front document to "https://developer.apple.com/account/resources/identifiers/add/bundleId"
Select "App IDs" → "App" type → Continue. Then fill the form:
// Fill Description and Bundle ID using native setter (framework-safe)
(function() {
var setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value").set;
var desc = document.getElementById("description");
setter.call(desc, "YOUR_APP_NAME");
desc.dispatchEvent(new InputEvent("input", {bubbles: true, inputType: "insertText"}));
desc.dispatchEvent(new Event("change", {bubbles: true}));
var ident = document.getElementById("identifier");
setter.call(ident, "com.yourdomain.appname");
ident.dispatchEvent(new InputEvent("input", {bubbles: true, inputType: "insertText"}));
ident.dispatchEvent(new Event("change", {bubbles: true}));
})();
Enable Sign In with Apple in the capabilities list:
(function() {
var checkboxes = document.querySelectorAll("input[type=checkbox]");
for (var i = 0; i < checkboxes.length; i++) {
var row = checkboxes[i].closest("tr, li, div");
if (row && row.textContent.includes("Sign In with Apple")) {
checkboxes[i].click();
return "enabled";
}
}
return "not found";
})();
Click Continue → Register.
Navigate:
tell application "Safari" to set URL of front document to "https://developer.apple.com/account/resources/identifiers/add/serviceId"
On the type selection page, select "Services IDs" (radio index 1 in ul.form-radio-list):
(function() {
var lis = document.querySelectorAll("ul.form-radio-list > li");
var radio = lis[1].querySelector("input[type=radio]"); // Services IDs
radio.click();
})();
Click Continue, then fill:
client_id)Register, then click into the new Services ID to edit. Enable Sign In with Apple checkbox, click Configure.
The Configure dialog uses a React Select dropdown for Primary App ID:
// Open the dropdown
(function() {
var input = document.getElementById("react-select-2-input");
input.focus();
input.dispatchEvent(new KeyboardEvent("keydown", {bubbles: true, key: "ArrowDown", keyCode: 40}));
})();
Then select the first option:
(function() {
var option = document.getElementById("react-select-2-option-0");
if (option) { option.click(); return option.textContent.trim(); }
return "no option";
})();
Fill domain and return URL (the native setter + InputEvent pattern is required because the form uses a JS framework):
(function() {
var setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value").set;
var domain = document.getElementById("domainsInput");
setter.call(domain, "YOUR_SUPABASE_REF.supabase.co");
domain.dispatchEvent(new InputEvent("input", {bubbles: true, inputType: "insertText"}));
domain.dispatchEvent(new Event("change", {bubbles: true}));
var urls = document.getElementById("urlsInput");
setter.call(urls, "https://YOUR_SUPABASE_REF.supabase.co/auth/v1/callback");
urls.dispatchEvent(new InputEvent("input", {bubbles: true, inputType: "insertText"}));
urls.dispatchEvent(new Event("change", {bubbles: true}));
})();
Click Next → Done → Continue → Save.
Navigate:
tell application "Safari" to set URL of front document to "https://developer.apple.com/account/resources/authkeys/add"
Fill key name, enable Sign In with Apple, Configure (select App ID via React Select as above), then Continue → Register.
The download page appears once. Click Download — Safari will show a permission dialog "要允許在 developer.apple.com 上下載嗎?". Handle it with AppleScript:
tell application "System Events"
tell process "Safari"
set allElems to entire contents of window 1
repeat with elem in allElems
try
if class of elem is button and name of elem is "允許" then
click elem
exit repeat
end if
end try
end repeat
end tell
end tell
Important: Standard sheet or group traversal won't find this button — entire contents is required.
Verify download:
ls -lt ~/Downloads/AuthKey_*.p8 | head -1
Backup the key:
mkdir -p ~/.appstoreconnect/private_keys/
cp ~/Downloads/AuthKey_KEYID.p8 ~/.appstoreconnect/private_keys/
If the download page shows "Downloaded" but the file isn't there, click Done, go to Keys list, click the key name to view details — the Download button is still available from the detail page.
Apple OAuth backends (Supabase, Firebase, etc.) need a JWT client secret generated from the .p8 key. This JWT expires every 6 months — set a reminder to regenerate.
python3 -c "
import jwt, time
with open('PATH_TO_P8_FILE', 'r') as f:
private_key = f.read()
headers = {'kid': 'KEY_ID', 'alg': 'ES256'}
payload = {
'iss': 'TEAM_ID',
'iat': int(time.time()),
'exp': int(time.time()) + 86400 * 180, # 6 months
'aud': 'https://appleid.apple.com',
'sub': 'SERVICES_ID' # e.g. com.yourdomain.auth
}
print(jwt.encode(payload, private_key, algorithm='ES256', headers=headers))
"
Replace:
PATH_TO_P8_FILE — path to the downloaded .p8 keyKEY_ID — from the Keys page (10-character ID)TEAM_ID — your Apple Developer Team IDSERVICES_ID — the Services ID identifier (e.g. com.yourdomain.auth)Navigate to Supabase Dashboard → Authentication → Providers → Apple. Use Safari AppleScript to fill the fields:
The provider accordion rows have class cursor-pointer:
// Click Apple row to expand
(function() {
var rows = document.querySelectorAll("div.cursor-pointer");
for (var i = 0; i < rows.length; i++) {
if (rows[i].textContent.includes("Apple")) {
rows[i].click();
return "expanded";
}
}
})();
Enable the toggle and fill credentials:
(function() {
// Enable toggle
var toggle = document.getElementById("EXTERNAL_APPLE_ENABLED");
if (toggle) toggle.click();
var setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value").set;
// Client ID = Services ID
var clientId = document.getElementById("EXTERNAL_APPLE_CLIENT_ID");
setter.call(clientId, "SERVICES_ID");
clientId.dispatchEvent(new InputEvent("input", {bubbles: true, inputType: "insertText"}));
clientId.dispatchEvent(new Event("change", {bubbles: true}));
// Secret = JWT from Step 6
var secret = document.getElementById("EXTERNAL_APPLE_SECRET");
setter.call(secret, "JWT_CLIENT_SECRET");
secret.dispatchEvent(new InputEvent("input", {bubbles: true, inputType: "insertText"}));
secret.dispatchEvent(new Event("change", {bubbles: true}));
})();
Click Save. Verify the toast says "Successfully updated settings" and Apple shows "Enabled".
| Problem | Solution |
|---|---|
| "No App ID is available" in Services ID config | Create an App ID with Sign In with Apple enabled first (Step 3) |
| Safari download dialog blocks .p8 download | Use entire contents AppleScript pattern (Step 5) |
Unsupported provider: provider is not enabled | Apple provider not enabled in Supabase Dashboard |
JWT generation fails with No module named 'jwt' | pip install pyjwt (not pip install jwt) |
| Personal Team shown in Xcode | Need paid Apple Developer Program ($99/year) |
| React Select dropdown won't open | Focus input first, dispatch ArrowDown KeyboardEvent |
| Form values don't persist after setting | Use Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value").set + InputEvent, not direct .value = |
After setup, save this info for future reference:
Team ID: ___________
App ID: com.________
Services ID: com.________ (this is the OAuth client_id)
Key ID: ___________
Key backup: ~/.appstoreconnect/private_keys/AuthKey_KEYID.p8
JWT expires: YYYY-MM-DD (6 months from generation)