subgraph/graphql/schema/create_auth_service/start_register/
mod.rs1use async_graphql::{
2 dynamic::{Field, FieldFuture, InputValue, TypeRef},
3 Value,
4};
5use bson::{doc, Bson};
6use log::{debug, error, trace};
7
8use crate::{
9 configuration::subgraph::data_sources::sql::DialectEnum,
10 data_sources::{sql::PoolEnum, DataSource, DataSources},
11 graphql::schema::ServiceSchema,
12};
13
14impl ServiceSchema {
15 pub fn create_register_start(mut self) -> Self {
16 debug!("Creating register start");
17 let auth_config = match self.subgraph_config.service.auth.clone() {
18 Some(auth) => auth,
19 None => {
20 panic!("Auth config not found.");
21 }
22 };
23
24 let resolver = Field::new(
25 "register_start",
26 TypeRef::named_nn(TypeRef::STRING),
27 move |ctx| {
28 debug!("Resolving register start");
29 let auth_config = auth_config.clone();
30
31 FieldFuture::new(async move {
32 let identifier = match ctx.args.try_get("identifier") {
33 Ok(input) => input
34 .deserialize::<String>().map_err(|e| {
35 error!("Failed to get input: {:?}", e);
36 async_graphql::Error::new(format!("Failed to get input: {:?}", e))
37 })?,
38 Err(e) => {
39 return Err(async_graphql::Error::new(format!(
40 "Failed to get input: {:?}",
41 e
42 )))
43 }
44 };
45
46 let data_sources = ctx.data_unchecked::<DataSources>();
48 let data_source = DataSources::get_data_source_by_name(
49 &data_sources,
50 &auth_config.data_source,
51 );
52
53 let user = ServiceSchema::get_user(&data_source, &identifier).await;
55
56 if !user.is_err() && user.clone().unwrap().clone().is_some() {
57 if user.clone().unwrap().unwrap().passkey.is_some() {
58 error!("User already exists: {:?}", &identifier);
59 return Err(async_graphql::Error::new(format!(
60 "User already exists: {:?}",
61 &identifier
62 )));
63 }else {
64 ServiceSchema::delete_user(&data_source, &identifier).await?;
65 }
66 }
67
68 trace!("Creating webauthn service");
69
70 let webauthn = ServiceSchema::build_webauthn(&auth_config).map_err(|e| {
71 error!("Failed to build webauthn: {:?}", e);
72 async_graphql::Error::new(format!("Failed to build webauthn: {:?}", e))
73 })?;
74
75 let user_uuid = uuid::Uuid::new_v4();
76
77 let (ccr, reg_state) = webauthn.start_passkey_registration(
78 user_uuid.clone(),
79 &identifier,
80 &identifier,
81 None,
82 ).map_err(|e| {
83 error!("Failed to start passkey registration: {:?}", e);
84 async_graphql::Error::new(format!(
85 "Failed to start passkey registration: {:?}",
86 e
87 ))
88 })?;
89
90 let reg_state = match serde_json::to_string(®_state) {
91 Ok(reg_state) => reg_state,
92 Err(e) => {
93 return Err(async_graphql::Error::new(format!(
94 "Failed to serialize registration state: {}",
95 e
96 )))
97 }
98 };
99
100 let user_uuid_string = user_uuid.to_string();
101
102 match &data_source {
104 DataSource::Mongo(mongo_ds) => {
105 let user = doc! {
106 "uuid": user_uuid_string.to_string(),
107 "identifier": identifier.clone(),
108 "registration_state": ®_state,
109 "authentication_state": Bson::Null,
110 "passkey": Bson::Null,
111 };
112
113 mongo_ds
114 .db
115 .collection("subgraph_user")
116 .insert_one(user, None)
117 .await?;
118 }
119 DataSource::SQL(sql_ds) => {
120 match sql_ds.config.dialect {
121 DialectEnum::MYSQL => {
122 let query = sqlx::query("INSERT INTO subgraph_user (uuid, identifier, registration_state) VALUES (?, ?, ?);")
123 .bind(&user_uuid_string)
124 .bind(&identifier)
125 .bind(®_state);
126 match sql_ds.pool.clone() {
127 PoolEnum::MySql(pool) => {
128 query.execute(&pool).await?;
129 }
130 _ => unreachable!(),
131 };
132 }
133 DialectEnum::SQLITE => {
134 let query = sqlx::query("INSERT INTO subgraph_user (uuid, identifier, registration_state) VALUES (?, ?, ?);")
135 .bind(&user_uuid_string)
136 .bind(&identifier)
137 .bind(®_state);
138 match sql_ds.pool.clone() {
139 PoolEnum::SqLite(pool) => {
140 query.execute(&pool).await?;
141 }
142 _ => unreachable!(),
143 };
144 }
145 DialectEnum::POSTGRES => {
146 let query = sqlx::query("INSERT INTO subgraph_user (uuid, identifier, registration_state) VALUES ($1, $2, $3);")
147 .bind(&user_uuid)
148 .bind(&identifier)
149 .bind(®_state);
150 match sql_ds.pool.clone() {
151 PoolEnum::Postgres(pool) => {
152 query.execute(&pool).await?;
153 }
154 _ => unreachable!(),
155 };
156 }
157 };
158 }
159 _ => panic!("Data Source not supported."),
160 };
161
162 trace!("Challenge created: {:?}", ccr);
163 trace!("Registration state created: {:?}", reg_state);
164
165 let json = match serde_json::to_value(&ccr) {
166 Ok(json) => json,
167 Err(e) => {
168 return Err(async_graphql::Error::new(format!(
169 "Failed to serialize challenge: {}",
170 e
171 )))
172 }
173 };
174
175 let value = Value::from_json(json);
176
177 match value {
178 Ok(value) => Ok(Some(value)),
179 Err(_) => Err(async_graphql::Error::new("Failed to resolve challenge.")),
180 }
181 })
182 },
183 )
184 .argument(InputValue::new(
185 "identifier",
186 TypeRef::named_nn(TypeRef::STRING),
187 ));
188
189 self.mutation = self.mutation.field(resolver);
190 self
191 }
192}