forked from
pds.dad/at-advent
this repo has no description
1use crate::session::{AxumSessionStore, FlashMessage, get_flash_message, set_flash_message};
2use crate::templates::{HtmlTemplate, login::LoginTemplate};
3use crate::{error_response, oauth_scopes};
4use atrium_api::agent::Agent;
5use atrium_oauth::{AuthorizeOptions, CallbackParams};
6use axum::{
7 extract::{Query, State},
8 http::StatusCode,
9 response::{IntoResponse, Redirect, Response},
10};
11use shared::OAuthClientType;
12
13pub async fn login_page_handler(
14 mut session: AxumSessionStore,
15) -> Result<impl IntoResponse, Response> {
16 let possible_error = match get_flash_message(&mut session, "error").await? {
17 Some(FlashMessage::Error(msg)) => Some(msg),
18 _ => None,
19 };
20 let is_logged_in = session.logged_in();
21
22 Ok(HtmlTemplate(LoginTemplate {
23 title: "at://advent - Login",
24 error: possible_error,
25 is_logged_in,
26 }))
27}
28
29#[derive(serde::Deserialize)]
30pub struct LoginParams {
31 pub handle: String,
32}
33
34pub async fn login_handle(
35 Query(params): Query<LoginParams>,
36 State(oauth_client): State<OAuthClientType>,
37 mut session: AxumSessionStore,
38) -> Result<impl IntoResponse, Response> {
39 match atrium_api::types::string::Handle::new(params.handle.to_ascii_lowercase()) {
40 Ok(handle) => {
41 match oauth_client
42 .authorize(
43 &handle,
44 AuthorizeOptions {
45 scopes: oauth_scopes(),
46 ..Default::default()
47 },
48 )
49 .await
50 {
51 Ok(url) => Ok(Redirect::to(url.as_str())),
52 Err(err) => {
53 log::error!("Error generating OAuth URL: {err}");
54 set_flash_message(
55 &mut session,
56 "error",
57 FlashMessage::Error("Error creating login URL".to_string()),
58 )
59 .await?;
60 Err(error_response(
61 StatusCode::INTERNAL_SERVER_ERROR,
62 "Error creating login URL",
63 ))
64 }
65 }
66 }
67 Err(err) => {
68 log::error!("Error parsing the handle: {err}");
69 set_flash_message(
70 &mut session,
71 "error",
72 FlashMessage::Error("Error parsing the handle".to_string()),
73 )
74 .await?;
75
76 Ok(Redirect::to("/login"))
77 }
78 }
79}
80
81///End point that takes back the OAuth call back and creates a session
82pub async fn oauth_callback_handler(
83 params: Query<CallbackParams>,
84 State(oauth_client): State<OAuthClientType>,
85 mut session: AxumSessionStore,
86) -> Response {
87 let call_back_params = CallbackParams {
88 code: params.code.clone(),
89 state: params.state.clone(),
90 iss: params.iss.clone(),
91 };
92 match oauth_client.callback(call_back_params).await {
93 Ok((bsky_session, _)) => {
94 let agent = Agent::new(bsky_session);
95 match agent.did().await {
96 Some(did) => {
97 if let Err(err) = session.set_did(did.clone().to_string()).await {
98 log::error!("Failed to write session: {err}");
99 return error_response(
100 StatusCode::INTERNAL_SERVER_ERROR,
101 "Failed to create session",
102 );
103 }
104
105 Redirect::permanent("/day/1").into_response()
106 }
107 None => error_response(StatusCode::INTERNAL_SERVER_ERROR, "No DID found"),
108 }
109 }
110 Err(err) => {
111 log::error!("OAuth callback error: {err}");
112 error_response(StatusCode::INTERNAL_SERVER_ERROR, "OAuth callback failed")
113 }
114 }
115}
116
117/// Logout handler - clears session and redirects to home
118pub async fn logout_handler(
119 mut session: AxumSessionStore,
120 State(oauth_client): State<OAuthClientType>,
121) -> Result<impl IntoResponse, Response> {
122 //TODO test this and move the shared variables to a shared struct for templates like is logged in and title
123
124 match session.get_did() {
125 None => {}
126 Some(did) => {
127 //TODO lots of unwraps
128 let did = atrium_api::types::string::Did::new(did.clone()).map_err(|err| {
129 log::error!("Failed to parse DID: {err}");
130 error_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to log out")
131 })?;
132 let client = oauth_client.restore(&did).await.map_err(|err| {
133 log::error!("Failed to restore OAuth client: {err}");
134 error_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to log out")
135 })?;
136 let agent = Agent::new(client);
137 let _ = agent.api.com.atproto.server.delete_session().await;
138 }
139 }
140
141 session.clear_session().await.map_err(|err| {
142 log::error!("Failed to clear session: {err}");
143 error_response(StatusCode::INTERNAL_SERVER_ERROR, "Failed to log out")
144 })?;
145
146 Ok(Redirect::to("/"))
147}