subgraph/graphql/schema/create_auth_service/finish_authentication/
mod.rs

1use 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}