Skip to main content

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
  • Scope users: Used for consuming the database, and they allow custom signup, signin and permissions logic

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).

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

Scope users

Scope is the term we use to describe the mechanism SurrealDB offers to define your own signin and signup logic. This feature contributes to making SurrealDB an all-in-one BaaS (Backend-as-a-Service).

Scopes are defined with the DEFINE SCOPE statement. A scope is configured with the following config:

  • SIGNUP: Defines the logic for when a user signs up to the scope. It usually creates a new entry to a table
  • SIGNIN: Defines the logic for when a user signs in to the scope. It usually check the provided credentials against the data in a table
  • SESSION: Defines the session duration

By default, scopes have no permissions. They don't use the RBAC system and can only view data if allowed by a PERMISSIONS clause, which is defined on every data resource (i.e. tables)

Go to DEFINE SCOPE for more information.

Example: Setup scope authentication

We will go over one of the many ways you can set up scope 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 scope

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

The scope is configured like this:

  • Session tokens expire in 1 day. When a user signs up or signs in, a new session token is created.
  • The sign in logic needs the email and password parameters to be provided by the user. In the scope logic, we 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 scope logic, we can use them as $name, $email and $password
Scope definition
DEFINE SCOPE user SESSION 1d
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 to the scope

Now that the scope 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 scope
SC: '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", "SC":"user", "name":"John Doe", "email":"john@doe.org", "password":"VerySecurePassword!"}' \
http://localhost:8000/signup

Sign in to the scope

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 scope
SC: '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", "SC":"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 persist 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 Since 1.3.0

Authenticated sessions established using a token (i.e. JWT) will remain authenticated for as long as that token is valid according to its exp claim. Whenever a token that was used to establish a session expires, so will the session. This is especially relevant in the case of WebSocket connections such as those used by the SurrealDB SDKs. 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 a new token that has not yet expired. This includes calling the signin method to obtain a new token with credentials, which will replace the expired session token, or calling the authenticate method to replace the expired session token with a new token.

Although SurrealDB sessions will always eventually expire when a token containing an expiration claim is used to authenticate, SurrealDB will not set a session expiration in sessions established when authenticating as a system user with credentials. This is due to the fact that all tokens issued by SurrealDB have a default duration of 24 hours, which cannot currently be modified in the case of system users. However, if a system user authenticates using a token, the expiration of the token will be used in the session regardless of the type of user and whether this token was issued by SurrealDB or a third party.

In summary, session expiration will always match the token expiration when authenticating with a token. When authenticating with credentials, session expiration will match the scope session duration defined via the DURATION clause in DEFINE SCOPE in the case of scope users. In the case of authenticating as a system user with credentials, sessions will currently not expire.