This page shows how a Python application signs in to SurrealDB with the SDK. It covers the four sign-in levels, sign-up for record users, access tokens and refresh tokens, the auth option that signs a connection in by itself, automatic token renewal, and signing out.
API references
Method | Description |
|---|---|
db.signin(vars) | Signs in as a root, namespace, database or record user |
db.signup(vars) | Signs up a new record user with a record access method |
db.authenticate(token) | Authenticates the session with an access token |
Surreal(url, auth=...) | Makes the connection sign itself in |
db.info() | Returns the record of the signed-in record user |
db.invalidate() | Signs the session out |
db.reset() | Clears the namespace, database, variables and sign-in of the session |
db.refresh(tokens?) | Exchanges a refresh token for a new access token and refresh token |
db.revoke(tokens?) | Revokes a refresh token, so that it cannot be exchanged again |
Signing in
signin() signs the session in. The fields that you pass choose the level of the user. It returns Tokens, which holds the access token and, for an access method defined WITH REFRESH, a refresh token. See the API reference for every field at each level.
A root user has access to the whole SurrealDB instance. Pass the username and password.
from surrealdb import Surreal
db = Surreal("ws://localhost:8000")
db.connect()
tokens = db.signin({
"username": "root",
"password": "secret",
})The examples use the blocking API. On an asynchronous connection, await each call.
When a namespace, database or record user signs in, the server also selects the namespace of that user, and for a database or record user, the database as well. A namespace user still needs use() to select a database.
The repr of Tokens hides both values, so printing or logging it does not expose them. For the root user, it prints:
print(tokens)Tokens(access='<redacted>', refresh=None)Read tokens.access and tokens.refresh when you need the values, for example to store them.
Each session from new_session() starts with the sign-in of the connection, and can sign in as another user without changing the connection. See Multiple sessions.
Signing up record users
signup() creates a record user with a record access method and signs the session in as that user. It returns Tokens, like signin(). Sign-up is only available for record access methods.
The examples on this page use the record access method below. Define it as a root, namespace or database user, for example with db.query(). Its WITH REFRESH clause makes it issue a refresh token as well, which Refresh tokens uses.
DEFINE TABLE person
PERMISSIONS FOR select, update WHERE id = $auth.id;
DEFINE ACCESS account ON DATABASE TYPE RECORD
SIGNUP (
CREATE person CONTENT {
email: $email,
pass: crypto::argon2::generate($password)
}
)
SIGNIN (
SELECT * FROM person WHERE email = $email
AND crypto::argon2::compare(pass, $password)
)
WITH REFRESH
DURATION FOR TOKEN 15m;Pass the namespace, database and access, and the variables that the SIGNUP clause reads:
from surrealdb import Surreal
db = Surreal("ws://localhost:8000")
db.connect()
tokens = db.signup({
"namespace": "surrealdb",
"database": "docs",
"access": "account",
"variables": {
"email": "tobie@surrealdb.com",
"password": "hunter123",
},
})Signing in with the auth option
The auth option of Surreal() and AsyncSurreal() makes the connection sign itself in, so that you do not need to call signin() yourself. Pass the same credentials that signin() takes:
from surrealdb import RecordID, Surreal
with Surreal(
"ws://localhost:8000",
auth={"username": "root", "password": "secret"},
) as db:
db.use("surrealdb", "docs")
db.create(RecordID("person", "jaime"), {"name": "Jaime"})auth takes four kinds of value:
| Value | What the connection does |
|---|---|
A dict of credentials | Signs in with them, as signin() does. |
A str | Authenticates with it as an access token, as authenticate() does. |
Tokens | Authenticates with the access token, and keeps the refresh token to renew it. If the server refuses the access token, for example because it has expired, the connection renews it with the refresh token. |
| A function with no arguments | Calls the function each time that the connection signs in, and uses the value that it returns, which is one of the values above. On AsyncSurreal it can be a coroutine function. |
A function lets the connection read credentials or a token each time it needs them, for example from the environment or from a secret store:
import os
from surrealdb import Surreal
def credentials():
return {
"username": "root",
"password": os.environ["SURREALDB_PASSWORD"],
}
db = Surreal("ws://localhost:8000", auth=credentials)A coroutine function given to Surreal() raises TypeError when the connection signs in, because a blocking connection cannot await it.
A WebSocket connection applies auth when its socket opens, and an HTTP connection applies it before its first request. After an automatic reconnect, the connection signs in again with the credentials, or calls the function again. A token or Tokens given directly is not applied again. The connection restores the tokens that it holds at that time instead, which are newer ones if a renewal has replaced them.
The connection also applies auth again in three cases: after close() and connect(), after connect() with a new URL, and after a WebSocket drops while reconnection is off. An embedded connection ignores auth.
Signing in on a connection that uses auth
A call to signin(), signup(), authenticate(), invalidate() or reset() on the connection's own session replaces the sign-in that auth gave. From then on, renewal uses only the refresh token of the session, and a reconnect restores the sign-in that you chose. auth applies again only in the three cases above:
from surrealdb import Surreal
db = Surreal(
"ws://localhost:8000",
auth={"username": "root", "password": "secret"},
)
db.connect()
db.signin({
"namespace": "surrealdb",
"database": "docs",
"access": "account",
"variables": {
"email": "tobie@surrealdb.com",
"password": "hunter123",
},
})
print(db.query("RETURN session::ac()").first())
db.close()
db.connect()
print(db.query("RETURN session::ac()").first())
db.close()account
NoneThe first query runs as the record user of the account access method. After close() and connect(), the connection is the root user of auth again, which has no access method. A sign-in on a session from new_session() does not change the connection's own session, so auth keeps applying to it.
Authenticating with a token
authenticate() signs the session in with an access token that you already have, for example one that signin() returned to another part of your application, or one that a client sent to your server:
db.authenticate(tokens.access)A string that does not have the form of a JWT raises ValueError before anything is sent. A token that the server refuses raises NotAllowedError over a WebSocket, and HttpStatusError with status 401 over HTTP. Over HTTP, a refused token does not replace the sign-in that the connection had before.
Reading the signed-in user
info() returns the record of the signed-in record user:
user = db.info()
print(user["email"])tobie@surrealdb.cominfo() returns None for a root, namespace or database user, when a namespace and database are selected. Without them, it raises ValidationError. It also returns None for a record user whose table permissions do not let them select their own record. The PERMISSIONS clause on person above lets each user select their own record.
Signing out and resetting a session
invalidate() signs the session out. Requests after it run without authentication.
db.invalidate()reset() returns the session to how it started: it clears the namespace, the database, the variables and the sign-in. This lets you reuse a connection for different work, for example to return a pooled connection in a clean state. It needs SurrealDB 3.0 or later.
db.let("greeting", "hello")
db.reset()
print(db.namespace, db.database, db.token)None None NoneBoth methods replace the sign-in that the auth option gave, as described in Signing in on a connection that uses auth.
Refresh tokens
An access method defined WITH REFRESH returns a refresh token with each sign-in and sign-up, in tokens.refresh. The client can exchange a refresh token once for a new access token and a new refresh token, so that the access token can be short-lived without the user signing in again. Refresh tokens need SurrealDB 3.0 or later. Against SurrealDB 2.x, refresh() and revoke() raise UnavailableFeatureError.
refresh() exchanges the refresh token for a new pair of tokens, authenticates the session with the new access token, and returns the new Tokens. Without an argument, it uses the tokens that the session holds. Pass Tokens to exchange other ones.
from surrealdb import Surreal
with Surreal("ws://localhost:8000") as db:
tokens = db.signin({
"namespace": "surrealdb",
"database": "docs",
"access": "account",
"variables": {
"email": "tobie@surrealdb.com",
"password": "hunter123",
},
})
print(tokens)
renewed = db.refresh()
print(renewed.refresh == tokens.refresh)Tokens(access='<redacted>', refresh='<redacted>')
FalseThe exchange spends the old refresh token. If you exchange it again, the server refuses it and the SDK raises NotAllowedError.
To sign in later with a stored refresh token, for example in a new process, pass it as refresh in place of variables. The sign-in spends that refresh token and returns a new pair. When the user signs out, revoke() makes the refresh token of the session unusable, and invalidate() ends the sign-in:
with Surreal("ws://localhost:8000") as db:
tokens = db.signin({
"namespace": "surrealdb",
"database": "docs",
"access": "account",
"refresh": renewed.refresh,
})
db.revoke()
db.invalidate()revoke() does not end the sign-in: the session stays signed in with its access token until that token expires or invalidate() is called. refresh() and revoke() raise ValueError when you pass no Tokens and the session holds no refresh token, for example after revoke().
Automatic token renewal
With renew_access=True, the default, a connection renews its own access token before the token expires: 60 seconds before, or a tenth of the token's lifetime before if that is shorter. It renews with its refresh token if it holds one and the server supports refresh tokens, and otherwise by applying the auth option again.
A connection created with auth credentials or a function can renew by signing in again. A connection that signed in with signin() renews only with a refresh token. A token or Tokens given to auth cannot be applied again, so without a refresh token such a connection cannot renew either.
The asynchronous WebSocket connection renews on a timer. The blocking connection and the HTTP connections have no background work, so they renew before the next request that finds the token due.
A failed renewal is reported as an "error" event on a WebSocket connection, and the connection tries again later. A refresh token that the server refuses is dropped. The connection then falls back to auth, or keeps the access token that it has.
Renewal keeps two kinds of connection working past the token duration: an HTTP connection, which sends the token with each request, and a WebSocket session that authenticated with a token. A WebSocket session that signed in with credentials does not depend on the token, as Connecting to SurrealDB shows. renew_access=False turns renewal off.
Authentication over HTTP and WebSocket
Over a WebSocket, the server holds the session of the connection. You sign in once, and the session keeps its namespace, database and sign-in until the session or the connection ends. The token property holds the access token of the connection's own session.
Over HTTP, the SDK holds the sign-in. It sends the access token as a bearer token, and the namespace and database as headers, with each request. A token that has expired or that the server refuses raises HttpStatusError with status 401, not NotAllowedError. token is a plain attribute on an HTTP connection, and assigning a new token to it sends that token from the next request on. This assignment replaces the set_token() method of SDK 2.x: see Migrating to 3.x.
Embedded databases
An embedded connection needs no sign-in. Its session starts with full access, as an owner of the engine. The engine has no root user, so signin() with root credentials raises NotAllowedError, and auth and renew_access are ignored. invalidate() leaves the session anonymous and without access. See Embedded databases.
Learn more
Surreal API reference for the parameters of each method
Tokens for the structure of the returned tokens
Connecting to SurrealDB for the connection options and what a reconnect restores
Multiple sessions for signing in several users on one connection
Authentication in SurrealDB for the users, access methods and durations on the server
DEFINE ACCESSfor defining record and JWT access methods