You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi team - the following is off the back of a lengthy discussion on SurrealDB's Auth capability and the current situation where "refreshing" of $token is not (yet) available.
Original Discord discussion: https://discord.com/channels/902568124350599239/1303884244313182238
I think I've formulated a solution that achieves the same effect as a Refresh Token, but I'd love to know what others think!
PURPOSE:
Hopefully, I've not unwittingly created some terrifying security vulnerability or hazard in the following so I'd greatly appreciate people's reviews and comments!
NOTE:
This may have been obvious to someone more experienced in security, though it was non-obvious nor clear how to implement (at least in the first instance!) for me, despite the lengthy discussion — so perhaps this will be helpful to anyone else who is trying to achieve a similar outcome!
As @gguillemas mentioned in the thread above and in other git issues, there is a "Refresh" functionality coming soon, but this release is TBA, and I frankly just wanted to get this Auth issue closed out so that I could carry on with developing the rest of my app with some piece of mind that my Auth is DONE! [ And hopefully done right!!😄]
SITUATION:
Setting up Authentication for an App that might be similar to a social media app,
eg:
User logs in,
does multiple CRUD operations,
at sporadic intervals.
For every CRUD operation, my client backend will validate the User (with .authenticate) prior to every request made to the sdb database.
PROBLEM:
$token currently has no refresh mechanism built into SurrealDB (yet), and it's recommended to keep $token duration SHORT lived. In this setting, it would force the User to "Signin" for eg. every 15mins <<< not good.
If I don't run the authentication (db.authenticate(token)) check for each CRUD op, I have to rely on $session being valid - even if the $token was somehow stolen, or needed to destroyed, it can't be modified/invalidated externally and will continue to Validate until session duration expires.
This means we can't force "Signout a User from all devices" in case of an issue - we have to wait for $session expiry
If we use a LONG $token duration the vulnerability is that the $token, if stolen, can be used for a long time! Not good either.
So we need mechanism to compensate for this, to avoid User being forced to Signin every 15mins, or forego the ability to boot out a User (attacker with Token that is stolen)
SOLUTION:
-- MECHANISM ===========================================================-- SurrealDB $token (and $session) effectively NEVER expires (only after 10yrs)-- #1 REFRESH TOKEN (.authenticate)-- ## If User activity is within 20m of sessionLast-- Access is PASSED & update sessionLast, sessionExpires-- ## If User activity is within sessionExpires-- Access is PASSED & update sessionLast, sessionExpires-- #2 FORCE RE-SIGNIN (.authenticate)-- If User wants to "invalidate all"/"Signout from All devices"-- sessionForceSignin is set TRUE-- THEN on next AUTHENTICATE event, User with existing $token is booted out!-- THEN forced to SIGNIN again (new $token is created & sessionForceSignin is reset to FALSE)-- DEFINE ACCESS LOGIC ===========================================================
DEFINE ACCESS OVERWRITE user ON DATABASE TYPE RECORD
SIGNUP (
CREATE ONLY user SET
name = $userData.username,
role = $userData.role,
email = $userData.email,
verified = $userData.verified,
authProvider = $userData.authProvider,
password = crypto::argon2::generate($userData.password),
dateCreated =time::now()
,
sessionLast =time::now(),
sessionExpires =time::now() + 14d, --default time to force re-Signin
sessionForceSignin = false
,
profile = (CREATE ONLY profile:[$this.id, rand::ulid()] CONTENT $profile).id
)
SIGNIN ({
LET $validUser = (SELECT*FROM user
WHERE email = $email
AND crypto::argon2::compare(password, $password)
);
-- RETURN $validUser
IF (!$validUser) {
THROW "User invalid - Please Signin again."-- email/password combo incorrect
} ELSE {
-- SIGNOUT ALL DEVICES - RESET
IF ($validUser.sessionForceSignin) {
UPDATE $validUser SET sessionForceSignin = false; -- reset
};
-- SHORT/LONG EXPIRY - RESETUPDATE $validUser.idSET
sessionLast =time::now(),
sessionExpires =time::now() + 14d --extend for 14d
;
RETURN $validUser
}
})
AUTHENTICATE ({
-- SIGNOUT ALL DEVICES - TRIGGER
IF ($auth.sessionForceSignin) {
UPDATE $auth SET sessionForceSignin = false; -- reset
THROW "User must Signin again."-- force re-Signin
} ELSE
-- SHORT EXPIRY (not required)-- If current action is within 20m of last action:-- PASS & update sessionLast
IF (time::now() <= $auth.sessionLast+ 20m){ -- SHORT not yet expiredUPDATE $auth.idSET
sessionLast =time::now(),
sessionExpires =time::now() + 14d; --extend for 14d
RETURN $auth;
} ELSE
-- LONG EXPIRY-- Consider long duration since activity.-- If current action is within expiry period:-- PASS & update sessionLast & extend sessionExpires
IF (time::now() <= $auth.sessionExpires){ -- LONG not yet expiredUPDATE $auth.idSET
sessionLast =time::now(),
sessionExpires =time::now() + 14d; --extend for 14d
RETURN $auth;
} ELSE {
THROW "User session expired - Please Signin again."-- session expired
}
})
DURATION FOR TOKEN 10y, FOR SESSION 1y;
In hindsight, the setup of SHORT EXPIRY / 20m / using sessionLast is perhaps not necessary... Maybe if I wanted additional controls for a "Shorter Expiry" I'd throw those functions inside there🤔
BTW, to test this in my client, I used these functions to trigger different things and review results in Surrealist:
sessionStatus: async()=>{constres=awaitSdb.query(/*surql*/` SELECT * FROM $session; SELECT * FROM $token; SELECT * FROM $auth; `)console.log('sessionStatus▶',JSON.stringify(res,null,2));},authenticateAgain: async(event)=>{consttoken=event.cookies.get(COOKIE_NAME_SESSION);constres=awaitSdb.authenticate(token||"")console.log('authenticateAgain▶',JSON.stringify(res,null,2));},signOutAllDevices: async()=>{constres=awaitSdb.query(/*surql*/` UPDATE $auth SET sessionForceSignin = true ;`)console.log('signOutAllDevices▶',JSON.stringify(res,null,2));},
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hi team - the following is off the back of a lengthy discussion on SurrealDB's Auth capability and the current situation where "refreshing" of $token is not (yet) available.
Original Discord discussion: https://discord.com/channels/902568124350599239/1303884244313182238
I think I've formulated a solution that achieves the same effect as a Refresh Token, but I'd love to know what others think!
PURPOSE:
Hopefully, I've not unwittingly created some terrifying security vulnerability or hazard in the following so I'd greatly appreciate people's reviews and comments!
NOTE:
SITUATION:
Setting up Authentication for an App that might be similar to a social media app,
eg:
.authenticate
) prior to every request made to the sdb database.PROBLEM:
db.authenticate(token)
) check for each CRUD op, I have to rely on $session being valid - even if the $token was somehow stolen, or needed to destroyed, it can't be modified/invalidated externally and will continue to Validate until session duration expires.SOLUTION:
In hindsight, the setup of SHORT EXPIRY / 20m / using
sessionLast
is perhaps not necessary... Maybe if I wanted additional controls for a "Shorter Expiry" I'd throw those functions inside there🤔BTW, to test this in my client, I used these functions to trigger different things and review results in Surrealist:
Link to this discussion (if continued) in discord: https://discord.com/channels/902568124350599239/1018618253695795261/1306132221203185705
Beta Was this translation helpful? Give feedback.
All reactions