-- -- DSP OAuth schema for JupyterHub integration -- -- Run inside the MySQL container, e.g.: -- docker-compose exec db mysql -u root -p niph_dsps < db/migrations/20241103_oauth_tables.sql -- START TRANSACTION; CREATE TABLE IF NOT EXISTS `dsp_oauth_clients` ( `client_id` varchar(128) NOT NULL, `client_name` varchar(255) NOT NULL, `client_secret_hash` varchar(255) DEFAULT NULL, `redirect_uris` text NOT NULL, `allowed_scopes` varchar(255) DEFAULT NULL, `is_confidential` tinyint(1) NOT NULL DEFAULT 1, `is_revoked` tinyint(1) NOT NULL DEFAULT 0, `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime DEFAULT NULL, PRIMARY KEY (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; CREATE TABLE IF NOT EXISTS `dsp_oauth_auth_codes` ( `code_hash` char(64) NOT NULL, `client_id` varchar(128) NOT NULL, `person_id` int(11) NOT NULL, `scope` varchar(255) DEFAULT NULL, `redirect_uri` varchar(2000) NOT NULL, `expires_at` datetime NOT NULL, `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`code_hash`), KEY `idx_oauth_auth_client` (`client_id`), KEY `idx_oauth_auth_expires` (`expires_at`), CONSTRAINT `dsp_oauth_auth_codes_ibfk_1` FOREIGN KEY (`client_id`) REFERENCES `dsp_oauth_clients` (`client_id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `dsp_oauth_auth_codes_ibfk_2` FOREIGN KEY (`person_id`) REFERENCES `ist_tbl_people` (`pkisp_id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; CREATE TABLE IF NOT EXISTS `dsp_oauth_access_tokens` ( `token_hash` char(64) NOT NULL, `client_id` varchar(128) NOT NULL, `person_id` int(11) NOT NULL, `scope` varchar(255) DEFAULT NULL, `expires_at` datetime NOT NULL, `refresh_token_hash` char(64) DEFAULT NULL, `refresh_expires_at` datetime DEFAULT NULL, `is_revoked` tinyint(1) NOT NULL DEFAULT 0, `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `last_used_at` datetime DEFAULT NULL, `revoked_at` datetime DEFAULT NULL, PRIMARY KEY (`token_hash`), KEY `idx_oauth_access_client` (`client_id`), KEY `idx_oauth_access_person` (`person_id`), KEY `idx_oauth_access_refresh` (`refresh_token_hash`), KEY `idx_oauth_access_expires` (`expires_at`), CONSTRAINT `dsp_oauth_access_tokens_ibfk_1` FOREIGN KEY (`client_id`) REFERENCES `dsp_oauth_clients` (`client_id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `dsp_oauth_access_tokens_ibfk_2` FOREIGN KEY (`person_id`) REFERENCES `ist_tbl_people` (`pkisp_id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; COMMIT; -- -- Optional helper to register the JupyterHub client. -- Replace the placeholder secret/redirects before running. -- -- INSERT INTO dsp_oauth_clients ( -- client_id, -- client_name, -- client_secret_hash, -- redirect_uris, -- allowed_scopes -- ) VALUES ( -- 'hub-client', -- 'DSP JupyterHub', -- '$2y$10$replace_this_with_password_hash', -- 'https://hub.example.com/hub/oauth_callback', -- 'profile' -- ) -- ON DUPLICATE KEY UPDATE -- client_name = VALUES(client_name), -- client_secret_hash = VALUES(client_secret_hash), -- redirect_uris = VALUES(redirect_uris), -- allowed_scopes = VALUES(allowed_scopes), -- updated_at = NOW();