alibi_api/plugins/email_otp/
endpoint.rs1use super::{EmailOtpPlugin, EmailOtpType};
2use crate::plugins::authentication_helpers::{JsonField, JsonFieldKind};
3use crate::plugins::endpoint::{definition, validate_fields};
4use alibi_core::endpoint::{
5 EndpointCall, EndpointDefinition, EndpointInput, EndpointResponse, ServerEndpoint,
6};
7use alibi_core::utils::json::JsValue;
8use alibi_core::{AuthContext, AuthResult, AuthSchema, HttpMethod};
9use serde::Deserialize;
10
11#[derive(Debug, Deserialize)]
12pub struct EmailOtpRead {
13 pub otp: Option<String>,
14}
15
16const FIELDS: &[JsonField] = &[
17 JsonField::string("email", true),
18 JsonField {
19 name: "type",
20 kind: JsonFieldKind::OneOf(&[
21 "email-verification",
22 "sign-in",
23 "forget-password",
24 "change-email",
25 ]),
26 required: true,
27 },
28];
29
30pub(super) fn definitions() -> Vec<EndpointDefinition> {
31 vec![
32 definition(
33 "createVerificationOTP",
34 "createEmailVerificationOTP",
35 None,
36 HttpMethod::Post,
37 ),
38 definition(
39 "getVerificationOTP",
40 "getEmailVerificationOTP",
41 None,
42 HttpMethod::Get,
43 ),
44 ]
45}
46
47pub(super) fn validate(call: &EndpointCall) -> AuthResult<EndpointInput> {
48 if call.operation_id() == "createEmailVerificationOTP" {
49 Ok(EndpointInput {
50 body: Some(validate_fields(call.body(), "body", FIELDS)?),
51 query: call.query().cloned(),
52 })
53 } else {
54 Ok(EndpointInput {
55 body: call.body().cloned(),
56 query: Some(validate_fields(call.query(), "query", FIELDS)?),
57 })
58 }
59}
60
61impl EmailOtpPlugin {
62 #[must_use]
64 pub fn create_verification_otp_endpoint(
65 email: impl Into<String>,
66 otp_type: EmailOtpType,
67 ) -> ServerEndpoint<String> {
68 ServerEndpoint::new("email-otp", "createVerificationOTP")
69 .with_body_value(input(email.into(), otp_type))
70 }
71
72 #[must_use]
74 pub fn get_verification_otp_endpoint(
75 email: impl Into<String>,
76 otp_type: EmailOtpType,
77 ) -> ServerEndpoint<EmailOtpRead> {
78 ServerEndpoint::new("email-otp", "getVerificationOTP")
79 .with_query_value(input(email.into(), otp_type))
80 }
81
82 pub(super) async fn call_endpoint(
83 &self,
84 call: &EndpointCall,
85 ctx: &AuthContext<impl AuthSchema>,
86 ) -> AuthResult<EndpointResponse> {
87 let (email, otp_type) = if call.operation_id() == "createEmailVerificationOTP" {
88 let body: super::types::SendRequest = call.body_as()?;
89 (body.email, body.otp_type)
90 } else {
91 let query: super::types::SendRequest = call.query_as()?;
92 (query.email, query.otp_type)
93 };
94 if call.operation_id() == "createEmailVerificationOTP" {
95 EndpointResponse::json(&self.create_verification_otp(ctx, &email, otp_type).await?)
96 } else {
97 EndpointResponse::json(
98 &serde_json::json!({"otp": self.get_verification_otp(ctx, &email, otp_type).await?}),
99 )
100 }
101 }
102}
103
104fn input(email: String, otp_type: EmailOtpType) -> JsValue {
105 JsValue::Object(
106 [
107 ("email".into(), JsValue::String(email)),
108 ("type".into(), JsValue::String(otp_type.as_str().into())),
109 ]
110 .into_iter()
111 .collect(),
112 )
113}