skills/modern-auth-2026/SKILL.md
Modern authentication implementation for 2026 - passkeys (WebAuthn), OAuth (Google, Apple), magic links, and cross-device sync. Use for passwordless-first authentication, social login setup, Supabase Auth, Next.js auth flows, and multi-factor authentication. Activate on "passkeys", "WebAuthn", "Google Sign-In", "Apple Sign-In", "magic link", "passwordless", "authentication", "login", "OAuth", "social login". NOT for session management without auth (use standard JWT docs), authorization/RBAC (use security-auditor), or API key management (use api-architect).
npx skillsauth add curiositech/windags-skills modern-auth-2026Install this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Master passwordless-first authentication with passkeys, OAuth, magic links, and cross-device sync for modern web and mobile applications.
User Auth Request
├── First-time user?
│ ├── YES → Single device?
│ │ ├── YES → Use platform passkey
│ │ └── NO → Use platform passkey + prompt backup registration
│ └── NO → Check existing credentials
│ ├── Has passkeys → Use passkey authentication
│ ├── Has OAuth only → Offer OAuth + passkey upgrade
│ └── Has legacy password → Force migration to passkey
│
├── Device capability check?
│ ├── Platform authenticator available (Face ID/Touch ID/Windows Hello)
│ │ → Prioritize platform passkey
│ ├── Only browser WebAuthn support
│ │ → Use roaming authenticator (YubiKey) OR fallback to OAuth
│ └── No WebAuthn support
│ → Use OAuth (Google/Apple) OR magic link
│
├── Cross-device scenario?
│ ├── User on desktop, has mobile with passkey
│ │ → Show QR code for hybrid transport
│ ├── User lost device access
│ │ → Email recovery → backup passkey registration
│ └── Corporate/shared device
│ → Magic link (no credential storage)
│
└── Compliance requirements?
├── App Store submission with third-party login
│ → MUST include Apple Sign-In
├── GDPR/CCPA region
│ → Default to privacy-first (Apple, passkeys)
└── Enterprise SSO
→ OAuth with organizational domain validation
| User Context | Primary Method | Fallback | Recovery Setup | |-------------|----------------|----------|----------------| | Single iPhone user | Face ID passkey | Apple Sign-In | Email + backup device prompt | | Multi-device Apple user | Platform passkey | iCloud sync | Secondary device registration | | Android user | Fingerprint passkey | Google Sign-In | Google Password Manager sync | | Desktop-only user | Windows Hello OR YubiKey | Magic link | Email + mobile app download | | Privacy-conscious | Platform passkey | Apple Sign-In | Offline backup codes | | Corporate user | SSO OAuth | Hardware key | Admin recovery contact |
Symptom: User gets "Invalid signature" error despite correct biometric Detection Rule: If authentication fails with counter mismatch error in logs Root Cause: Multiple devices using same synced passkey without counter sync Fix: Reset counter to max value from all devices OR implement counter-less verification
// WRONG: Strict counter validation
if (storedCounter >= newCounter) reject();
// RIGHT: Allow counter drift for synced credentials
if (credential.backed_up && Math.abs(storedCounter - newCounter) > 100) {
// Reset to new counter, log security event
}
Symptom: User clicks OAuth button, gets redirected back to login repeatedly Detection Rule: If redirect_uri in OAuth error OR user reports "spinning" login Root Cause: Redirect URI mismatch between provider console and Supabase config Fix: Verify exact URL match including trailing slashes and http/https
// Provider Console: https://yourproject.supabase.co/auth/v1/callback
// Supabase Config: Must match exactly - no trailing slash variations
Symptom: User completes biometric prompt but passkey doesn't save Detection Rule: If registration returns success but no database entry created Root Cause: Challenge verification passed but database insert failed (RLS policy) Fix: Check RLS policies allow insert, verify user context in registration flow
-- WRONG: Restrictive policy
CREATE POLICY "Only verified users" ON passkey_credentials
FOR INSERT WITH CHECK (auth.email_confirmed_at IS NOT NULL);
-- RIGHT: Allow during registration
CREATE POLICY "Users can register" ON passkey_credentials
FOR INSERT WITH CHECK (auth.uid() = user_id);
Symptom: Apple Sign-In suddenly returns 400 Invalid Client error Detection Rule: If Apple OAuth fails with client authentication error after working previously Root Cause: Apple .p8 private key expired (6-month limit) Fix: Set calendar reminders, implement monitoring, rotate keys before expiry
// Add monitoring
const keyCreatedAt = new Date('2024-06-01');
const warningPeriod = 5 * 24 * 60 * 60 * 1000; // 5 days
if (Date.now() - keyCreatedAt.getTime() > (6 * 30 * 24 * 60 * 60 * 1000) - warningPeriod) {
console.warn('Apple key expiring soon!');
}
Symptom: User reports "Check your email" but no emails arrive Detection Rule: If sign-up/magic link triggered but email delivery rate drops below 95% Root Cause: SMTP not configured OR emails marked as spam OR wrong email templates Fix: Verify SMTP settings, check deliverability, update DNS records (SPF/DKIM)
// Add delivery confirmation
await supabase.auth.signInWithOtp({
email,
options: {
shouldCreateUser: false, // Prevent silent failures
}
});
// Check Supabase logs for email delivery status
Scenario: Implementing passkey auth for a Next.js SaaS app with Supabase
Step 1 - Decision Point Navigation:
Step 2 - Database Setup (What novices miss: RLS policies):
-- Experts check: Proper indexing for performance
CREATE TABLE passkey_credentials (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE,
credential_id text UNIQUE NOT NULL, -- Base64URL encoded
public_key bytea NOT NULL,
counter bigint DEFAULT 0, -- BIGINT not INT (prevents overflow)
transports text[],
backed_up boolean DEFAULT false,
created_at timestamptz DEFAULT now()
);
-- Novice mistake: Forgetting compound index
CREATE INDEX idx_passkey_user_cred ON passkey_credentials(user_id, credential_id);
Step 3 - Registration API (What experts catch: Challenge storage):
// Novice approach: Store challenge in memory (fails with multiple servers)
let challenges = new Map();
// Expert approach: Persistent challenge storage with expiry
await supabase.from('auth_challenges').upsert({
user_id: user.id,
challenge: options.challenge,
type: 'passkey_register',
expires_at: new Date(Date.now() + 5 * 60 * 1000),
});
Step 4 - Frontend Implementation (What novices miss: Error handling):
// Novice: Generic error handling
try {
const credential = await startRegistration(options);
} catch (error) {
setError('Registration failed');
}
// Expert: Specific error cases
try {
const credential = await startRegistration(options);
} catch (error) {
if (error.name === 'NotAllowedError') {
setError('Registration cancelled or timeout');
} else if (error.name === 'InvalidStateError') {
setError('Device already registered for this account');
} else if (error.name === 'NotSupportedError') {
// Fallback to OAuth
redirectToAppleSignIn();
} else {
setError(`Registration failed: ${error.message}`);
}
}
Step 5 - Testing (Expert checks novices skip):
Do NOT use this skill for:
security-auditor skill for RBAC, permissions, access controlapi-architect skill for service-to-service authenticationsupabase-admin skill for database-level security rulescompliance-auditor skill for GDPR/HIPAA/SOC2 requirementsperformance-optimizer skill for auth-related bottlenecksDelegate to other skills when user asks about:
supabase-adminsecurity-auditorapi-architectcompliance-auditordata-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.