Skip to main content
Version: 2.x(alpha)

Authentication

There are multiple forms of authentication built into SurrealDB, supporting different use cases:

  • System users: Created by the SurrealDB administrator and used for managing and consuming the database.
  • Record users: Used for consuming the database within permissions logic, they allow custom signup and signin.

System users

System users is the term we use to describe users defined directly on SurrealDB by the administrator. Same concept as any other database.

Users may belong to different levels (root, namespace or database) and have different roles assigned to limit what they can do to the system. Users are defined with the DEFINE USER statement.

SurrealDB implements RBAC (Role Based Access Control) to define what a user can do. Each user is assigned one or more roles (currently limited to the built-in OWNER, EDITOR and VIEWER roles) and will be allowed to perform an action over a resource as long as at least one of their roles allow it.

Go to DEFINE USER for more information.

Example: Define a Root-level user

Root-level users have visibility into all namespaces and databases, which means that their permissions apply to all of those levels.

In this example we will create a root-level user john with a password and the OWNER role:

DEFINE USER john ON ROOT PASSWORD "VerySecurePassword!" ROLES OWNER;

Sign in using the new user

Examples using the JavaScript SDK or a raw HTTP request.

JavaScript SDK

const db = new Surreal();
db.connect('ws://localhost:8000/rpc', {
ns: 'test',
db: 'test',
});

db.signin({
user: 'john',
pass: 'VerySecurePassword!',
});

HTTP Request

curl -X POST \
-H "Accept: application/json" \
-d '{"user":"john", "pass":"VerySecurePassword!"}' \
http://localhost:8000/signin

Example: Define a Database-level user

Database-level users have visibility into all resources that belong to the database where the user is defined.

In this example we will create a database-level user mary with a password and the EDITOR role:

DEFINE USER mary ON DATABASE PASSWORD "VerySecurePassword!" ROLES EDITOR;

Sign in using the new user

Examples using the JavaScript SDK or a raw HTTP request.

Notice how we need to pass along NS and DB properties here, to let SurrealDB know where the user is defined.

JavaScript SDK

const db = new Surreal();
db.connect('ws://localhost:8000/rpc', {
ns: 'test',
db: 'test',
});

db.signin({
// Because we are signin in a database user, we need to let SurrealDB know on which database this user is located.
NS: 'test',
DB: 'test',

user: 'mary',
pass: 'VerySecurePassword!',
});

HTTP Request

curl -X POST \
-H "Accept: application/json" \
-d '{"NS":"test", "DB":"test", "user":"mary", "pass":"VerySecurePassword!"}' \
http://localhost:8000/signin

Record users

Record users represent users that are defined as a record in a database instead of through the DEFINE USER statement. Since these users exist as regular database records, they can have associated fields containing any information required for authentication and authorization.

Thanks to this, SurrealDB is able to offer mechanisms to define your own signin and signup logic as well as custom table and field permissions for record users. This feature contributes to making SurrealDB an all-in-one BaaS (Backend-as-a-Service).

Record users are defined with the DEFINE ACCESS statement of TYPE RECORD.

A record access is configured with the following specific clauses:

  • SIGNUP: Defines the logic for when a user signs up as a record user. Usually creates a new record in a table.
  • SIGNIN: Defines the logic for when a user signs in as a record user. Usually checks credentials against table records.

By default, record users have no permissions. They don't use the Role-Based Access Control (RBAC) system and can only access data if allowed by a PERMISSIONS clause, which is defined on every data resource (i.e. tables and fields) and defaults to NONE.

To learn more about creating a record user, refer to the DEFINE ACCESS ... TYPE RECORD documentation.

Example: Setup record authentication

We will go over one of the many ways you can set up record authentication. Given you can define your own logic, there is not a single way to do it. Feel free to modify where needed!

Define the User table and fields

Typically, you would define a user table where new records are created every time a user signs up.

In the following code snippet we will define the user table and a few fields that enforce the following:

  • An authenticated user can select, update and delete its own user record.
  • Asserts that the email provided by the user is actually an email address.
  • Forbid users to use an email that is already in use by another user. We do this by creating a unique index for the email field.
Define tables and fields
DEFINE TABLE user SCHEMAFULL
PERMISSIONS
FOR select, update, delete WHERE id = $auth.id;

