verify magic link
This commit is contained in:
parent
cd07ff5310
commit
51b6d3a85a
|
@ -6,10 +6,12 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/trifid-api/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/trifid-pki/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/dnapi-rs/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/trifid-api/trifid_api_entities/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/trifid-api/trifid_api_migration/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tfcli/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/nebula-ffi/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/sagittariusdb/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/trifid-api-old/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/trifid-api-old/trifid_api_entities/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/trifid-api-old/trifid_api_migration/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
|
|
|
@ -40,3 +40,6 @@ starttls = false
|
|||
[tokens]
|
||||
# (Required) How long should magic links be valid for, in seconds?
|
||||
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
|
|
@ -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
|
||||
);
|
|
@ -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
|
||||
);
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE session_tokens;
|
|
@ -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
|
||||
);
|
|
@ -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
|
||||
}
|
|
@ -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())
|
||||
|
|
|
@ -18,3 +18,13 @@ pub struct MagicLink {
|
|||
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
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub mod verify_magic_link;
|
|
@ -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<VerifyLinkReq>, state: Data<AppState>) -> JsonAPIResponse<VerifyLinkResp> {
|
||||
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 {}
|
||||
})
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
pub mod signup;
|
||||
pub mod auth;
|
|
@ -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,
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue