There are multiple forms of authentication built into SurrealDB, supporting different use cases:
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.
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;
Examples using the JavaScript SDK or a raw HTTP request.
const db = new Surreal(); db.connect('ws://localhost:8000/rpc', { namespace: 'test', database: 'test', }); db.signin({ username: 'john', password: 'VerySecurePassword!', });
curl -X POST \ -H "Accept: application/json" \ -d '{"user":"john", "pass":"VerySecurePassword!"}' \ http://localhost:8000/signin
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;
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.
const db = new Surreal(); db.connect('ws://localhost:8000/rpc', { namespace: 'test', database: 'test', }); db.signin({ // Because we are signin in a database user, we need to let SurrealDB know on which database this user is located. namespace: 'test', database: 'test', username: 'mary', password: 'VerySecurePassword!', });
curl -X POST \ -H "Accept: application/json" \ -d '{"NS":"test", "DB":"test", "user":"mary", "pass":"VerySecurePassword!"}' \ http://localhost:8000/signin
Available since: v2.0.0
Record users (called “scope users” before v2.0.0) 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.
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!
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:
Define tables and fieldsDEFINE 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: 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:
email
and password
parameters to be provided by the user. In the query, we can use them as $email
and $password
.name
, email
and password
parameters to be provided by the user. In the query, we can use them as $name
, $email
and $password
.
Scope definitionDEFINE 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) } );
Now that the record access is defined, we can start using it.
Examples using the JavaScript SDK or a raw HTTP request.
const db = new Surreal(); db.connect('ws://localhost:8000/rpc', { namespace: 'test', database: 'test', }); db.signup({ namespace: 'test', database: 'test', // Provide the name of the access method access: 'user', // Provide the variables used by the signup query variables: { name: 'John Doe', email: 'john@doe.org', password: 'VerySecurePassword!', } });
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
Once a user has signed up, it can now sign in when needed.
Examples using the JavaScript SDK or a raw HTTP request.
const db = new Surreal(); db.connect('ws://localhost:8000/rpc', { namespace: 'test', database: 'test', }); db.signin({ namespace: 'test', database: 'test', // Provide the name of the access method access: 'user', // Provide the variables used by the signin query variables: { email: 'john@doe.org', password: 'VerySecurePassword!', } });
curl -X POST \ -H "Accept: application/json" \ -d '{"NS":"test", "DB":"test", "AC":"user", "email":"john@doe.org", "password":"VerySecurePassword!"}' \ http://localhost:8000/signin
Available since: v2.0.0
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.
Certain security-related parameters are automatically set by SurrealDB in the context of a session. These parameters can be referenced in SurrealQL during authentication and authorization.
The $session
parameter contains information about the current session. This parameter is set in every SurrealDB session.
In the following example, you can see the result of the SELECT * FROM $session
query in an authenticated session for a record user using the HTTP REST API:
{ "ac": "user", "db": "test", "exp": null, "id": "example-client", "ip": "127.0.0.1", "ns": "test", "or": "http://www.example.com", "rd": "user:example", "tk": { "AC": "user", "DB": "test", "ID": "user:example", "NS": "test", "exp": 1723118226, "iat": 1723114626, "iss": "SurrealDB", "jti": "3b3fe74a-955c-46d7-9400-363848912292", "nbf": 1723114626 } }
On the root of the object, you will find the following fields:
ip
: The IP address that established the connection with SurrealDB.exp
: The time at which the session will expire.NONE
or null
when the session does not expire.ns
: The name of the currently selected namespace.NONE
or null
when no namespace is selected.db
: The name of the currently selected database.NONE
or null
when no database is selected.rd
: The record identifier of the currently authenticated record user.NONE
or null
when unauthenticated or authenticated as a system user.ac
: The name of the access method that was used to authenticate.NONE
or null
when not authenticated with an access method.or
: The value of the Origin
header of the HTTP request.id
: The value of the surreal-id
header of the HTTP request.tk
: An object containing the claims present in the authentication token used to establish the session.NONE
or null
when not authenticated.The values stored in the session parameter can be accessed through the session::*
family of functions.
The $token
parameter contains the claims contained in the token used to authenticate the current session. This parameter is set in every authenticated SurrealDB session.
In this example, you can see the result of the SELECT * FROM $token
query in an authenticated session for a record user:
{ "AC": "user", "DB": "test", "ID": "user:example", "NS": "test", "exp": 1723118226, "iat": 1723114626, "iss": "SurrealDB", "jti": "3b3fe74a-955c-46d7-9400-363848912292", "nbf": 1723114626 }
Whenever authentication is done directly using a token, this object will contain the claims contained in that token.
When that token is issued by SurrealDB after successful authentication, the object will usually contain the following fields:
NS
: The name of the namespace the user is authenticated in.NONE
or null
when the user is authenticated at the root level.DB
: The name of the database the user is authenticated in.NONE
or null
when the user is authenticated at the root or namespace level.ID
: The record identifier of the currently authenticated record user.AC
: The name of the access method that is used to authenticate.NONE
or null
when authenticating without an access method.The following fields correspond to claims defined in the standard JWT implementation described in RFC 7519:
iat
: The time at which the token was issued.nbf
: The time before which the token will not be accepted to establish new authenticated sessions.exp
: The time after which the token will not be accepted to establish new authenticated sessions.jti
: A unique identifier used to reference the token.iss
: A string identifying the entity which issued the token.The $auth
parameter points to the record belonging to the current authenticated record user. This parameter is set only when the session is authenticated with an existing record user.
When the $auth
parameter is set, you can access any of the fields of the record corresponding to the authenticated user (e.g. $auth.name
or $auth.email
) via that parameter.
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.