Rust SurrealDB – JWT Auth

Testing Rust code for “signup” and checking the token. Add Scope and then you can create the JWT. Watch the YouTube video here:

https://youtu.be/e4a8lOT_Gxg

https://surrealdb.com

surreal-jwt
❯ surreal start --auth -u root -p root --deny-guests --no-banner
[package]
name = "sjwt"
version = "0.1.0"
edition = "2021"

[dependencies]
reqwest = "0.12.7"
serde = { version = "1.0.210", features = ["derive"] }
surrealdb = "1.5.4"
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
use serde::Serialize;
use std::fs::File;
use std::io::{self, Write};
use surrealdb::engine::remote::ws::{Client, Ws};
use surrealdb::opt::auth::{Root, Scope};
use surrealdb::Response;
use surrealdb::Surreal;

#[derive(Serialize)]
struct Credentials<'a> {
    email: &'a str,
    pass: &'a str,
}

#[tokio::main]
async fn main() -> surrealdb::Result<()> {
    let db = Surreal::new::<Ws>("127.0.0.1:8000").await?;

    db.use_ns("test").use_db("test").await?;

    db.signin(Root {
        username: "root",
        password: "root",
    })
    .await?;

    // Create the scope for the user
    create_scope(&db).await?;

    println!("Scope created successfully!");


        // Get user input
    let (email, password) = get_user_credentials().await.unwrap();

    // sign up to get JWT
    let jwt = db
        .signup(Scope {
            namespace: "test",
            database: "test",
            scope: "admin",
            params: Credentials {
                email : &email,
                pass: &password,
            },
        })
        .await?;

    let token = jwt.as_insecure_token();
    println!("JWT Token: {:?}", token);

    // Write the token to a file
    let file = File::create("token.txt");
    match writeln!(file.expect("file"), "{}", token) {
        Ok(_) => println!("Token has been written to token.txt"),
        Err(e) => eprintln!("Failed to write token to file: {}", e),
    }

    // Perform a query
    let query = r#"
        SELECT * FROM user;
    "#;

    let response = perform_query(&db, query).await?;
    println!("Query Response: {:?}", response);

    Ok(())
}

// Function to create scope
async fn create_scope(db: &Surreal<Client>) -> surrealdb::Result<Response> {
    let query = r#"
        DEFINE SCOPE admin
            SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($pass) )
            SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) );
    "#;

    db.query(query).await
}


// Function to perform a query
async fn perform_query(db: &Surreal<Client>, query: &str) -> surrealdb::Result<Response> {
    let response = db.query(query).await?;
    Ok(response)
}


// Function to get user credentials
async fn get_user_credentials() -> io::Result<(String, String)> {
    let mut email = String::new();
    let mut password = String::new();

    println!("Enter email:");
    io::stdin().read_line(&mut email)?;
    let email = email.trim().to_string(); // Remove any extra whitespace/newlines

    println!("Enter password:");
    io::stdin().read_line(&mut password)?;
    let password = password.trim().to_string(); // Remove any extra whitespace/newlines

    Ok((email, password))
}
~/rust/sjwt main*
❯ cargo r
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
     Running `target/debug/sjwt`
Scope created successfully!
JWT Token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MjYwNTAwMDksIm5iZiI6MTcyNjA1MDAwOSwiZXhwIjoxNzI2MDUzNjA5LCJpc3MiOiJTdXJyZWFsREIiLCJqdGkiOiJlZTRmMTZiNi00NWJmLTRhYWQtOWYxZS03ZmJiY2JkMTlhOGEiLCJOUyI6InRlc3QiLCJEQiI6InRlc3QiLCJTQyI6InVzZXIiLCJJRCI6InVzZXI6dzV0bHV5eHhnZ3BtbXg5ZTFnZjkifQ.DySabfDoBaM5623OWyVl-8YQTW7S3eJiciESPlaUPZOTMh2e8-BwygfGNykX2s-vtYHrtqG_26B7J4z8A1deHA"

Use the bearer token created by the rust code, not the one here as it will have expired

