Skip to content

Instantly share code, notes, and snippets.

@koakh
Last active April 6, 2024 02:04
Show Gist options
  • Star 40 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save koakh/fbbc37cde630bedcf57acfd4d6a6956b to your computer and use it in GitHub Desktop.
Save koakh/fbbc37cde630bedcf57acfd4d6a6956b to your computer and use it in GitHub Desktop.
SurrealDB : How to use signin and signup (server + client nodesjs)
**As a developer you have complete control over the signup and signin functionality...**
```shell
$ surreal sql --conn http://localhost:8000 --user root --pass root --ns test --db test
```
```sql
-- optional : this is the default see --ns test --db test start server flags
-- USE NS test DB test;
-- define TABLE user SCHEMAFULL PERMISSIONS FOR select, update WHERE id = $auth.id FOR create, delete NONE; // miss ,
-- Tobie: However, make it schemaless, as you haven't defined fields.
-- SCHEMAFULL if we defined fields else use SCHEMALESS
---opt1:SCHEMALESS
-- DEFINE TABLE user SCHEMALESS
-- PERMISSIONS
-- FOR select, update WHERE id = $auth.id,
-- FOR create, delete NONE;
-- warn with opt1 if we re signup again it will create another user with same email, and after it all signin's will fail,
-- to fix this issue always use next opt2 that have a UNIQUE INDEX, and prevents create a user with same email, and this is the expected behavior
---opt2:SCHEMAFULL
DEFINE TABLE user SCHEMAFULL
PERMISSIONS
FOR select, update WHERE id = $auth.id,
FOR create, delete NONE;
DEFINE FIELD user ON user TYPE string;
DEFINE FIELD pass ON user TYPE string;
DEFINE FIELD settings.* ON user TYPE object;
DEFINE FIELD settings.marketing ON user TYPE string;
DEFINE FIELD tags ON user TYPE array;
DEFINE INDEX idx_user ON user COLUMNS user UNIQUE;
-- define scope
DEFINE SCOPE allusers
-- the JWT session will be valid for 14 days
SESSION 14d
-- The optional SIGNUP clause will be run when calling the signup method for this scope
-- It is designed to create or add a new record to the database.
-- If set, it needs to return a record or a record id
-- The variables can be passed in to the signin method
SIGNUP ( CREATE user SET settings.marketing = $marketing, user = $user, pass = crypto::argon2::generate($pass), tags = $tags )
-- The optional SIGNIN clause will be run when calling the signin method for this scope
-- It is designed to check if a record exists in the database.
-- If set, it needs to return a record or a record id
-- The variables can be passed in to the signin method
SIGNIN ( SELECT * FROM user WHERE user = $user AND crypto::argon2::compare(pass, $pass) )
-- this optional clause will be run when calling the signup method for this scope
```
ctrl+c
check info db in shell
```shell
$ echo "INFO FOR DB;" | surreal sql --conn http://localhost:8000 --user root --pass root --ns test --db test | jq
```
check info db in REPL
```shell
$ surreal sql --conn http://localhost:8000 --user root --pass root --ns test --db test --pretty
```
```sql
INFO FOR DB;
```
```json
[
{
"time": "249.753µs",
"status": "OK",
"result": {
"dl": {},
"dt": {},
"sc": {
"allusers": "DEFINE SCOPE allusers SESSION 2w SIGNUP (CREATE user SET settings.marketing = $marketing, email = $email, pass = crypto::argon2::generate($pass), tags = $tags) SIGNIN (SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass))"
},
"tb": {
"user": "DEFINE TABLE user SCHEMALESS PERMISSIONS FOR select WHERE id = $auth.id, FOR create NONE, FOR update WHERE id = $auth.id, FOR delete NONE"
}
}
}
]
```
Then you could use the **following query from the client to signup (create) a new user**, or **signin (select) an existing user**...
```js
db.signup({
NS: 'my_ns',
DB: 'my_db',
SC: 'allusers', // We want to signup to the 'allusers' scope defined above
email: 'test@acme.com', // We can add any variable here to use in the SIGNUP clause
pass: 'some password', // We can add any variable here to use in the SIGNUP clause
marketing: true, // We can add any variable here to use in the SIGNUP clause
tags: ['rust', 'golang', 'javascript'], // We can add any variable here to use in the SIGNUP clause
});
// you should receive a jwt token
db.signin({
NS: 'my_ns',
DB: 'my_db',
SC: 'allusers', // We want to signup to the 'allusers' scope defined above
email: 'test@acme.com', // We can add any variable here to use in the SIGNUP clause
pass: 'some password', // We can add any variable here to use in the SIGNUP clause
});
// you should receive a jwt token
```
@hgoona
Copy link

hgoona commented Jan 17, 2023

I'm getting my head around scopes still and how to use them. I can see SC: allusers written into the request for Signin and Signup which is using the Root user and password, but what happens if someone uses those priveledges of Root and changes the SC: to something like an admin scope?

How is that prevented on the front-end app?? I'm a bit confused what parts need to be exposed and what parts are safe..

@kubeworkz
Copy link

kubeworkz commented Apr 17, 2023

@dustsucker did you ever get dynamic namespace/database creation working? I also need to create a namespace/database for each new user logging into the system. It seems like a trivial exercise but I can't seem to find a clear solution to this. Basically, a login flow like Supabase. Login -> Create a Project (new namespace/db in our case) and you're in. Extra bonus points for building a connection string so people can access SDB from the net.

@bitdom8
Copy link

bitdom8 commented Jun 29, 2023

@koakh hi, can you help us with argon2 https://github.com/orgs/surrealdb/discussions/2196#discussioncomment-6314556

Thanks for the great guidance

@pkastoori
Copy link

pkastoori commented Jul 4, 2023

I am using the signup function and providing a username and password. After that I use the signin function with the same username but with the a different password, I was expecting an error but I still got a token also calling the authenticate function after that did not throw an error. How is this possible? What am I doing wrong?

await db.signup({
NS: "mydb",
DB: "mydb",
SC: "allusers",
user: "jane",
password: "jane",
email: "jane@gmail.com",
});

const token = await db.signin({
NS: "mydb",
DB: "mydb",
SC: "allusers",
user: "jane",
password: "jane1",
});

await db.authenticate(token)

All of this still works even though I give an incorrect pass while signin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment