openauth_plugins/magic_link/
options.rs1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4
5use http::Request;
6use openauth_core::context::AuthContext;
7use openauth_core::error::OpenAuthError;
8use openauth_core::options::RateLimitRule;
9use serde_json::Value;
10
11use super::token::TokenStorage;
12
13pub type MagicLinkFuture<'a, T> =
14 Pin<Box<dyn Future<Output = Result<T, OpenAuthError>> + Send + 'a>>;
15
16#[derive(Debug, Clone, PartialEq)]
17pub struct MagicLinkEmail {
18 pub email: String,
19 pub url: String,
20 pub token: String,
21 pub metadata: Option<Value>,
22}
23
24#[derive(Clone, Copy)]
25pub struct MagicLinkSendContext<'a> {
26 pub context: &'a AuthContext,
27 pub request: &'a Request<Vec<u8>>,
28}
29
30pub type SendMagicLink = Arc<dyn Fn(MagicLinkEmail) -> MagicLinkFuture<'static, ()> + Send + Sync>;
31pub type SendMagicLinkWithContext = Arc<
32 dyn for<'a> Fn(MagicLinkEmail, MagicLinkSendContext<'a>) -> MagicLinkFuture<'a, ()>
33 + Send
34 + Sync,
35>;
36pub type GenerateToken = Arc<dyn for<'a> Fn(&'a str) -> MagicLinkFuture<'a, String> + Send + Sync>;
37
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct MagicLinkRateLimit {
40 pub window: u64,
41 pub max: u64,
42}
43
44impl Default for MagicLinkRateLimit {
45 fn default() -> Self {
46 Self { window: 60, max: 5 }
47 }
48}
49
50#[derive(Clone)]
51pub struct MagicLinkOptions {
52 pub(crate) expires_in: u64,
53 pub(crate) allowed_attempts: AllowedAttempts,
54 pub(crate) send_magic_link: SendMagicLinkWithContext,
55 pub(crate) disable_sign_up: bool,
56 pub(crate) rate_limit: MagicLinkRateLimit,
57 pub(crate) generate_token: Option<GenerateToken>,
58 pub(crate) store_token: TokenStorage,
59}
60
61impl MagicLinkOptions {
62 pub fn new<F>(send_magic_link: F) -> Self
63 where
64 F: Fn(MagicLinkEmail) -> MagicLinkFuture<'static, ()> + Send + Sync + 'static,
65 {
66 let send_magic_link: SendMagicLink = Arc::new(send_magic_link);
67 Self::new_with_context(move |email, _ctx| {
68 let send_magic_link = Arc::clone(&send_magic_link);
69 Box::pin(async move { send_magic_link(email).await })
70 })
71 }
72
73 pub fn new_with_context<F>(send_magic_link: F) -> Self
74 where
75 F: for<'a> Fn(MagicLinkEmail, MagicLinkSendContext<'a>) -> MagicLinkFuture<'a, ()>
76 + Send
77 + Sync
78 + 'static,
79 {
80 Self {
81 expires_in: 60 * 5,
82 allowed_attempts: AllowedAttempts::Limited(1),
83 send_magic_link: Arc::new(send_magic_link),
84 disable_sign_up: false,
85 rate_limit: MagicLinkRateLimit::default(),
86 generate_token: None,
87 store_token: TokenStorage::Plain,
88 }
89 }
90
91 #[must_use]
92 pub fn expires_in(mut self, seconds: u64) -> Self {
93 self.expires_in = seconds;
94 self
95 }
96
97 #[must_use]
98 pub fn allowed_attempts(mut self, attempts: u64) -> Self {
99 self.allowed_attempts = AllowedAttempts::Limited(attempts);
100 self
101 }
102
103 #[must_use]
104 pub fn unlimited_attempts(mut self) -> Self {
105 self.allowed_attempts = AllowedAttempts::Unlimited;
106 self
107 }
108
109 #[must_use]
110 pub fn disable_sign_up(mut self, disabled: bool) -> Self {
111 self.disable_sign_up = disabled;
112 self
113 }
114
115 #[must_use]
116 pub fn rate_limit(mut self, rate_limit: MagicLinkRateLimit) -> Self {
117 self.rate_limit = rate_limit;
118 self
119 }
120
121 #[must_use]
122 pub fn generate_token<F>(mut self, generate_token: F) -> Self
123 where
124 F: for<'a> Fn(&'a str) -> MagicLinkFuture<'a, String> + Send + Sync + 'static,
125 {
126 self.generate_token = Some(Arc::new(generate_token));
127 self
128 }
129
130 #[must_use]
131 pub fn store_token(mut self, store_token: TokenStorage) -> Self {
132 self.store_token = store_token;
133 self
134 }
135
136 pub(crate) fn rate_limit_rule(&self) -> RateLimitRule {
137 RateLimitRule {
138 window: self.rate_limit.window,
139 max: self.rate_limit.max,
140 }
141 }
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145pub(crate) enum AllowedAttempts {
146 Limited(u64),
147 Unlimited,
148}
149
150impl AllowedAttempts {
151 pub(crate) fn exceeded(self, attempt: u64) -> bool {
152 match self {
153 Self::Limited(limit) => attempt >= limit,
154 Self::Unlimited => false,
155 }
156 }
157}