Skip to main content

MySQL setup

important

This is needed only if you are running the SuperTokens core yourself.

blog

We also have a blog post writeup highlighting all the steps in more detail for different scenarios.

1) Create a database ๐Ÿ› ๏ธ#

CREATE DATABASE supertokens;

You can skip this step if you want SuperTokens to write to your own database. In this case, you will need to provide your database's name as shown in the step below.

2) Connect SuperTokens to your database ๐Ÿ”Œ#

caution

Host being localhost / 127.0.0.1 will not work in a docker image. Instead, please provide the database's local / public hostname or IP address.

You also need to make the database listen on all the IP's of the local machine. This can be done by editing the mysqld.cnf config file and setting the value of bind-address to 0.0.0.0.


docker run \
-p 3567:3567 \
-e MYSQL_CONNECTION_URI="mysql://username:pass@host/dbName" \
-d registry.supertokens.io/supertokens/supertokens-mysql

# OR

docker run \
-p 3567:3567 \
-e MYSQL_USER="username" \
-e MYSQL_PASSWORD="password" \
-e MYSQL_HOST="host" \
-e MYSQL_PORT="3306" \
-e MYSQL_DATABASE_NAME="supertokens" \
-d registry.supertokens.io/supertokens/supertokens-mysql

3) Create tables ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป#

note

This happens automatically, unless you provide a MySQL user that doesn't have table creation permission.

CREATE TABLE IF NOT EXISTS key_value (
name VARCHAR(128),
value TEXT,
created_at_time BIGINT UNSIGNED,
PRIMARY KEY(name)
);

CREATE TABLE IF NOT EXISTS all_auth_recipe_users(
user_id CHAR(36) NOT NULL,
recipe_id VARCHAR(128) NOT NULL,
time_joined BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (user_id)
);
CREATE INDEX all_auth_recipe_users_pagination_index ON all_auth_recipe_users (time_joined DESC, user_id DESC);

