Supakeys

Database Setup

The passkey authentication system requires several database tables to store credentials, challenges, and audit logs.

Tables Created

The CLI creates the following tables in your Supabase database:

passkey_credentials

Stores the WebAuthn credentials for each user:

ColumnTypeDescription
idtextPrimary key (credential ID)
user_iduuidReference to auth.users
public_keybyteaPublic key bytes
webauthn_user_idtextWebAuthn user handle
counterbigintSignature counter
device_typetext'singleDevice' or 'multiDevice'
backed_upbooleanIf synced to cloud
transportstext[]Available transports
aaguidtextAuthenticator AAGUID
authenticator_nametextUser-defined name
created_attimestamptzCreation timestamp
last_used_attimestamptzLast authentication

passkey_challenges

Stores pending authentication challenges:

ColumnTypeDescription
iduuidPrimary key
challengetextChallenge string
user_iduuidAssociated user (nullable)
typetext'registration' or 'authentication'
expires_attimestamptzExpiration time (5 min TTL)
created_attimestamptzCreation timestamp

passkey_audit_log

Records all authentication events:

ColumnTypeDescription
iduuidPrimary key
user_iduuidAssociated user
event_typetextEvent name
ip_addressinetClient IP
user_agenttextBrowser user agent
metadatajsonbAdditional data
created_attimestamptzEvent timestamp

passkey_rate_limits

Tracks rate limiting by IP and email:

ColumnTypeDescription
iduuidPrimary key
identifiertextRate limit key
attempt_countintegerRequest count
window_starttimestamptzWindow start time

Row Level Security

All tables have RLS enabled with appropriate policies:

  • passkey_credentials: Users can only see their own credentials
  • passkey_challenges: Only accessible by the edge function (service role)
  • passkey_audit_log: Users can only see their own audit entries
  • passkey_rate_limits: Only accessible by the edge function (service role)

Manual Setup

If you prefer to run the migration manually instead of using the CLI:

create table passkey_credentials (
  id text primary key,
  user_id uuid references auth.users(id) on delete cascade not null,
  public_key bytea not null,
  webauthn_user_id text not null,
  counter bigint not null default 0,
  device_type text not null,
  backed_up boolean not null default false,
  transports text[] not null default '{}',
  aaguid text,
  authenticator_name text,
  created_at timestamptz not null default now(),
  last_used_at timestamptz
);

alter table passkey_credentials enable row level security;

create policy "Users can view own credentials"
  on passkey_credentials for select
  using (auth.uid() = user_id);

See the full migration in your supabase/migrations folder after running init.

On this page