aboutsummaryrefslogtreecommitdiffstats
path: root/src/server/driver/auth.rs
blob: 958698b1eadccee05057ed55188bc5111623c836 (plain)
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
use anyhow::Result;
use async_session::{Session, SessionStore};
use async_trait::async_trait;
use bon::Builder;
use oauth2::{AuthUrl, ClientId, ClientSecret, EndpointNotSet, EndpointSet, RedirectUrl, TokenUrl};
use secrecy::{ExposeSecret, SecretString};
use time::OffsetDateTime;
use tracing::{instrument, trace};

use crate::server::{driver::Services, entity};

pub(super) static COOKIE_NAME: &str = "SESSION";
pub(super) static CSRF_TOKEN: &str = "csrf_token";

#[derive(Builder, Debug)]
pub struct ClientOptions {
    client_id: String,
    client_secret: SecretString,
    token_url: String,
    auth_url: String,
    redirect_url: String,
}

pub type OauthClient = oauth2::basic::BasicClient<
    EndpointSet,
    EndpointNotSet,
    EndpointNotSet,
    EndpointNotSet,
    EndpointSet,
>;

pub fn oauth_client(opts: &ClientOptions) -> anyhow::Result<OauthClient> {
    dbg!(&opts);
    let redirect_url = RedirectUrl::new(opts.redirect_url.to_owned())?;
    let client_id = ClientId::new(opts.client_id.to_owned());
    let auth_url = AuthUrl::new(opts.auth_url.to_owned())?;
    let token_url = TokenUrl::new(opts.token_url.to_owned())?;
    let client_secret = ClientSecret::new(opts.client_secret.expose_secret().to_string());

    Ok(oauth2::basic::BasicClient::new(client_id)
        .set_client_secret(client_secret)
        .set_auth_uri(auth_url)
        .set_token_uri(token_url)
        .set_redirect_uri(redirect_url))
}

#[async_trait]
impl SessionStore for Services {
    #[instrument(skip(self))]
    async fn load_session(&self, cookie_value: String) -> Result<Option<Session>> {
        let id = Session::id_from_cookie_value(&cookie_value)?;

        let result = sqlx::query_as!(
            entity::auth::Session,
            "select
                *
            from
                session
            where
                    id = $1
                and
                    (
                        expires
                            is null
                        or
                            expires > $2
                    )
            ",
            id,
            OffsetDateTime::now_utc()
        )
        .fetch_optional(&self.database)
        .await?
        .map(|value| serde_json::from_str(&value.session));

        Ok(result.transpose()?)
    }

    #[instrument(skip(self, session), fields(id = session.id()))]
    async fn store_session(&self, session: Session) -> Result<Option<String>> {
        let id = session.id();
        trace!("storing session");
        let mut connection = self.database.acquire().await?;
        Ok(session.into_cookie_value())
    }

    async fn destroy_session(&self, session: Session) -> Result<()> {
        todo!()
    }

    async fn clear_store(&self) -> Result<()> {
        todo!()
    }
}