Authenticates the current connection with a JWT token.
db.authenticate(token)Arguments
Argument | Description |
|---|---|
token | The JWT authentication token. |
Example usage
Note: the following example uses the ureq crate with the json feature to first send a request to the database's /signup endpoint which returns a token. The reqwest crate and others can be used here instead.
Alternatively, you could use a command like the following, copy the returned token, and paste it into the .authenticate() method.
curl -X POST -H "Accept: application/json" -d '{"ns":"main","db":"main","ac":"account","user":"info@surrealdb.com","pass":"123456"}' http://localhost:8000/signupAs the DEFINE ACCESS statement below shows, a token will remain valid by default for 15 minutes.
// Use the following statement to set up the access
//
// DEFINE ACCESS account ON DATABASE TYPE RECORD
// SIGNUP ( CREATE user SET email = $email,
// pass = crypto::argon2::generate($pass) )
// SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) )
// DURATION FOR TOKEN 15m, FOR SESSION 12h
// ;
// DEFINE TABLE cat SCHEMALESS
// PERMISSIONS for select, update, delete, create
// WHERE $auth.id;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use surrealdb::Surreal;
use surrealdb::engine::remote::ws::Ws;
use surrealdb_types::SurrealValue;
#[derive(Deserialize, SurrealValue)]
struct Response {
token: String,
}
impl Display for Response {
fn fmt(&self,
f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.token)
}
}
#[derive(Serialize)]
struct Signup {
ns: String,
db: String,
ac: String,
email: String,
pass: String,
}
#[tokio::main]
async fn main() -> surrealdb::Result<()> {
let db = Surreal::new::<Ws>("127.0.0.1:8000").await?;
let response_string = ureq::post("http://127.0.0.1:8000/signup")
.header("Accept", "application/json")
.send_json(Signup {
ns: "main".to_string(),
db: "main".to_string(),
ac: "account".to_string(),
email: "info@surrealdb.com".to_string(),
pass: "123456".to_string(),
})
.unwrap()
.into_body()
.read_to_string()
.unwrap();
let response = serde_json::from_str::<Response>(&response_string).unwrap();
// Not signed in, doesn't work
println!("{:?}", db.query("CREATE cat;").await);
db.authenticate(response.token).await?;
// Now it works
println!("{:?}", db.query("CREATE cat;").await?);
Ok(())
} Refreshing a session (.refresh())
When the server issues a token that includes a refresh component, you can obtain a new access token without signing in again. Build the usual db.authenticate(token) future, then call .refresh(). The inner future runs the refresh command and returns a new Token. If the token has no refresh material, the SDK returns an error (Missing refresh token).
// Get a token from signin
let token = db.signin(credentials).await?;
// Later, refresh the token
let new_token = db.authenticate(token).refresh().await?;This pairs with the access and refresh model configured via DEFINE ACCESS (token duration, refresh behaviour, and scope depend on your statement).
The following example demonstrates how to use a refresh token for a user and how the same user is authenticated throughout:
// Two dependencies:
// cargo add surrealdb --features kv-mem tokio
use surrealdb::{
Error, Surreal,
engine::local::Mem,
opt::{Config, auth::Record},
types::{SurrealValue, ToSql, Value},
};
const NAMESPACE: &str = "ns";
const DATABASE: &str = "db";
const ACCESS: &str = "account";
const EMAIL: &str = "jane@example.com";
const PASSWORD: &str = "password123";
// What you persist across app restarts
// (access JWT stays in RAM only)
#[derive(Debug, SurrealValue)]
struct PersistedSession {
namespace: String,
database: String,
access_method: String,
refresh: String,
}
// Signin / signup credentials
// flattened into the RPC payload by `Record`
#[derive(Debug, SurrealValue)]
struct EmailPassword {
email: String,
pass: String,
}
#[derive(Debug, SurrealValue)]
struct RefreshOnly {
refresh: String,
}
fn truncate(token: &str) -> &str {
&token[..token.len().min(24)]
}
#[tokio::main]
async fn main() -> Result<(), Error> {
let db = Surreal::new::<Mem>(Config::default()).await?;
db.use_ns("ns").use_db("db").await?;
// Schema setup
db.query(
"
DEFINE ACCESS account ON DATABASE TYPE RECORD
SIGNUP (
CREATE user SET email = $email, pass = crypto::argon2::generate($pass)
)
SIGNIN (
SELECT * FROM user
WHERE email = $email AND crypto::argon2::compare(pass, $pass)
)
WITH REFRESH
DURATION FOR SESSION 1d FOR TOKEN 1h;
",
)
.await?
.check()?;
// First sign up a user
let _ = db
.signup(Record {
namespace: NAMESPACE.into(),
database: DATABASE.into(),
access: ACCESS.into(),
params: EmailPassword {
email: EMAIL.into(),
pass: PASSWORD.into(),
},
})
.await;
// Sign in with same email + password used to sign in
let token = db
.signin(Record {
namespace: NAMESPACE.into(),
database: DATABASE.into(),
access: ACCESS.into(),
params: EmailPassword {
email: EMAIL.into(),
pass: PASSWORD.into(),
},
})
.await?;
println!(
"Signed in.\n access JWT: {}...\n refresh JWT: {}...",
truncate(token.access.as_insecure_token()),
truncate(token.refresh.as_ref().unwrap().as_insecure_token())
);
let old_access = token.access.as_insecure_token().to_string();
// Use the access token on this connection
// (refresh is ignored here)
db.authenticate(token.access.clone()).await?;
let me = db
.query("RETURN $auth")
.await?
.take::<Option<Value>>(0)?
.into_value()
.to_sql_pretty();
println!("Authenticated as: {me:?}");
// Refresh — pass the full Token (access + refresh)
//
// Server decodes the access JWT without checking expiry
// to recover ns/db/ac/id, then validates the refresh grant.
// Old refresh is single-use so is revoked,
// and a new pair is returned.
let refreshed = db.authenticate(token).refresh().await?;
assert_ne!(old_access, refreshed.access.as_insecure_token());
println!(
"Refreshed. \n new access: {}...\n new refresh: {}...",
truncate(refreshed.access.as_insecure_token()),
truncate(refreshed.refresh.as_ref().unwrap().as_insecure_token())
);
// Persist refresh only (simulate writing to disk)
let persisted = PersistedSession {
namespace: NAMESPACE.into(),
database: DATABASE.into(),
access_method: ACCESS.into(),
refresh: refreshed
.refresh
.as_ref()
.unwrap()
.as_insecure_token()
.to_string(),
};
let json = persisted.into_value();
println!("\nPersisted session:\n{}", json.clone().to_sql_pretty());
// Simulate app restart: no access JWT in memory,
// only persisted refresh
db.invalidate().await?;
let loaded = PersistedSession::from_value(json).unwrap();
let cold_token = db
.signin(Record {
namespace: loaded.namespace,
database: loaded.database,
access: loaded.access_method,
params: RefreshOnly {
refresh: loaded.refresh,
},
})
.await?;
db.authenticate(cold_token.access.clone()).await?;
let me_again = db
.query("RETURN $auth")
.await?
.take::<Option<Value>>(0)?
.unwrap()
.to_sql_pretty();
println!("After cold-start refresh signin: {me_again:?}");
// Refresh again on the warm path
let _ = db.authenticate(cold_token).refresh().await?;
println!("Second refresh OK.");
Ok(())
}