From 51b6d3a85a2809494419aa6ac72adb907e4a8bea Mon Sep 17 00:00:00 2001 From: core Date: Sun, 19 Nov 2023 20:58:46 -0500 Subject: [PATCH] verify magic link --- .idea/trifid.iml | 6 +- trifid-api/config.toml | 5 +- .../2023-11-19-033954_create_users/up.sql | 5 +- .../up.sql | 7 +- .../down.sql | 1 + .../up.sql | 6 ++ trifid-api/src/config.rs | 3 +- trifid-api/src/main.rs | 1 + trifid-api/src/models.rs | 10 +++ trifid-api/src/routes/v1/auth/mod.rs | 1 + .../src/routes/v1/auth/verify_magic_link.rs | 67 +++++++++++++++++++ trifid-api/src/routes/v1/mod.rs | 3 +- trifid-api/src/schema.rs | 10 +++ 13 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 trifid-api/migrations/2023-11-20-012402_create_session_tokens/down.sql create mode 100644 trifid-api/migrations/2023-11-20-012402_create_session_tokens/up.sql create mode 100644 trifid-api/src/routes/v1/auth/mod.rs create mode 100644 trifid-api/src/routes/v1/auth/verify_magic_link.rs diff --git a/.idea/trifid.iml b/.idea/trifid.iml index bd7461a..7ec81cf 100644 --- a/.idea/trifid.iml +++ b/.idea/trifid.iml @@ -6,10 +6,12 @@ - - + + + + diff --git a/trifid-api/config.toml b/trifid-api/config.toml index 0843af7..d78483d 100644 --- a/trifid-api/config.toml +++ b/trifid-api/config.toml @@ -39,4 +39,7 @@ starttls = false # [tokens] contains options for token expiry [tokens] # (Required) How long should magic links be valid for, in seconds? -magic_link_expiry_seconds = 3600 # 1 hour \ No newline at end of file +magic_link_expiry_seconds = 3600 # 1 hour +# (Required) How long should session tokens be valid for, in seconds? This controls how long users can remain "identified" +# before they must re-identify via magic link. +session_token_expiry_seconds = 31536000 # ~1 year \ No newline at end of file diff --git a/trifid-api/migrations/2023-11-19-033954_create_users/up.sql b/trifid-api/migrations/2023-11-19-033954_create_users/up.sql index 0741c9b..4a487b7 100644 --- a/trifid-api/migrations/2023-11-19-033954_create_users/up.sql +++ b/trifid-api/migrations/2023-11-19-033954_create_users/up.sql @@ -1,4 +1,5 @@ -CREATE TABLE users ( - id VARCHAR NOT NULL PRIMARY KEY, +CREATE TABLE users +( + id VARCHAR NOT NULL PRIMARY KEY, email VARCHAR NOT NULL UNIQUE ); \ No newline at end of file diff --git a/trifid-api/migrations/2023-11-19-161415_create_magic_links/up.sql b/trifid-api/migrations/2023-11-19-161415_create_magic_links/up.sql index 6146e82..760ace7 100644 --- a/trifid-api/migrations/2023-11-19-161415_create_magic_links/up.sql +++ b/trifid-api/migrations/2023-11-19-161415_create_magic_links/up.sql @@ -1,5 +1,6 @@ -CREATE TABLE magic_links ( - id VARCHAR NOT NULL PRIMARY KEY, - user_id VARCHAR NOT NULL REFERENCES users(id) ON DELETE CASCADE, +CREATE TABLE magic_links +( + id VARCHAR NOT NULL PRIMARY KEY, + user_id VARCHAR NOT NULL REFERENCES users (id) ON DELETE CASCADE, expires TIMESTAMP NOT NULL ); \ No newline at end of file diff --git a/trifid-api/migrations/2023-11-20-012402_create_session_tokens/down.sql b/trifid-api/migrations/2023-11-20-012402_create_session_tokens/down.sql new file mode 100644 index 0000000..36a2921 --- /dev/null +++ b/trifid-api/migrations/2023-11-20-012402_create_session_tokens/down.sql @@ -0,0 +1 @@ +DROP TABLE session_tokens; \ No newline at end of file diff --git a/trifid-api/migrations/2023-11-20-012402_create_session_tokens/up.sql b/trifid-api/migrations/2023-11-20-012402_create_session_tokens/up.sql new file mode 100644 index 0000000..78e2703 --- /dev/null +++ b/trifid-api/migrations/2023-11-20-012402_create_session_tokens/up.sql @@ -0,0 +1,6 @@ +CREATE TABLE session_tokens +( + id VARCHAR NOT NULL PRIMARY KEY, + user_id VARCHAR NOT NULL REFERENCES users (id) ON DELETE CASCADE, + expires TIMESTAMP NOT NULL +); \ No newline at end of file diff --git a/trifid-api/src/config.rs b/trifid-api/src/config.rs index ef0c9e6..e9ed439 100644 --- a/trifid-api/src/config.rs +++ b/trifid-api/src/config.rs @@ -40,5 +40,6 @@ pub struct ConfigEmail { #[derive(Deserialize, Clone)] pub struct ConfigTokens { - pub magic_link_expiry_seconds: u64 + pub magic_link_expiry_seconds: u64, + pub session_token_expiry_seconds: u64 } \ No newline at end of file diff --git a/trifid-api/src/main.rs b/trifid-api/src/main.rs index f13e340..893db62 100644 --- a/trifid-api/src/main.rs +++ b/trifid-api/src/main.rs @@ -123,6 +123,7 @@ async fn main() { }) })) .service(routes::v1::signup::signup_req) + .service(routes::v1::auth::verify_magic_link::verify_link_req) .wrap(Logger::default()) .wrap(actix_cors::Cors::permissive()) .app_data(app_state.clone()) diff --git a/trifid-api/src/models.rs b/trifid-api/src/models.rs index 8250c60..3bced51 100644 --- a/trifid-api/src/models.rs +++ b/trifid-api/src/models.rs @@ -17,4 +17,14 @@ pub struct MagicLink { pub id: String, pub user_id: String, pub expires: SystemTime +} + +#[derive(Queryable, Selectable, Insertable, Identifiable, Associations, Debug, PartialEq)] +#[diesel(belongs_to(User))] +#[diesel(table_name = crate::schema::session_tokens)] +#[diesel(check_for_backend(diesel::pg::Pg))] +pub struct SessionToken { + pub id: String, + pub user_id: String, + pub expires: SystemTime } \ No newline at end of file diff --git a/trifid-api/src/routes/v1/auth/mod.rs b/trifid-api/src/routes/v1/auth/mod.rs new file mode 100644 index 0000000..06675be --- /dev/null +++ b/trifid-api/src/routes/v1/auth/mod.rs @@ -0,0 +1 @@ +pub mod verify_magic_link; \ No newline at end of file diff --git a/trifid-api/src/routes/v1/auth/verify_magic_link.rs b/trifid-api/src/routes/v1/auth/verify_magic_link.rs new file mode 100644 index 0000000..881e456 --- /dev/null +++ b/trifid-api/src/routes/v1/auth/verify_magic_link.rs @@ -0,0 +1,67 @@ +use std::time::{Duration, SystemTime}; +use actix_web::http::StatusCode; +use actix_web::post; +use actix_web::web::{Data, Json}; +use diesel::{ExpressionMethods, QueryDsl, SelectableHelper}; +use serde::{Deserialize, Serialize}; +use crate::{AppState, randid}; +use crate::models::{MagicLink, SessionToken}; +use crate::response::JsonAPIResponse; +use diesel_async::RunQueryDsl; +use crate::schema::session_tokens; + +#[derive(Deserialize)] +pub struct VerifyLinkReq { + #[serde(rename = "magicLinkToken")] + pub magic_link_token: String, +} + +#[derive(Serialize, Debug)] +pub struct VerifyLinkResp { + pub data: VerifyLinkRespData, + pub metadata: VerifyLinkRespMetadata +} +#[derive(Serialize, Debug)] +pub struct VerifyLinkRespData { + #[serde(rename = "sessionToken")] + pub session_token: String +} +#[derive(Serialize, Debug)] +pub struct VerifyLinkRespMetadata {} + +#[post("/v1/auth/verify-magic-link")] +pub async fn verify_link_req(req: Json, state: Data) -> JsonAPIResponse { + use crate::schema::magic_links::dsl::*; + + let mut conn = handle_error!(state.pool.get().await); + + let tokens = handle_error!(magic_links.filter(id.eq(&req.magic_link_token)).select(MagicLink::as_select()).load(&mut conn).await); + + let token = match tokens.get(0) { + Some(token) => token, + None => { + err!(StatusCode::BAD_REQUEST, make_err!("ERR_INVALID_MAGIC_LINK_TOKEN", "does not exist (maybe it expired?)", "magicLinkToken")) + } + }; + + if token.expires < SystemTime::now() { + err!(StatusCode::BAD_REQUEST, make_err!("ERR_INVALID_MAGIC_LINK_TOKEN", "does not exist (maybe it expired?)", "magicLinkToken")) + } + + handle_error!(diesel::delete(token).execute(&mut conn).await); + + let new_token = SessionToken { + id: randid!(token "sess"), + user_id: token.user_id.clone(), + expires: SystemTime::now() + Duration::from_secs(state.config.tokens.session_token_expiry_seconds), + }; + + handle_error!(diesel::insert_into(session_tokens::table).values(&new_token).execute(&mut conn).await); + + ok!(VerifyLinkResp { + data: VerifyLinkRespData { + session_token: new_token.id.clone() + }, + metadata: VerifyLinkRespMetadata {} + }) +} \ No newline at end of file diff --git a/trifid-api/src/routes/v1/mod.rs b/trifid-api/src/routes/v1/mod.rs index a4f0d8d..824934f 100644 --- a/trifid-api/src/routes/v1/mod.rs +++ b/trifid-api/src/routes/v1/mod.rs @@ -1 +1,2 @@ -pub mod signup; \ No newline at end of file +pub mod signup; +pub mod auth; \ No newline at end of file diff --git a/trifid-api/src/schema.rs b/trifid-api/src/schema.rs index 6e38b6a..6436c1b 100644 --- a/trifid-api/src/schema.rs +++ b/trifid-api/src/schema.rs @@ -8,6 +8,14 @@ diesel::table! { } } +diesel::table! { + session_tokens (id) { + id -> Varchar, + user_id -> Varchar, + expires -> Timestamp, + } +} + diesel::table! { users (id) { id -> Varchar, @@ -16,8 +24,10 @@ diesel::table! { } diesel::joinable!(magic_links -> users (user_id)); +diesel::joinable!(session_tokens -> users (user_id)); diesel::allow_tables_to_appear_in_same_query!( magic_links, + session_tokens, users, );