How to use signin and signup



Tutorial by Koakh.
Edit this tutorial on GitHub.



Start InMemory Server

$ surreal start --log trace --user root --pass root

Option #1: SCHEMAFULL

launch surreal REPL to connect to local SurrealDB server instance

$ surreal sql --conn http://localhost:8000 --user root --pass root --ns test --db test --pretty

now paste bellow surrealQL code block to create the schemafull scheme

---define SCHEMAFULL and PERMISSIONS
DEFINE TABLE user SCHEMAFULL
  PERMISSIONS
    FOR select, update WHERE id = $auth.id, 
    FOR create, delete NONE;
--- define FIELD's
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's
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

check info db in REPL

> INFO FOR DB;
[
  {
    "result": {
      "dl": {},
      "dt": {},
      "sc": {
        "allusers": "DEFINE SCOPE allusers SESSION 2w SIGNUP (CREATE user SET settings.marketing = $marketing, user = $user, pass = crypto::argon2::generate($pass), tags = $tags) SIGNIN (SELECT * FROM user WHERE user = $user AND crypto::argon2::compare(pass, $pass))"
      },
      "tb": {
        "user": "DEFINE TABLE user SCHEMAFULL PERMISSIONS FOR select WHERE id = $auth.id, FOR create NONE, FOR update WHERE id = $auth.id, FOR delete NONE"
      }
    },
    "status": "OK",
    "time": "153.601µs"
  }
]

Option #2 : SCHEMALESS

another nice option suggested by George Shammas / georgyo

DEFINE SCOPE allusers 
SESSION 14d
SIGNUP (
  CREATE type::thing("user", string::lowercase(string::trim($user)))
  SET pass = crypto::argon2::generate($pass)
)
SIGNIN (
  SELECT * FROM type::thing("user", string::lowercase(string::trim($user)))
  WHERE crypto::argon2::compare(pass, $pass)
)

Because we are reusing the id, the username unique constraint is enforced even in a SCHEMALESS table.

However this also means that username will not be changeable in the future as all relationships will be using the old username as the key.

check info db in REPL

> INFO FOR DB;
[
  {
    "result": {
      "dl": {},
      "dt": {},
      "sc": {
        "allusers": "DEFINE SCOPE allusers SESSION 2w SIGNUP (CREATE type::thing(\"user\", string::lowercase(string::trim($user))) SET pass = crypto::argon2::generate($pass)) SIGNIN (SELECT * FROM type::thing(\"user\", string::lowercase(string::trim($user))) WHERE crypto::argon2::compare(pass, $pass))"
      },
      "tb": {
        "user": "DEFINE TABLE user SCHEMALESS PERMISSIONS NONE"
      }
    },
    "status": "OK",
    "time": "93.978µs"
  }
]

> SELECT * FROM user;
[
  {
    "result": [
      {
        "id": "user:tobie",
        "pass": "$argon2id$v=19$m=4096,t=3,p=1$oFQ8a4HFXjfaadQcOdPlLw$MZrA6mNvtvFLuQq1GGhfrGs4wk9iflhKT2rRsZLxwE4"
      }
    ],
    "status": "OK",
    "time": "125.017µs"
  }
]

Test signin and signup with Javascript surrealdb.js integration

use following query from the client to signup a new user

db.signup({
  NS: 'my_ns',
  DB: 'my_db',
  // We want to signup to the 'allusers' scope defined above
  SC: 'allusers',
  // We can add any variable here to use in the SIGNUP clause
  user: 'tobie',
  // 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'],
});

you should receive a jwt token for ex

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2NjQyMjQxMjEsIm5iZiI6MTY2NDIyNDEyMSwiZXhwIjoxNjY1NDMzNzIxLCJpc3MiOiJTdXJyZWFsREIiLCJucyI6InRlc3QiLCJkYiI6InRlc3QiLCJzYyI6ImFsbHVzZXJzIiwiaWQiOiJ1c2VyOmQ4dzR4aHZtY2hxcHh6enl5ZjUyIn0.TWjIEm8z2TeuE27uJ9MvgfCELvOT0hC6e8vlOPUNJMx-lvxYXIYgXXcibdVxEWNQqnXu1gaUOus4KgjsWaUD-A

and signin an existing user

db.signin({
  NS: 'my_ns',
  DB: 'my_db',
  // We want to signup to the 'allusers' scope defined above
  SC: 'allusers',
  // We can add any variable here to use in the SIGNUP clause
  user: 'tobie',
  // We can add any variable here to use in the SIGNUP clause
  pass: 'some password',
});

you should receive a jwt token for ex

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE2NjQyMjQxNjMsIm5iZiI6MTY2NDIyNDE2MywiZXhwIjoxNjY1NDMzNzYzLCJpc3MiOiJTdXJyZWFsREIiLCJucyI6InRlc3QiLCJkYiI6InRlc3QiLCJzYyI6ImFsbHVzZXJzIiwiaWQiOiJ1c2VyOmQ4dzR4aHZtY2hxcHh6enl5ZjUyIn0.kKx_WShYBf9T_sCa_pRldHTG9LyfIx-4q0V4sz2kKzxSG4b_okG-MddTwdqHnsdS7rEJxZ-Gp-iP4cpgo77kqQ