-
-
Save koakh/fbbc37cde630bedcf57acfd4d6a6956b to your computer and use it in GitHub Desktop.
**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: '[email protected]', // 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: '[email protected]', // 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 | |
``` |
Can we have an example of how to use this token? I'm getting authorization errors when trying
curl -X GET http://10.0.0.24:8000/key/:users -H 'content-type: application/json' -H 'NS: test' -H 'DB: test' -H 'SC: allusers' -H 'Authorization: '+$token
{"code":403,"details":"Authentication failed","description":"Your authentication details are invalid. Reauthenticate using valid authentication parameters.","information":"There was a problem with authentication"}
You need to prefix the token with Bearer
So your header would be
-H "Authorization: Bearer ${token}"
This is true for almost all tokens including JWT and oauth.
Helpful thanks!
You need to prefix the token with
Bearer
So your header would be-H "Authorization: Bearer ${token}"
This is true for almost all tokens including JWT and oauth.
Probably a point to make is that it needs double quotes specifically. Single quotes seems to throw a fit.
Hi, is it possible to generate JWT token in 3rd party service? Maybe i can access secret that SurrealDB uses to sign JWTs?
I don't understand the question, can elaborate?
Thanks
Of course, I am implementing an application that uses custom authentication. I receive from the client some data and its hash generated from another application, and I can validate this data using secret and send back a jwt token so after the client can interact with my application API. I want to use SurrealDB as an api/storage layer. Maybe you know if it is possible to implement it somehow? Perhaps I can inject this logic into the SurrealDB authentication flow, or somehow gain access to the secret that SurrealDB uses to sign JWT tokens, and do it using my custom service (e.g. lambda, etc.)
Here is also a pseudo-code implementation of what I'm talking about
function login(req) {
// ?query_id=AAH9mUo3AAAAAP2ZSjcRGoav
// &user={"id":927635965,"first_name":"john","last_name":"doe","username":"johndoe","language_code":"en"}
// &auth_date=1664822318
// &hash=0f559cc48df6d03f76a332a840b43e05ee62b41caf6325979eb5b31b5fb33525
const { auth_date, query_id, user, hash } = req.query;
if (auth_date && query_id && user && hash) {
const data = "auth_date=" + auth_date + "\nquery_id=" + query_id + "\nuser=" + user;
const secret = "some-secret";
// sign data with secret
const secretKeyHash = hmacSHA256(data, secret).toString();
// compare hash with secretKeyHash
if (secretKeyHash.toString() === hash) {
// find user in db
const apiUser = getUserFromDB(user);
if (apiUser == "OK") {
// return token for future API requests
const token = jwt.sign({ sub: apiUser.id }, "SomeSecretToSignJWT", { expiresIn: '1d' })
return token;
} else {
return "Some auth error";
}
}
}
How do I get a user object from a jwt token?
How do I get a user object from a jwt token?
@ralyodio it's a json web token, you can decode it here https://jwt.io/
or use a package https://www.npmjs.com/package/jsonwebtoken
hello @irishburlybear
please check this link that is updated
https://tutorials.surrealdb.com/community/03_How_to_use_signin_and_signup.html
thanks
Any way to use a third party authentication provider like google or git hub or authO
@wpitallo Have a look at https://authorizer.dev/. they will soon be supporting SurrealDB, and any help they can get would be welcome!
I have a couple of questions. I run the setup, the SQL Part seems to work fine, but with JS I get an error: “There was a problem with authentication”. First Question, do you have to be signed in as root for the signup to work? So:
db.signin({user:root,pass:root}) db.signup({ NS: 'my_ns', DB: 'my_db', SC: 'allusers', // We want to signup to the 'allusers' scope defined above email: '[email protected]', // 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 });Because in my case this didn't work either And with the namespaces, do you just need to do the SQL setup one time for all namespaces and db's ? Or is the SQL setup scoped to namespace or DB? And is there some deeper Documentation available for SurrealDB on the Documentation Page I found nothing about the setup to get signup working.
I have encountered the same problem as you did. The solution for me, as they often are, was a facepalm moment.
Are you ready?
- Solution 1: Turn off strict mode. (Do this by omitting the --strict flag when starting the server)
- Solution 2: Manually (as root) define the namespace, the database, and the table you used in the scope definition for signup and signin.
🤦
Unfortunately I don't know how to get the database to emit errors like this... if I knew rust I would be adding some pull requests, but alas at this point my skill is mostly node-based, and my focus is on my project. Anyway, I hope it helps the next one who comes here looking for:
- There was a problem with authentication
- surrealdb signup not working
- surreal signup error
and other such search terms 😸
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..
@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.
@koakh hi, can you help us with argon2 https://github.com/orgs/surrealdb/discussions/2196#discussioncomment-6314556
Thanks for the great guidance
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: "[email protected]",
});
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
This was super helpful!
Here is a slightly modified one that creates users as
user:<username>
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.