CREATE TABLE IF NOT EXISTS userid_mapping(
supertokens_user_id CHAR(36) NOT NULL UNIQUE,
external_user_id VARCHAR(128) NOT NULL UNIQUE,
external_user_id_info TEXT,
PRIMARY KEY (supertokens_user_id, external_user_id),
FOREIGN KEY (supertokens_user_id) REFERENCES all_auth_recipe_users(user_id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS dashboard_users(
user_id CHAR(36) NOT NULL,
email VARCHAR(256) NOT NULL UNIQUE,
password_hash VARCHAR(256) NOT NULL,
time_joined BIGINT UNSIGNED NOT NULL,
PRIMARY KEY(user_id)
);

CREATE TABLE IF NOT EXISTS dashboard_user_sessions(
session_id CHAR(36) NOT NULL,
user_id CHAR(36) NOT NULL,
time_created BIGINT UNSIGNED NOT NULL,
expiry BIGINT UNSIGNED NOT NULL,
PRIMARY KEY(session_id),
FOREIGN KEY(user_id) REFERENCES dashboard_users (user_id) ON DELETE CASCADE
);

CREATE INDEX dashboard_user_sessions_expiry_index ON dashboard_user_sessions(expiry);

CREATE TABLE session_access_token_signing_keys (
created_at_time BIGINT UNSIGNED NOT NULL,
value TEXT,
PRIMARY KEY (created_at_time)
);

CREATE TABLE IF NOT EXISTS session_info (
session_handle VARCHAR(255) NOT NULL,
user_id VARCHAR(128) NOT NULL,
refresh_token_hash_2 VARCHAR(128) NOT NULL,
session_data TEXT,
expires_at BIGINT UNSIGNED NOT NULL,
created_at_time BIGINT UNSIGNED NOT NULL,
jwt_user_payload TEXT,
PRIMARY KEY(session_handle)
);

CREATE TABLE IF NOT EXISTS user_last_active (
user_id VARCHAR(128),
last_active_time BIGINT UNSIGNED,
PRIMARY KEY(user_id)
);

CREATE TABLE IF NOT EXISTS emailpassword_users (
user_id CHAR(36) NOT NULL,
email VARCHAR(256) NOT NULL UNIQUE,
password_hash VARCHAR(256) NOT NULL,
time_joined BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (user_id)
);

CREATE TABLE IF NOT EXISTS emailpassword_pswd_reset_tokens (
user_id CHAR(36) NOT NULL,
token VARCHAR(128) NOT NULL UNIQUE,
token_expiry BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (user_id, token),
FOREIGN KEY (user_id) REFERENCES emailpassword_users (user_id) ON DELETE CASCADE ON UPDATE CASCADE
);

CREATE INDEX emailpassword_password_reset_token_expiry_index ON emailpassword_pswd_reset_tokens(token_expiry);

CREATE TABLE IF NOT EXISTS emailverification_verified_emails (
user_id VARCHAR(128) NOT NULL,
email VARCHAR(256),
PRIMARY KEY (user_id, email)
);

CREATE TABLE IF NOT EXISTS emailverification_tokens (
user_id VARCHAR(128) NOT NULL,
email VARCHAR(256),
token VARCHAR(128) NOT NULL UNIQUE,
token_expiry BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (user_id, email, token)
);

CREATE INDEX emailverification_tokens_index ON emailverification_tokens(token_expiry);

CREATE TABLE IF NOT EXISTS thirdparty_users (
third_party_id VARCHAR(28) NOT NULL,
third_party_user_id VARCHAR(256) NOT NULL,
user_id CHAR(36) NOT NULL,
email VARCHAR(256) NOT NULL,
time_joined BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (third_party_id, third_party_user_id)
);

CREATE TABLE IF NOT EXISTS passwordless_users (
user_id CHAR(36) NOT NULL,
email VARCHAR(256) UNIQUE,
phone_number VARCHAR(256) UNIQUE,
time_joined BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (user_id)
);

CREATE TABLE IF NOT EXISTS passwordless_devices (
device_id_hash CHAR(44) NOT NULL,
email VARCHAR(256),
phone_number VARCHAR(256),
link_code_salt CHAR(44) NOT NULL,
failed_attempts INT UNSIGNED NOT NULL,
PRIMARY KEY (device_id_hash)
);
CREATE INDEX passwordless_devices_email_index ON passwordless_devices USING HASH (email);
CREATE INDEX passwordless_devices_phone_number_index ON passwordless_devices USING HASH (phone_number);

CREATE TABLE IF NOT EXISTS passwordless_codes (
code_id CHAR(36) NOT NULL,
device_id_hash CHAR(44) NOT NULL,
link_code_hash CHAR(44) NOT NULL UNIQUE,
created_at BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (code_id),
FOREIGN KEY (device_id_hash)
REFERENCES passwordless_devices(device_id_hash) ON DELETE CASCADE ON UPDATE CASCADE
);

CREATE INDEX passwordless_codes_created_at_index ON passwordless_codes(created_at);

CREATE TABLE IF NOT EXISTS jwt_signing_keys (
key_id VARCHAR(255) NOT NULL,
key_string TEXT NOT NULL,
algorithm VARCHAR(10) NOT NULL,
created_at BIGINT UNSIGNED,
PRIMARY KEY(key_id)
);

CREATE TABLE IF NOT EXISTS user_metadata (
user_id VARCHAR(128) NOT NULL,
user_metadata TEXT NOT NULL,
PRIMARY KEY(user_id)
);

CREATE TABLE IF NOT EXISTS roles (
role VARCHAR(255) NOT NULL,
PRIMARY KEY (role)
);

CREATE TABLE IF NOT EXISTS role_permissions (
role VARCHAR(255) NOT NULL,
permission VARCHAR(255) NOT NULL,
PRIMARY KEY (role, permission),
FOREIGN KEY (role) REFERENCES roles(role) ON DELETE CASCADE
);

CREATE INDEX role_permissions_permission_index ON role_permissions(permission);

CREATE TABLE IF NOT EXISTS user_roles (
user_id VARCHAR(128) NOT NULL,
role VARCHAR(255) NOT NULL,
PRIMARY KEY (user_id, role),
FOREIGN KEY (role) REFERENCES roles (role) ON DELETE CASCADE
);

CREATE INDEX user_roles_role_index ON user_roles(role);

CREATE TABLE IF NOT EXISTS totp_users (
user_id VARCHAR(128) NOT NULL,
PRIMARY KEY (user_id)
);

CREATE TABLE IF NOT EXISTS totp_user_devices (
user_id VARCHAR(128) NOT NULL,
device_name VARCHAR(256) NOT NULL,
secret_key VARCHAR(256) NOT NULL,
period INTEGER NOT NULL,
skew INTEGER NOT NULL,
verified BOOLEAN NOT NULL,
PRIMARY KEY (user_id, device_name),
FOREIGN KEY (user_id) REFERENCES totp_users(user_id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS totp_used_codes (
user_id VARCHAR(128) NOT NULL,
code VARCHAR(8) NOT NULL,
is_valid BOOLEAN NOT NULL,
expiry_time_ms BIGINT UNSIGNED NOT NULL,
created_time_ms BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (user_id, created_time_ms),
FOREIGN KEY (user_id) REFERENCES totp_users(user_id) ON DELETE CASCADE
);

CREATE INDEX totp_used_codes_expiry_time_ms_index ON totp_used_codes(expiry_time_ms);
tip

You also have the option to rename these tables.

4) Test the connection ๐Ÿคž#

To test, start SuperTokens and run the following query in your database

SELECT * FROM key_value;

If you see at least one row, it means that the connection has been successfully completed! ๐Ÿฅณ๐ŸŽ‰

blog

We also have a blog post writeup highlighting all the steps in more detail for different scenarios.