From a7444a7c8effffd0e0298248d68ea6173223ab0d Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Sat, 17 Sep 2022 02:52:07 +0100 Subject: [PATCH] Add authentication token claims data to session object Closes #135 --- lib/src/dbs/session.rs | 11 +++++++---- src/iam/mod.rs | 1 + src/iam/parse.rs | 15 +++++++++++++++ src/iam/signin.rs | 28 ++++++++++++++++++++-------- src/iam/signup.rs | 5 ++++- src/iam/token.rs | 36 ++++++++++++++++++++++++++++++++++++ src/iam/verify.rs | 8 ++++++++ 7 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 src/iam/parse.rs diff --git a/lib/src/dbs/session.rs b/lib/src/dbs/session.rs index 36c77dd0..954482bb 100644 --- a/lib/src/dbs/session.rs +++ b/lib/src/dbs/session.rs @@ -22,6 +22,8 @@ pub struct Session { pub db: Option, /// The currently selected authentication scope pub sc: Option, + /// The current scope authentication token + pub tk: Option, /// The current scope authentication data pub sd: Option, } @@ -101,13 +103,14 @@ impl Session { // Add session value let key = String::from("session"); let val: Value = Value::from(map! { - "ip".to_string() => self.ip.to_owned().into(), - "or".to_string() => self.or.to_owned().into(), - "id".to_string() => self.id.to_owned().into(), - "ns".to_string() => self.ns.to_owned().into(), "db".to_string() => self.db.to_owned().into(), + "id".to_string() => self.id.to_owned().into(), + "ip".to_string() => self.ip.to_owned().into(), + "ns".to_string() => self.ns.to_owned().into(), + "or".to_string() => self.or.to_owned().into(), "sc".to_string() => self.sc.to_owned().into(), "sd".to_string() => self.sd.to_owned().into(), + "tk".to_string() => self.tk.to_owned().into(), }); ctx.add_value(key, val); // Output context diff --git a/src/iam/mod.rs b/src/iam/mod.rs index c77229c5..eceeb2c6 100644 --- a/src/iam/mod.rs +++ b/src/iam/mod.rs @@ -1,4 +1,5 @@ pub mod clear; +pub mod parse; pub mod signin; pub mod signup; pub mod token; diff --git a/src/iam/parse.rs b/src/iam/parse.rs new file mode 100644 index 00000000..f9342698 --- /dev/null +++ b/src/iam/parse.rs @@ -0,0 +1,15 @@ +use crate::err::Error; +use std::str; +use surrealdb::sql::json; +use surrealdb::sql::Value; + +pub fn parse(value: &str) -> Result { + // Extract the middle part of the token + let value = value.splitn(3, '.').skip(1).take(1).next().ok_or(Error::InvalidAuth)?; + // Decode the base64 token data content + let value = base64::decode(value).map_err(|_| Error::InvalidAuth)?; + // Convert the decoded data to a string + let value = str::from_utf8(&value).map_err(|_| Error::InvalidAuth)?; + // Parse the token data into SurrealQL + json(value).map_err(|_| Error::InvalidAuth) +} diff --git a/src/iam/signin.rs b/src/iam/signin.rs index 48b0e6ce..7b73fb74 100644 --- a/src/iam/signin.rs +++ b/src/iam/signin.rs @@ -144,14 +144,17 @@ pub async fn sc( id: Some(rid.to_raw()), ..Claims::default() }; + // Create the authentication token + let enc = encode(&*HEADER, &val, &key); // Set the authentication on the session + session.tk = Some(val.into()); session.ns = Some(ns.to_owned()); session.db = Some(db.to_owned()); session.sc = Some(sc.to_owned()); session.sd = Some(Value::from(rid)); session.au = Arc::new(Auth::Sc(ns, db, sc)); - // Create the authentication token - match encode(&*HEADER, &val, &key) { + // Check the authentication token + match enc { // The auth token was created successfully Ok(tk) => Ok(tk), // There was an error creating the token @@ -206,10 +209,15 @@ pub async fn db( id: Some(user), ..Claims::default() }; - // Set the authentication on the session - session.au = Arc::new(Auth::Db(ns, db)); // Create the authentication token - match encode(&*HEADER, &val, &key) { + let enc = encode(&*HEADER, &val, &key); + // Set the authentication on the session + session.tk = Some(val.into()); + session.ns = Some(ns.to_owned()); + session.db = Some(db.to_owned()); + session.au = Arc::new(Auth::Db(ns, db)); + // Check the authentication token + match enc { // The auth token was created successfully Ok(tk) => Ok(tk), // There was an error creating the token @@ -255,10 +263,14 @@ pub async fn ns( id: Some(user), ..Claims::default() }; - // Set the authentication on the session - session.au = Arc::new(Auth::Ns(ns)); // Create the authentication token - match encode(&*HEADER, &val, &key) { + let enc = encode(&*HEADER, &val, &key); + // Set the authentication on the session + session.tk = Some(val.into()); + session.ns = Some(ns.to_owned()); + session.au = Arc::new(Auth::Ns(ns)); + // Check the authentication token + match enc { // The auth token was created successfully Ok(tk) => Ok(tk), // There was an error creating the token diff --git a/src/iam/signup.rs b/src/iam/signup.rs index 209f09fe..ecf0d7fe 100644 --- a/src/iam/signup.rs +++ b/src/iam/signup.rs @@ -79,14 +79,17 @@ pub async fn sc( id: Some(rid.to_raw()), ..Claims::default() }; + // Create the authentication token + let enc = encode(&*HEADER, &val, &key); // Set the authentication on the session + session.tk = Some(val.into()); session.ns = Some(ns.to_owned()); session.db = Some(db.to_owned()); session.sc = Some(sc.to_owned()); session.sd = Some(Value::from(rid)); session.au = Arc::new(Auth::Sc(ns, db, sc)); // Create the authentication token - match encode(&*HEADER, &val, &key) { + match enc { // The auth token was created successfully Ok(tk) => Ok(tk), // There was an error creating the token diff --git a/src/iam/token.rs b/src/iam/token.rs index 09324c6f..835dd60b 100644 --- a/src/iam/token.rs +++ b/src/iam/token.rs @@ -1,6 +1,8 @@ use jsonwebtoken::{Algorithm, Header}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; +use surrealdb::sql::Object; +use surrealdb::sql::Value; pub static HEADER: Lazy
= Lazy::new(|| Header::new(Algorithm::HS512)); @@ -36,3 +38,37 @@ pub struct Claims { #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, } + +impl From for Value { + fn from(v: Claims) -> Value { + // Set default value + let mut out = Object::default(); + // Add default fields + out.insert("iat".to_string(), v.iat.into()); + out.insert("nbf".to_string(), v.nbf.into()); + out.insert("exp".to_string(), v.exp.into()); + out.insert("iss".to_string(), v.iss.into()); + // Add NS field if set + if let Some(ns) = v.ns { + out.insert("NS".to_string(), ns.into()); + } + // Add DB field if set + if let Some(db) = v.db { + out.insert("DB".to_string(), db.into()); + } + // Add SC field if set + if let Some(sc) = v.sc { + out.insert("SC".to_string(), sc.into()); + } + // Add TK field if set + if let Some(tk) = v.tk { + out.insert("TK".to_string(), tk.into()); + } + // Add NS field if set + if let Some(id) = v.id { + out.insert("ID".to_string(), id.into()); + } + // Return value + out.into() + } +} diff --git a/src/iam/verify.rs b/src/iam/verify.rs index bef998fe..889a12b6 100644 --- a/src/iam/verify.rs +++ b/src/iam/verify.rs @@ -156,6 +156,8 @@ pub async fn token(session: &mut Session, auth: String) -> Result<(), Error> { let kvs = DB.get().unwrap(); // Decode the token without verifying let token = decode::(auth, &KEY, &DUD)?; + // Parse the token and catch any errors + let value = super::parse::parse(auth)?; // Check the token authentication claims match token.claims { // Check if this is scope token authentication @@ -181,6 +183,7 @@ pub async fn token(session: &mut Session, auth: String) -> Result<(), Error> { // Log the success debug!(target: LOG, "Authenticated to scope `{}` with token `{}`", sc, tk); // Set the session + session.tk = Some(value); session.ns = Some(ns.to_owned()); session.db = Some(db.to_owned()); session.sc = Some(sc.to_owned()); @@ -210,6 +213,7 @@ pub async fn token(session: &mut Session, auth: String) -> Result<(), Error> { // Log the success debug!(target: LOG, "Authenticated to scope `{}`", sc); // Set the session + session.tk = Some(value); session.ns = Some(ns.to_owned()); session.db = Some(db.to_owned()); session.sc = Some(sc.to_owned()); @@ -236,6 +240,7 @@ pub async fn token(session: &mut Session, auth: String) -> Result<(), Error> { // Log the success debug!(target: LOG, "Authenticated to database `{}` with token `{}`", db, tk); // Set the session + session.tk = Some(value); session.ns = Some(ns.to_owned()); session.db = Some(db.to_owned()); session.au = Arc::new(Auth::Db(ns, db)); @@ -260,6 +265,7 @@ pub async fn token(session: &mut Session, auth: String) -> Result<(), Error> { // Log the success debug!(target: LOG, "Authenticated to database `{}` with login `{}`", db, id); // Set the session + session.tk = Some(value); session.ns = Some(ns.to_owned()); session.db = Some(db.to_owned()); session.au = Arc::new(Auth::Db(ns, db)); @@ -283,6 +289,7 @@ pub async fn token(session: &mut Session, auth: String) -> Result<(), Error> { // Log the success trace!(target: LOG, "Authenticated to namespace `{}` with token `{}`", ns, tk); // Set the session + session.tk = Some(value); session.ns = Some(ns.to_owned()); session.au = Arc::new(Auth::Ns(ns)); return Ok(()); @@ -305,6 +312,7 @@ pub async fn token(session: &mut Session, auth: String) -> Result<(), Error> { // Log the success trace!(target: LOG, "Authenticated to namespace `{}` with login `{}`", ns, id); // Set the session + session.tk = Some(value); session.ns = Some(ns.to_owned()); session.au = Arc::new(Auth::Ns(ns)); return Ok(());