subgraph/graphql/schema/create_auth_service/finish_authentication/
mod.rs1use async_graphql::{
2 dynamic::{Field, FieldFuture, FieldValue, InputValue, Object, TypeRef},
3 Value,
4};
5use biscuit_auth::{Biscuit, KeyPair};
6use log::{debug, error};
7use serde::{Deserialize, Serialize};
8use webauthn_rs::prelude::PublicKeyCredential;
9
10use crate::{data_sources::DataSources, graphql::schema::ServiceSchema};
11
12#[derive(Debug, Serialize, Deserialize, Clone)]
13pub struct AuthenticateSuccess {
14 pub token: String,
15 pub user_uuid: String,
16 pub user_identifier: String,
17}
18
19impl ServiceSchema {
20 pub fn create_authenticate_finish(mut self) -> Self {
21 debug!("Creating authenticate finish");
22
23 let auth_config = match self.subgraph_config.service.auth.clone() {
24 Some(auth) => auth,
25 None => {
26 panic!("Auth config not found.");
27 }
28 };
29
30 let resolver = Field::new(
31 "authenticate_finish",
32 TypeRef::named_nn("authenticate_success"),
33 move |ctx| {
34 debug!("Resolving authenticate finish");
35 let auth_config = auth_config.clone();
36
37 FieldFuture::new(async move {
38 let data_sources = ctx.data_unchecked::<DataSources>();
39 let data_source = DataSources::get_data_source_by_name(
40 &data_sources,
41 &auth_config.data_source,
42 );
43 let key_pair = match ctx.data_unchecked::<Option<KeyPair>>() {
44 Some(key_pair) => key_pair,
45 None => {
46 error!("Failed to get key pair.");
47 return Err(async_graphql::Error::new(format!(
48 "Failed to get key pair."
49 )));
50 }
51 };
52
53 let identifier = match ctx.args.try_get("identifier") {
54 Ok(input) => input.deserialize::<String>().map_err(|e| {
55 error!("Failed to get input: {:?}", e);
56 async_graphql::Error::new(format!("Failed to get input: {:?}", e))
57 })?,
58 Err(e) => {
59 error!("Failed to get input: {:?}", e);
60 return Err(async_graphql::Error::new(format!(
61 "Failed to get input: {:?}",
62 e
63 )));
64 }
65 };
66 let pub_key = match ctx.args.try_get("public_key") {
67 Ok(input) => input.deserialize::<String>().map_err(|e| {
68 error!("Failed to deserialize: {:?}", e);
69 async_graphql::Error::new(format!("Failed to deserialize: {:?}", e))
70 })?,
71 Err(e) => {
72 error!("Failed to get input: {:?}", e);
73 return Err(async_graphql::Error::new(format!(
74 "Failed to get input: {:?}",
75 e
76 )));
77 }
78 };
79 let pub_key: Result<PublicKeyCredential, async_graphql::Error> =
80 serde_json::from_str(&pub_key).map_err(|e| {
81 async_graphql::Error::new(format!("Failed to deserialize: {:?}", e))
82 });
83
84 let pub_key = match pub_key {
85 Ok(pk) => pk,
86 Err(error) => {
87 error!("Failed to deserialize public key: {:?}", error);
88 return Err(error);
89 }
90 };
91
92 let user = ServiceSchema::get_user(&data_source, &identifier).await?;
93
94 let user = match user {
95 Some(user) => {
96 if user.passkey.is_none() {
97 error!("User does not have a passkey.");
98 return Err(async_graphql::Error::new(format!(
99 "User does not have a passkey."
100 )));
101 };
102
103 if user.authentication_state.is_none() {
104 error!("User does not have an authentication state.");
105 return Err(async_graphql::Error::new(format!(
106 "User does not have an authentication state."
107 )));
108 };
109 user
110 }
111 None => {
112 error!("User not found.");
113 return Err(async_graphql::Error::new(format!("User not found.")));
114 }
115 };
116
117 debug!("Authenticating Config: {:?}", &auth_config);
118
119 let webauthn = ServiceSchema::build_webauthn(&auth_config).map_err(|e| {
120 error!("Failed to build webauthn: {:?}", e);
121 async_graphql::Error::new(format!("Failed to build webauthn: {:?}", e))
122 })?;
123
124 webauthn
125 .finish_passkey_authentication(
126 &pub_key,
127 &user.authentication_state.unwrap(),
128 )
129 .map_err(|e| {
130 error!("Failed to finish authentication: {:?}", e);
131 async_graphql::Error::new(format!(
132 "Failed to finish authentication: {:?}",
133 e
134 ))
135 })?;
136
137 let user_uuid = user.uuid.clone().to_string();
138
139 let mut biscuit = Biscuit::builder();
140 biscuit
141 .add_fact(format!("user(\"{}\", \"{}\")", identifier, user_uuid).as_str())
142 .map_err(|e| {
143 error!("Failed to add fact: {:?}", e);
144 async_graphql::Error::new(format!("Failed to add fact: {:?}", e))
145 })?;
146 let biscuit = biscuit.build(key_pair).map_err(|e| {
147 error!("Failed to build biscuit: {:?}", e);
148 async_graphql::Error::new(format!("Failed to build biscuit: {:?}", e))
149 })?;
150 let base64 = biscuit.to_base64().map_err(|e| {
151 error!("Failed to convert to base64: {:?}", e);
152 async_graphql::Error::new(format!("Failed to convert to base64: {:?}", e))
153 })?;
154
155 let response_value = serde_json::to_value(AuthenticateSuccess {
156 token: base64.clone(),
157 user_uuid: user.uuid.clone().to_string(),
158 user_identifier: identifier.clone(),
159 })
160 .map_err(|e| {
161 error!("Failed to serialize: {:?}", e);
162 async_graphql::Error::new(format!("Failed to serialize: {:?}", e))
163 })?;
164
165 Ok(Some(FieldValue::owned_any(response_value)))
166 })
167 },
168 )
169 .argument(InputValue::new(
170 "identifier",
171 TypeRef::named_nn(TypeRef::STRING),
172 ))
173 .argument(InputValue::new(
174 "public_key",
175 TypeRef::named_nn(TypeRef::STRING),
176 ));
177
178 let authentication_success = Object::new("authenticate_success")
179 .field(Field::new(
180 "token",
181 TypeRef::named_nn(TypeRef::STRING),
182 move |ctx| {
183 FieldFuture::new(async move {
184 let parent_value = ctx
185 .parent_value
186 .try_downcast_ref::<serde_json::Value>()
187 .map_err(|e| {
188 error!("Failed to downcast: {:?}", e);
189 async_graphql::Error::new(format!("Failed to downcast: {:?}", e))
190 })?;
191 let token = parent_value["token"].as_str().ok_or_else(|| {
192 error!("Failed to get token.");
193 async_graphql::Error::new(format!("Failed to get token."))
194 })?;
195
196 Ok(Some(Value::from(token)))
197 })
198 },
199 ))
200 .field(Field::new(
201 "user_uuid",
202 TypeRef::named_nn(TypeRef::STRING),
203 move |ctx| {
204 FieldFuture::new(async move {
205 let parent_value = ctx
206 .parent_value
207 .try_downcast_ref::<serde_json::Value>()
208 .map_err(|e| {
209 error!("Failed to downcast: {:?}", e);
210 async_graphql::Error::new(format!("Failed to downcast: {:?}", e))
211 })?;
212 let user_uuid = parent_value["user_uuid"].as_str().ok_or_else(|| {
213 async_graphql::Error::new(format!("Failed to get user_uuid."))
214 })?;
215
216 Ok(Some(Value::from(user_uuid)))
217 })
218 },
219 ))
220 .field(Field::new(
221 "user_identifier",
222 TypeRef::named_nn(TypeRef::STRING),
223 move |ctx| {
224 FieldFuture::new(async move {
225 let parent_value = ctx
226 .parent_value
227 .try_downcast_ref::<serde_json::Value>()
228 .map_err(|e| {
229 error!("Failed to downcast: {:?}", e);
230 async_graphql::Error::new(format!("Failed to downcast: {:?}", e))
231 })?;
232 let user_identifier =
233 parent_value["user_identifier"].as_str().ok_or_else(|| {
234 async_graphql::Error::new(format!("Failed to get user_identifier."))
235 })?;
236
237 Ok(Some(Value::from(user_identifier)))
238 })
239 },
240 ));
241
242 self = self.register_types(vec![authentication_success]);
243 self.mutation = self.mutation.field(resolver);
244 self
245 }
246}