92 lines
3.4 KiB
SQL
92 lines
3.4 KiB
SQL
-- 0003_device_pairing.sql
|
|
-- Additive, idempotent migration for the OAuth2 device-authorization-grant
|
|
-- style "device pairing" flow used by the Home Assistant add-on.
|
|
-- Safe to run multiple times.
|
|
|
|
-- 1. Device pairings ------------------------------------------------------
|
|
-- One row per device-pairing attempt. The opaque device_code is only ever
|
|
-- stored as a SHA-256 hash; the short, human-typed user_code is stored in the
|
|
-- clear so it can be looked up when the user approves on the web.
|
|
create table if not exists public.device_pairings (
|
|
id bigint generated always as identity primary key,
|
|
device_code_hash text not null,
|
|
user_code text not null,
|
|
user_id uuid,
|
|
subdomain citext,
|
|
status text not null default 'pending',
|
|
interval_seconds int not null default 5,
|
|
created_at timestamptz not null default now(),
|
|
expires_at timestamptz not null,
|
|
approved_at timestamptz,
|
|
claimed_at timestamptz,
|
|
last_polled_at timestamptz
|
|
);
|
|
|
|
create unique index if not exists device_pairings_device_code_hash_uidx
|
|
on public.device_pairings (device_code_hash);
|
|
|
|
create unique index if not exists device_pairings_user_code_uidx
|
|
on public.device_pairings (user_code);
|
|
|
|
create index if not exists device_pairings_expires_at_idx
|
|
on public.device_pairings (expires_at);
|
|
|
|
-- Cascade-delete pairings when the owning auth user is removed.
|
|
do $$
|
|
begin
|
|
if not exists (
|
|
select 1 from pg_constraint
|
|
where conname = 'device_pairings_user_id_fkey'
|
|
and conrelid = 'public.device_pairings'::regclass
|
|
) then
|
|
alter table public.device_pairings
|
|
add constraint device_pairings_user_id_fkey
|
|
foreign key (user_id) references auth.users (id) on delete cascade;
|
|
end if;
|
|
end $$;
|
|
|
|
-- RLS enabled with NO policies: service-role only.
|
|
alter table public.device_pairings enable row level security;
|
|
|
|
comment on table public.device_pairings is
|
|
'Device-authorization-grant pairings (device_code SHA-256 hashed). RLS enabled with no policies: service-role only.';
|
|
|
|
-- 2. Device tokens --------------------------------------------------------
|
|
-- Long-lived bearer tokens minted to a paired device. Only the SHA-256 hash
|
|
-- is stored; the plaintext is returned to the device exactly once at mint.
|
|
create table if not exists public.device_tokens (
|
|
id bigint generated always as identity primary key,
|
|
token_hash text not null,
|
|
user_id uuid not null,
|
|
label text,
|
|
created_at timestamptz not null default now(),
|
|
last_used_at timestamptz,
|
|
revoked_at timestamptz
|
|
);
|
|
|
|
create unique index if not exists device_tokens_token_hash_uidx
|
|
on public.device_tokens (token_hash);
|
|
|
|
create index if not exists device_tokens_user_id_idx
|
|
on public.device_tokens (user_id);
|
|
|
|
-- Cascade-delete device tokens when the owning auth user is removed.
|
|
do $$
|
|
begin
|
|
if not exists (
|
|
select 1 from pg_constraint
|
|
where conname = 'device_tokens_user_id_fkey'
|
|
and conrelid = 'public.device_tokens'::regclass
|
|
) then
|
|
alter table public.device_tokens
|
|
add constraint device_tokens_user_id_fkey
|
|
foreign key (user_id) references auth.users (id) on delete cascade;
|
|
end if;
|
|
end $$;
|
|
|
|
-- RLS enabled with NO policies: service-role only.
|
|
alter table public.device_tokens enable row level security;
|
|
|
|
comment on table public.device_tokens is
|
|
'Device bearer tokens (SHA-256 hashed). RLS enabled with no policies: service-role only.';
|