~/rust/sjwt main*
❯ curl -vvv localhost:8000/sql \
--data "SELECT * from users:test" \
-H "Accept: application/json" \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MjYwNTAwMDksIm5iZiI6MTcyNjA1MDAwOSwiZXhwIjoxNzI2MDUzNjA5LCJpc3MiOiJTdXJyZWFsREIiLCJqdGkiOiJlZTRmMTZiNi00NWJmLTRhYWQtOWYxZS03ZmJiY2JkMTlhOGEiLCJOUyI6InRlc3QiLCJEQiI6InRlc3QiLCJTQyI6InVzZXIiLCJJRCI6InVzZXI6dzV0bHV5eHhnZ3BtbXg5ZTFnZjkifQ.DySabfDoBaM5623OWyVl-8YQTW7S3eJiciESPlaUPZOTMh2e8-BwygfGNykX2s-vtYHrtqG_26B7J4z8A1deHA"
> Content-Length: 24
> Content-Type: application/x-www-form-urlencoded
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: application/json
< content-length: 47
< access-control-allow-origin: *
< vary: origin
< vary: access-control-request-method
< vary: access-control-request-headers
< surreal-version: surrealdb-1.5.4
< server: SurrealDB
< x-request-id: 51ecd565-fbc5-4690-9ae8-07e6b0f66dab
< date: Wed, 11 Sep 2024 10:06:33 GMT
< 
* Connection #0 to host localhost left intact
[{"result":[],"status":"OK","time":"38.96µs"}]%  

#     """Example of how to use the SurrealDB client."""
#     async with Surreal("ws://localhost:8000/rpc") as db:
# Run Rust code to generate the token and paste in here ->


token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MjYwNjM1ODMsIm5iZiI6MTcyNjA2MzU4MywiZXhwIjoxNzI2MDY3MTgzLCJpc3MiOiJTdXJyZWFsREIiLCJqdGkiOiJkYzJmODM3Zi1kZjYwLTQzMTAtOTY1OS0wMzFlZmZkZmY4ZDYiLCJOUyI6InRlc3QiLCJEQiI6InRlc3QiLCJTQyI6ImFkbWluIiwiSUQiOiJ1c2VyOmp4aWdvNTRycWRibGUzc2R0eHFvIn0.QW85GLXk9zXYRjU0CvMTIgNLSevtcEfsv2wYnSGwP54pgu6a13ncV1At7iF9yGgW4MCFJPztwjIdAiVF3MghOw"

from surrealdb import Surreal
import asyncio
    
async def main():
    async with Surreal("ws://localhost:8000/rpc") as db:
        print("Attempting to authenticate...")
        await db.authenticate(token)
        print("Authentication successful.")
        await db.signin({"user": "root", "pass": "root"})
        await db.use("test_namespace", "test_database")

    
        res = await db.create(
            "character",
            {
                "name": "Elvis Presley",
                "race": "human",
            },
        )
        print(res)
    
        res = await db.create(
            "character",
            {
                "name": "Dennis the Menace",
                "race": "human",
            },
        )
        print(res)
    
        res = await db.create(
            "character",
            {
                "name": "Boss Hogg",
                "race": "Sheriff",
            },
        )
        print(res)
    
        res = await db.select("character")
        print(res)
    
        res = await db.query("SELECT * FROM character")
        print(res)
    
        res = await db.query("SELECT * FROM character WHERE race='human'")
        print(res)
    
        res = await db.query("SELECT * FROM character WHERE race=$race", {
            'race': "Sheriff",
        })
        print(res)
    
        #res = await db.update("character", {
        #    "race": "Sheriff",
        #})
    
        res = await db.query("UPDATE character SET race=$race WHERE name=$name", {
            'race': "Sheriff",
            'name': "Rosco P Coltrane"
        })
        print(res)
    
        res = await db.select("character")
        print(res)
    
        res = await db.delete("character")
        print(res)
    
    
asyncio.run(main())

WEBDOCK Linux VPS Hosting

Previous article

Factory Pattern in Rust