diff --git a/ddl/migrations/0190_oauth_pkce.sql b/ddl/migrations/0190_oauth_pkce.sql new file mode 100644 index 00000000..fd93abfd --- /dev/null +++ b/ddl/migrations/0190_oauth_pkce.sql @@ -0,0 +1,49 @@ +BEGIN; + +-- oauth_authorization_codes: short-lived (10 min), one-time-use authorization codes for PKCE flow +CREATE TABLE IF NOT EXISTS oauth_authorization_codes ( + code VARCHAR(255) NOT NULL PRIMARY KEY, + client_id VARCHAR(255) NOT NULL, + user_id INTEGER NOT NULL, + redirect_uri TEXT NOT NULL, + code_challenge VARCHAR(255) NOT NULL, + code_challenge_method VARCHAR(10) NOT NULL DEFAULT 'S256', + scope VARCHAR(50) NOT NULL, + expires_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() + INTERVAL '10 minutes'), + used BOOLEAN NOT NULL DEFAULT false +); + +CREATE INDEX IF NOT EXISTS idx_oauth_authorization_codes_client_id ON oauth_authorization_codes(client_id); +CREATE INDEX IF NOT EXISTS idx_oauth_authorization_codes_expires_used ON oauth_authorization_codes(expires_at, used); + +-- oauth_tokens: opaque access and refresh tokens for PKCE flow +CREATE TABLE IF NOT EXISTS oauth_tokens ( + token VARCHAR(255) NOT NULL PRIMARY KEY, + token_type VARCHAR(10) NOT NULL, + client_id VARCHAR(255) NOT NULL, + user_id INTEGER NOT NULL, + scope VARCHAR(50) NOT NULL, + expires_at TIMESTAMPTZ NOT NULL, + is_revoked BOOLEAN NOT NULL DEFAULT false, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + refresh_token_id VARCHAR(255), + family_id VARCHAR(255) NOT NULL +); + +CREATE INDEX IF NOT EXISTS idx_oauth_tokens_client_id ON oauth_tokens(client_id); +CREATE INDEX IF NOT EXISTS idx_oauth_tokens_user_id ON oauth_tokens(user_id); +CREATE INDEX IF NOT EXISTS idx_oauth_tokens_family_id ON oauth_tokens(family_id); +CREATE INDEX IF NOT EXISTS idx_oauth_tokens_refresh_token_id ON oauth_tokens(refresh_token_id); +CREATE INDEX IF NOT EXISTS idx_oauth_tokens_lookup ON oauth_tokens(token, token_type, is_revoked, expires_at); + +-- oauth_redirect_uris: pre-registered redirect URIs per app +CREATE TABLE IF NOT EXISTS oauth_redirect_uris ( + id SERIAL PRIMARY KEY, + client_id VARCHAR(255) NOT NULL, + redirect_uri TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX IF NOT EXISTS idx_oauth_redirect_uris_client_id ON oauth_redirect_uris(client_id); + +COMMIT; diff --git a/sql/01_schema.sql b/sql/01_schema.sql index 8d259a98..930d0ba6 100644 --- a/sql/01_schema.sql +++ b/sql/01_schema.sql @@ -7167,6 +7167,73 @@ CREATE TABLE public.notification_seen ( ); +-- +-- Name: oauth_authorization_codes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.oauth_authorization_codes ( + code character varying(255) NOT NULL, + client_id character varying(255) NOT NULL, + user_id integer NOT NULL, + redirect_uri text NOT NULL, + code_challenge character varying(255) NOT NULL, + code_challenge_method character varying(10) DEFAULT 'S256'::character varying NOT NULL, + scope character varying(50) NOT NULL, + expires_at timestamp with time zone DEFAULT (now() + '00:10:00'::interval) NOT NULL, + used boolean DEFAULT false NOT NULL +); + + +-- +-- Name: oauth_redirect_uris; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.oauth_redirect_uris ( + id integer NOT NULL, + client_id character varying(255) NOT NULL, + redirect_uri text NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: oauth_redirect_uris_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.oauth_redirect_uris_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: oauth_redirect_uris_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.oauth_redirect_uris_id_seq OWNED BY public.oauth_redirect_uris.id; + + +-- +-- Name: oauth_tokens; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.oauth_tokens ( + token character varying(255) NOT NULL, + token_type character varying(10) NOT NULL, + client_id character varying(255) NOT NULL, + user_id integer NOT NULL, + scope character varying(50) NOT NULL, + expires_at timestamp with time zone NOT NULL, + is_revoked boolean DEFAULT false NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + refresh_token_id character varying(255), + family_id character varying(255) NOT NULL +); + + -- -- Name: payment_router_txs; Type: TABLE; Schema: public; Owner: - -- @@ -8188,7 +8255,8 @@ CREATE TABLE public.sol_purchases ( is_valid boolean, city character varying, region character varying, - country character varying + country character varying, + block_timestamp timestamp with time zone ); @@ -9304,6 +9372,13 @@ ALTER TABLE ONLY public.eth_blocks ALTER COLUMN last_scanned_block SET DEFAULT n ALTER TABLE ONLY public.notification ALTER COLUMN id SET DEFAULT nextval('public.notification_id_seq'::regclass); +-- +-- Name: oauth_redirect_uris id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.oauth_redirect_uris ALTER COLUMN id SET DEFAULT nextval('public.oauth_redirect_uris_id_seq'::regclass); + + -- -- Name: plays id; Type: DEFAULT; Schema: public; Owner: - -- @@ -9904,6 +9979,30 @@ ALTER TABLE ONLY public.notification_seen ADD CONSTRAINT notification_seen_pkey PRIMARY KEY (user_id, seen_at); +-- +-- Name: oauth_authorization_codes oauth_authorization_codes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.oauth_authorization_codes + ADD CONSTRAINT oauth_authorization_codes_pkey PRIMARY KEY (code); + + +-- +-- Name: oauth_redirect_uris oauth_redirect_uris_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.oauth_redirect_uris + ADD CONSTRAINT oauth_redirect_uris_pkey PRIMARY KEY (id); + + +-- +-- Name: oauth_tokens oauth_tokens_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.oauth_tokens + ADD CONSTRAINT oauth_tokens_pkey PRIMARY KEY (token); + + -- -- Name: core_indexed_blocks pk_chain_id_height; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -10998,6 +11097,62 @@ CREATE INDEX idx_genre_related_artists ON public.aggregate_user USING btree (dom CREATE INDEX idx_lower_wallet ON public.users USING btree (lower((wallet)::text)); +-- +-- Name: idx_oauth_authorization_codes_client_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_oauth_authorization_codes_client_id ON public.oauth_authorization_codes USING btree (client_id); + + +-- +-- Name: idx_oauth_authorization_codes_expires_used; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_oauth_authorization_codes_expires_used ON public.oauth_authorization_codes USING btree (expires_at, used); + + +-- +-- Name: idx_oauth_redirect_uris_client_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_oauth_redirect_uris_client_id ON public.oauth_redirect_uris USING btree (client_id); + + +-- +-- Name: idx_oauth_tokens_client_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_oauth_tokens_client_id ON public.oauth_tokens USING btree (client_id); + + +-- +-- Name: idx_oauth_tokens_family_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_oauth_tokens_family_id ON public.oauth_tokens USING btree (family_id); + + +-- +-- Name: idx_oauth_tokens_lookup; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_oauth_tokens_lookup ON public.oauth_tokens USING btree (token, token_type, is_revoked, expires_at); + + +-- +-- Name: idx_oauth_tokens_refresh_token_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_oauth_tokens_refresh_token_id ON public.oauth_tokens USING btree (refresh_token_id); + + +-- +-- Name: idx_oauth_tokens_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX idx_oauth_tokens_user_id ON public.oauth_tokens USING btree (user_id); + + -- -- Name: idx_payment_router_txs_slot; Type: INDEX; Schema: public; Owner: - --