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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
use api_core::models::user::User;
use async_session::{Session, serde_json};
use oauth2::{AuthorizationCode, CsrfToken, Scope, TokenResponse};
use redis::AsyncCommands;
use serde::de::DeserializeOwned;
use sh_util::cache::{CacheKey, RedisManager};
use crate::{BasicClient, CSRF_TOKEN, SessionResponse, client::AuthHttpClient, error::AuthError};
pub async fn create_oauth_session(
client: &BasicClient,
cache: &RedisManager,
scopes: &[&str],
) -> Result<SessionResponse, AuthError> {
let mut builder = client.authorize_url(CsrfToken::new_random);
for pat in scopes {
builder = builder.add_scope(Scope::new(pat.to_string()));
}
let (auth_url, csrf_token) = builder.url();
let mut session = Session::new();
session.insert(CSRF_TOKEN, &csrf_token).unwrap();
let cache_key = CacheKey::Session(session.id());
let mut cache = cache.get().await.unwrap();
cache
.set::<_, _, ()>(
cache_key,
serde_json::to_string(&session).or(Err(AuthError::InvalidSession))?,
)
.await?;
let cookie = session
.into_cookie_value()
.ok_or(AuthError::MissingSession)?;
Ok(SessionResponse {
cookie_value: cookie,
auth_url,
})
}
pub async fn get_user<T>(
c: &BasicClient,
client: &AuthHttpClient,
code: &str,
_endpoint: &str,
) -> Result<User, AuthError>
where
User: TryFrom<T>,
T: DeserializeOwned,
{
// Get an auth token
let token = c
.exchange_code(AuthorizationCode::new(code.to_owned()))
.request_async(client)
.await
.map_err(|_e| AuthError::UserToken)?;
// Fetch user data from discord
let user_data: T = client
// https://discord.com/developers/docs/resources/user#get-current-user
.get("https://discordapp.com/api/users/@me")
.bearer_auth(token.access_token().secret())
.send()
.await
.map_err(|_e| AuthError::UserRetrieval)?
.json::<T>()
.await
.map_err(|_e| AuthError::UserDeserialisation)?;
User::try_from(user_data).map_err(|_e| AuthError::UserDeserialisation)
}
pub async fn validate_session(
cache: &RedisManager,
cookie: &str,
state: &str,
) -> Result<(), AuthError> {
let id = Session::id_from_cookie_value(cookie)?;
let cache_key = CacheKey::Session(&id);
let mut cache = cache.get().await.unwrap();
let session = cache.get::<_, String>(&cache_key).await?;
let session: Session =
serde_json::from_str(&session).map_err(|_e| AuthError::InvalidSession)?;
match session.validate() {
Some(session) => {
// Extract the CSRF token from the session
let stored_csrf_token = session.get::<CsrfToken>(CSRF_TOKEN);
if let Some(stored) = stored_csrf_token {
// Cleanup the CSRF token session
cache.del::<_, ()>(cache_key).await?;
// Validate CSRF token is the same as the one in the auth request
if *stored.secret() != state {
Err(AuthError::TokenMismatch)
} else {
Ok(())
}
} else {
Err(AuthError::NoCSRFToken)
}
}
None => Err(AuthError::MissingSession),
}
}
|