feature: the OpenID Provider has the client credentials and device authorization grants, public clients, and service accounts as resources with roles and scopes in JWT access tokens.
Release / release (push) Successful in 17s
Release / release (push) Successful in 17s
This commit is contained in:
1 parent
3ed79c7563
commit
3c4c5a8c32
15 files changed
+1572
-240
No files matched your search
@@ -1,10 +1,12 @@
|
||||
-- The state of the router's OpenID Provider (vpnrouter_oidc) in the database of web.database.
|
||||
-- Codes, tokens and sessions are kept as the SHA-256 of their values; expired rows are purged when new ones are saved.
|
||||
-- The whole file runs as one transaction, serialized by an advisory lock, so that two processes can load it at once.
|
||||
-- Codes, tokens, device codes and sessions are kept as the SHA-256 of their values; expired rows are purged when new
|
||||
-- ones are saved. The whole file runs as one transaction, serialized by an advisory lock, so that two processes can
|
||||
-- load it at once.
|
||||
|
||||
SELECT pg_advisory_xact_lock(hashtext('vpn-router oidc schema'));
|
||||
|
||||
-- An authorization code, valid until it is exchanged for tokens or expires.
|
||||
-- An authorization code, valid until it is exchanged for tokens or expires; resource is the resource the tokens are
|
||||
-- for, null for the client itself.
|
||||
CREATE TABLE IF NOT EXISTS oidc_codes (
|
||||
hash text PRIMARY KEY,
|
||||
client_id text NOT NULL,
|
||||
@@ -16,10 +18,13 @@ CREATE TABLE IF NOT EXISTS oidc_codes (
|
||||
code_challenge_method text NOT NULL,
|
||||
auth_time timestamptz NOT NULL,
|
||||
amr text[] NOT NULL,
|
||||
expires timestamptz NOT NULL
|
||||
expires timestamptz NOT NULL,
|
||||
resource text
|
||||
);
|
||||
ALTER TABLE oidc_codes ADD COLUMN IF NOT EXISTS resource text;
|
||||
|
||||
-- An access token or a refresh token (kind) a client was issued for a user.
|
||||
-- An access token or a refresh token (kind) a client was issued for a user, or for itself as a service account with
|
||||
-- client credentials; resource as in oidc_codes.
|
||||
CREATE TABLE IF NOT EXISTS oidc_tokens (
|
||||
hash text PRIMARY KEY,
|
||||
kind text NOT NULL CHECK (kind IN ('access', 'refresh')),
|
||||
@@ -28,9 +33,30 @@ CREATE TABLE IF NOT EXISTS oidc_tokens (
|
||||
scopes text[] NOT NULL,
|
||||
auth_time timestamptz NOT NULL,
|
||||
amr text[] NOT NULL,
|
||||
expires timestamptz NOT NULL,
|
||||
resource text
|
||||
);
|
||||
ALTER TABLE oidc_tokens ADD COLUMN IF NOT EXISTS resource text;
|
||||
CREATE INDEX IF NOT EXISTS oidc_tokens_name ON oidc_tokens (name);
|
||||
|
||||
-- A device code of the device authorization grant and its user code (user_hash): pending until the user allows or
|
||||
-- denies it, then the user, when and how they signed in; poll_seconds is the least time between two polls, polled
|
||||
-- the last poll.
|
||||
CREATE TABLE IF NOT EXISTS oidc_devices (
|
||||
hash text PRIMARY KEY,
|
||||
user_hash text NOT NULL UNIQUE,
|
||||
client_id text NOT NULL,
|
||||
scopes text[] NOT NULL,
|
||||
resource text,
|
||||
state text NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'allowed', 'denied')),
|
||||
name text,
|
||||
auth_time timestamptz,
|
||||
amr text[],
|
||||
poll_seconds integer NOT NULL,
|
||||
polled timestamptz,
|
||||
expires timestamptz NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS oidc_tokens_name ON oidc_tokens (name);
|
||||
CREATE INDEX IF NOT EXISTS oidc_devices_name ON oidc_devices (name);
|
||||
|
||||
-- A user's sign-in at the OpenID Provider, which the session cookie names.
|
||||
CREATE TABLE IF NOT EXISTS oidc_sessions (
|
||||
@@ -41,22 +67,30 @@ CREATE TABLE IF NOT EXISTS oidc_sessions (
|
||||
expires timestamptz NOT NULL
|
||||
);
|
||||
|
||||
-- The functions and procedures whose parameters or results differ from those of the same name.
|
||||
DROP PROCEDURE IF EXISTS oidc_code_save(text, text, text, text, text[], text, text, text, timestamptz, text[], integer);
|
||||
DROP FUNCTION IF EXISTS oidc_code_get(text);
|
||||
DROP PROCEDURE IF EXISTS oidc_token_save(text, text, text, text, text[], timestamptz, text[], integer);
|
||||
DROP FUNCTION IF EXISTS oidc_token_get(text, text);
|
||||
|
||||
CREATE OR REPLACE PROCEDURE oidc_code_save(p_hash text, p_client_id text, p_name text, p_redirect_uri text,
|
||||
p_scopes text[], p_nonce text, p_code_challenge text,
|
||||
p_code_challenge_method text, p_auth_time timestamptz, p_amr text[],
|
||||
p_seconds integer)
|
||||
p_resource text, p_seconds integer)
|
||||
LANGUAGE sql AS $$
|
||||
DELETE FROM oidc_codes WHERE expires < now();
|
||||
INSERT INTO oidc_codes VALUES (p_hash, p_client_id, p_name, p_redirect_uri, p_scopes, p_nonce, p_code_challenge,
|
||||
p_code_challenge_method, p_auth_time, p_amr, now() + make_interval(secs => p_seconds));
|
||||
INSERT INTO oidc_codes (hash, client_id, name, redirect_uri, scopes, nonce, code_challenge, code_challenge_method,
|
||||
auth_time, amr, resource, expires)
|
||||
VALUES (p_hash, p_client_id, p_name, p_redirect_uri, p_scopes, p_nonce, p_code_challenge, p_code_challenge_method,
|
||||
p_auth_time, p_amr, p_resource, now() + make_interval(secs => p_seconds));
|
||||
$$;
|
||||
|
||||
-- The unexpired code.
|
||||
CREATE OR REPLACE FUNCTION oidc_code_get(p_hash text)
|
||||
RETURNS TABLE (client_id text, name text, redirect_uri text, scopes text[], nonce text, code_challenge text,
|
||||
code_challenge_method text, auth_time timestamptz, amr text[])
|
||||
code_challenge_method text, auth_time timestamptz, amr text[], resource text)
|
||||
LANGUAGE sql STABLE AS $$
|
||||
SELECT client_id, name, redirect_uri, scopes, nonce, code_challenge, code_challenge_method, auth_time, amr
|
||||
SELECT client_id, name, redirect_uri, scopes, nonce, code_challenge, code_challenge_method, auth_time, amr, resource
|
||||
FROM oidc_codes WHERE hash = p_hash AND expires > now();
|
||||
$$;
|
||||
|
||||
@@ -66,18 +100,19 @@ LANGUAGE sql AS $$
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE oidc_token_save(p_hash text, p_kind text, p_client_id text, p_name text, p_scopes text[],
|
||||
p_auth_time timestamptz, p_amr text[], p_seconds integer)
|
||||
p_auth_time timestamptz, p_amr text[], p_resource text, p_seconds integer)
|
||||
LANGUAGE sql AS $$
|
||||
DELETE FROM oidc_tokens WHERE expires < now();
|
||||
INSERT INTO oidc_tokens VALUES (p_hash, p_kind, p_client_id, p_name, p_scopes, p_auth_time, p_amr,
|
||||
now() + make_interval(secs => p_seconds));
|
||||
INSERT INTO oidc_tokens (hash, kind, client_id, name, scopes, auth_time, amr, resource, expires)
|
||||
VALUES (p_hash, p_kind, p_client_id, p_name, p_scopes, p_auth_time, p_amr, p_resource,
|
||||
now() + make_interval(secs => p_seconds));
|
||||
$$;
|
||||
|
||||
-- The unexpired token of the kind.
|
||||
CREATE OR REPLACE FUNCTION oidc_token_get(p_hash text, p_kind text)
|
||||
RETURNS TABLE (client_id text, name text, scopes text[], auth_time timestamptz, amr text[])
|
||||
RETURNS TABLE (client_id text, name text, scopes text[], auth_time timestamptz, amr text[], resource text)
|
||||
LANGUAGE sql STABLE AS $$
|
||||
SELECT client_id, name, scopes, auth_time, amr FROM oidc_tokens
|
||||
SELECT client_id, name, scopes, auth_time, amr, resource FROM oidc_tokens
|
||||
WHERE hash = p_hash AND kind = p_kind AND expires > now();
|
||||
$$;
|
||||
|
||||
@@ -86,6 +121,59 @@ LANGUAGE sql AS $$
|
||||
DELETE FROM oidc_tokens WHERE hash = p_hash;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE oidc_device_save(p_hash text, p_user_hash text, p_client_id text, p_scopes text[],
|
||||
p_resource text, p_poll_seconds integer, p_seconds integer)
|
||||
LANGUAGE sql AS $$
|
||||
DELETE FROM oidc_devices WHERE expires < now();
|
||||
INSERT INTO oidc_devices (hash, user_hash, client_id, scopes, resource, poll_seconds, expires)
|
||||
VALUES (p_hash, p_user_hash, p_client_id, p_scopes, p_resource, p_poll_seconds,
|
||||
now() + make_interval(secs => p_seconds));
|
||||
$$;
|
||||
|
||||
-- The unexpired device code of the user code while it waits for the user.
|
||||
CREATE OR REPLACE FUNCTION oidc_device_find(p_user_hash text)
|
||||
RETURNS TABLE (client_id text, scopes text[], resource text)
|
||||
LANGUAGE sql STABLE AS $$
|
||||
SELECT client_id, scopes, resource FROM oidc_devices
|
||||
WHERE user_hash = p_user_hash AND state = 'pending' AND expires > now();
|
||||
$$;
|
||||
|
||||
-- Allow or deny the waiting device code of the user code as the user; whether there was one.
|
||||
CREATE OR REPLACE FUNCTION oidc_device_decide(p_user_hash text, p_name text, p_auth_time timestamptz, p_amr text[],
|
||||
p_allowed boolean)
|
||||
RETURNS boolean
|
||||
LANGUAGE sql AS $$
|
||||
WITH decided AS (
|
||||
UPDATE oidc_devices SET state = CASE WHEN p_allowed THEN 'allowed' ELSE 'denied' END, name = p_name,
|
||||
auth_time = p_auth_time, amr = p_amr
|
||||
WHERE user_hash = p_user_hash AND state = 'pending' AND expires > now()
|
||||
RETURNING 1
|
||||
)
|
||||
SELECT count(*) > 0 FROM decided;
|
||||
$$;
|
||||
|
||||
-- A poll of the device code: the row, whether it has expired, and whether it came sooner than poll_seconds after the
|
||||
-- last one, which adds 5 seconds to poll_seconds.
|
||||
CREATE OR REPLACE FUNCTION oidc_device_poll(p_hash text)
|
||||
RETURNS TABLE (client_id text, scopes text[], resource text, state text, name text, auth_time timestamptz, amr text[],
|
||||
expired boolean, too_soon boolean)
|
||||
LANGUAGE sql AS $$
|
||||
UPDATE oidc_devices AS device
|
||||
SET polled = now(),
|
||||
poll_seconds = device.poll_seconds +
|
||||
CASE WHEN device.polled + make_interval(secs => device.poll_seconds) > now() THEN 5 ELSE 0 END
|
||||
FROM (SELECT hash, polled, poll_seconds FROM oidc_devices WHERE hash = p_hash) AS previous
|
||||
WHERE device.hash = previous.hash
|
||||
RETURNING device.client_id, device.scopes, device.resource, device.state, device.name, device.auth_time, device.amr,
|
||||
device.expires <= now(),
|
||||
previous.polled IS NOT NULL AND previous.polled + make_interval(secs => previous.poll_seconds) > now();
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE oidc_device_delete(p_hash text)
|
||||
LANGUAGE sql AS $$
|
||||
DELETE FROM oidc_devices WHERE hash = p_hash;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE PROCEDURE oidc_session_save(p_hash text, p_name text, p_amr text[], p_seconds integer)
|
||||
LANGUAGE sql AS $$
|
||||
DELETE FROM oidc_sessions WHERE expires < now();
|
||||
@@ -104,10 +192,11 @@ LANGUAGE sql AS $$
|
||||
DELETE FROM oidc_sessions WHERE hash = p_hash;
|
||||
$$;
|
||||
|
||||
-- Remove every code, token and session of the user.
|
||||
-- Remove every code, token, device code and session of the user or service account.
|
||||
CREATE OR REPLACE PROCEDURE oidc_revoke(p_name text)
|
||||
LANGUAGE sql AS $$
|
||||
DELETE FROM oidc_codes WHERE name = p_name;
|
||||
DELETE FROM oidc_tokens WHERE name = p_name;
|
||||
DELETE FROM oidc_devices WHERE name = p_name;
|
||||
DELETE FROM oidc_sessions WHERE name = p_name;
|
||||
$$;
|
||||
Reference in new issue
Block a user