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 tableSIGNIN
: Defines the logic for when a user signs in to the scope. It usually check the provided credentials against the data in a tableSESSION
: 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 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 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
andpassword
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
andpassword
parameters to be provided by the user. In the scope logic, we can use them as$name
,$email
and$password
Scope definitionDEFINE 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.