DEFINE FIELD name ON user TYPE string;
DEFINE FIELD email ON user TYPE string ASSERT string::is::email($value);
DEFINE FIELD password ON user TYPE string;

DEFINE INDEX email ON user FIELDS email UNIQUE;
Define the User record access

Define the user record access: allow users to signin and signup by using the table and fields defined in the previous step

We will configure the record access like this:

  • The sign in logic needs the email and password parameters to be provided by the user. In the query, we can use them as $email and $password.
  • The sign up logic needs the name, email and password parameters to be provided by the user. In the query, we can use them as $name, $email and $password.
Scope definition
DEFINE ACCESS user ON DATABASE TYPE RECORD
SIGNIN (
SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(password, $password)
)
SIGNUP (
CREATE user CONTENT {
name: $name,
email: $email,
password: crypto::argon2::generate($password)
}
);

Sign up as a user record

Now that the record access is defined, we can start using it.

Examples using the JavaScript SDK or a raw HTTP request.

JavaScript SDK

const db = new Surreal();
db.connect('ws://localhost:8000/rpc', {
ns: 'test',
db: 'test',
});

db.signup({
NS: 'test',
DB: 'test',

// Provide the name of the access method
AC: 'user',

// Provide the variables used by the signup query
name: 'John Doe',
email: 'john@doe.org',
password: 'VerySecurePassword!',
});

HTTP Request

curl -X POST \
-H "Accept: application/json" \
-d '{"NS":"test", "DB":"test", "AC":"user", "name":"John Doe", "email":"john@doe.org", "password":"VerySecurePassword!"}' \
http://localhost:8000/signup

Sign in as a record user

Once a user has signed up, it can now sign in when needed.

Examples using the JavaScript SDK or a raw HTTP request.

JavaScript SDK

const db = new Surreal();
db.connect('ws://localhost:8000/rpc', {
ns: 'test',
db: 'test',
});

db.signin({
NS: 'test',
DB: 'test',

// Provide the name of the access method
AC: 'user',

// Provide the variables used by the signin query
email: 'john@doe.org',
password: 'VerySecurePassword!',
});

HTTP Request

curl -X POST \
-H "Accept: application/json" \
-d '{"NS":"test", "DB":"test", "AC":"user", "email":"john@doe.org", "password":"VerySecurePassword!"}' \
http://localhost:8000/signin

Sessions

Whenever authentication is performed with any kind of user against SurrealDB, a session is established between the client and the SurrealDB server with which the connection was established. These sessions exist only in memory on the server for the duration of the connection, whether it is a single request through the HTTP REST API or through multiple requests in the same connection using the WebSocket API and any of the SDKs that leverage it.

Expiration

Authenticated sessions remain valid for a certain duration. This duration is NONE by default, meaning that sessions will not expire unless otherwise specified. This duration can be customised on both the DEFINE USER and DEFINE ACCESS statements to any specific value defining the maximum duration of an authenticated session associated with that user or access method. After the defined duration, the authenticated session will expire. For example, the DEFINE USER example DURATION FOR SESSION 1d clause will ensure that any authenticated sessions for the example user will expire after a day.

Expired sessions can no longer be used to call authenticated methods and doing so will result in a specific error indicating that the session has expired. SurrealQL can be used to check the expiration of the active session by running SELECT exp FROM $session, which show the Unix time when the session will expire or NONE in the case that the session does not expire.

Clients can reuse a connection with an expired session to refresh the session using valid credentials. This includes calling the signin method to obtain a new token with credentials and reauthenticate the session using that token or calling the authenticate method to reauthenticate the session with an existing token.

It is important to note that token duration and session duration are independent concepts. Token duration, which can be similarly customised, refers to the validity of the token (i.e. the value of its exp claim), during which it can be used to establish an authenticated session. Tokens issued by SurrealDB have a default duration of one hour. Token expiration is used to limit the time during which a token can be compromised resulting in unauthorized access; tokens are often stored in the client and could be stolen with attacks like cross-site scripting. Session expiration can be used to ensure that users are required to reauthenticate in order to prove that they still have access to valid credentials as well as to limit the impact of a compromised client with an established session; sessions are stored in memory in the server and cannot be similarly stolen. It is recommended that tokens are configured to last for as little as necessary before being exchanged for an authenticated session, whereas sessions are recommended to last for as little as necessary to allow for the typical client to complete a set of authenticated actions.