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-api/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/trifid-pki/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$/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$/tfcli/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/nebula-ffi/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" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
|
|
|
@ -40,3 +40,6 @@ starttls = false
|
||||||
[tokens]
|
[tokens]
|
||||||
# (Required) How long should magic links be valid for, in seconds?
|
# (Required) How long should magic links be valid for, in seconds?
|
||||||
magic_link_expiry_seconds = 3600 # 1 hour
|
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 (
|
CREATE TABLE users
|
||||||
|
(
|
||||||
id VARCHAR NOT NULL PRIMARY KEY,
|
id VARCHAR NOT NULL PRIMARY KEY,
|
||||||
email VARCHAR NOT NULL UNIQUE
|
email VARCHAR NOT NULL UNIQUE
|
||||||
);
|
);
|
|
@ -1,5 +1,6 @@
|
||||||
CREATE TABLE magic_links (
|
CREATE TABLE magic_links
|
||||||
|
(
|
||||||
id VARCHAR NOT NULL PRIMARY KEY,
|
id VARCHAR NOT NULL PRIMARY KEY,
|
||||||
user_id VARCHAR NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
user_id VARCHAR NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||||
expires TIMESTAMP NOT NULL
|
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)]
|
#[derive(Deserialize, Clone)]
|
||||||
pub struct ConfigTokens {
|
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::signup::signup_req)
|
||||||
|
.service(routes::v1::auth::verify_magic_link::verify_link_req)
|
||||||
.wrap(Logger::default())
|
.wrap(Logger::default())
|
||||||
.wrap(actix_cors::Cors::permissive())
|
.wrap(actix_cors::Cors::permissive())
|
||||||
.app_data(app_state.clone())
|
.app_data(app_state.clone())
|
||||||
|
|
|
@ -18,3 +18,13 @@ pub struct MagicLink {
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
pub expires: SystemTime
|
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 signup;
|
||||||
|
pub mod auth;
|
|
@ -8,6 +8,14 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
session_tokens (id) {
|
||||||
|
id -> Varchar,
|
||||||
|
user_id -> Varchar,
|
||||||
|
expires -> Timestamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
users (id) {
|
users (id) {
|
||||||
id -> Varchar,
|
id -> Varchar,
|
||||||
|
@ -16,8 +24,10 @@ diesel::table! {
|
||||||
}
|
}
|
||||||
|
|
||||||
diesel::joinable!(magic_links -> users (user_id));
|
diesel::joinable!(magic_links -> users (user_id));
|
||||||
|
diesel::joinable!(session_tokens -> users (user_id));
|
||||||
|
|
||||||
diesel::allow_tables_to_appear_in_same_query!(
|
diesel::allow_tables_to_appear_in_same_query!(
|
||||||
magic_links,
|
magic_links,
|
||||||
|
session_tokens,
|
||||||
users,
|
users,
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue