1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
use std::time::Duration;
use anyhow::Context;
use axum::{
extract::State,
http::HeaderMap,
response::{IntoResponse, Redirect},
};
use oauth2::{CsrfToken, Scope};
use reqwest::header::SET_COOKIE;
use sqlx::types::uuid;
use tower_sessions::{
SessionStore,
session::{Id, Record},
};
use crate::{
error::AppError,
server::{CSRF_TOKEN, OAUTH_CSRF_COOKIE},
state::AppHandle,
};
pub async fn discord_auth(State(state): State<AppHandle>) -> Result<impl IntoResponse, AppError> {
let (auth_url, csrf_token) = state
.discord_client
.authorize_url(CsrfToken::new_random)
.add_scope(Scope::new("identify".to_string()))
.url();
// Store the token in the session and retrieve the session cookie.
let session_id = Id(i128::from_le_bytes(uuid::Uuid::new_v4().to_bytes_le()));
let store = state.session_store.clone();
store
.create(&mut Record {
id: session_id,
data: [(
CSRF_TOKEN.to_string(),
serde_json::to_value(csrf_token).unwrap(),
)]
.into(),
expiry_date: time::OffsetDateTime::now_utc()
+ Duration::from_secs(state.local_config.oauth.session_lifespan),
})
.await
.context("failed in inserting CSRF token into session")?;
// Attach the session cookie to the response header
let cookie =
format!("{OAUTH_CSRF_COOKIE}={session_id}; SameSite=Lax; HttpOnly; Secure; Path=/");
let mut headers = HeaderMap::new();
headers.insert(
SET_COOKIE,
cookie.parse().context("failed to parse cookie")?,
);
Ok((headers, Redirect::to(auth_url.as_ref())))
}
|