1
use axum::{
2
    Extension,
3
    extract::{Request, State},
4
    http::{StatusCode, header},
5
    response::IntoResponse,
6
};
7
use axum_extra::headers::authorization::{Bearer, Credentials};
8
use log::error;
9

            
10
use sylvia_iot_corelib::{err::ErrResp, http::Json};
11

            
12
use super::{super::super::State as AppState, response};
13
use crate::models::{
14
    access_token::QueryCond as AccessTokenQueryCond, client::Client,
15
    refresh_token::QueryCond as RefreshTokenQueryCond, user::User,
16
};
17

            
18
/// `GET /{base}/api/v1/auth/tokeninfo`
19
16
pub async fn get_tokeninfo(
20
16
    Extension(user): Extension<User>,
21
16
    Extension(client): Extension<Client>,
22
16
) -> impl IntoResponse {
23
16
    Json(response::GetTokenInfo {
24
16
        data: response::GetTokenInfoData {
25
16
            user_id: user.user_id,
26
16
            account: user.account,
27
16
            name: user.name,
28
16
            roles: user.roles,
29
16
            client_id: client.client_id,
30
16
            scopes: client.scopes,
31
16
        },
32
16
    })
33
16
}
34

            
35
/// `POST /{base}/api/v1/auth/logout`
36
4
pub async fn post_logout(state: State<AppState>, req: Request) -> impl IntoResponse {
37
    const FN_NAME: &'static str = "post_logout";
38

            
39
4
    let token = match req.headers().get(header::AUTHORIZATION) {
40
        None => {
41
            return Err(ErrResp::ErrUnknown(Some(
42
                "no Authorization header".to_string(),
43
            )));
44
        }
45
4
        Some(auth) => match Bearer::decode(auth) {
46
            None => return Err(ErrResp::ErrUnknown(Some("no Bearer token".to_string()))),
47
4
            Some(token) => token.token().to_string(),
48
        },
49
    };
50

            
51
4
    let refresh_token = match state.model.access_token().get(token.as_str()).await {
52
        Err(e) => {
53
            error!("[{}] pre-clear access token error: {}", FN_NAME, e);
54
            let e = ErrResp::ErrDb(Some(format!("pre-clear access token error: {}", e)));
55
            return Err(e);
56
        }
57
4
        Ok(token) => match token {
58
            None => None,
59
4
            Some(token) => token.refresh_token,
60
        },
61
    };
62
4
    let cond = AccessTokenQueryCond {
63
4
        access_token: Some(token.as_str()),
64
4
        ..Default::default()
65
4
    };
66
4
    if let Err(e) = state.model.access_token().del(&cond).await {
67
        error!("[{}] clear access token error: {}", FN_NAME, e);
68
        let e = ErrResp::ErrDb(Some(format!("clear access token error: {}", e)));
69
        return Err(e);
70
4
    }
71
4
    if let Some(token) = refresh_token {
72
4
        let cond = RefreshTokenQueryCond {
73
4
            refresh_token: Some(token.as_str()),
74
4
            ..Default::default()
75
4
        };
76
4
        if let Err(e) = state.model.refresh_token().del(&cond).await {
77
            error!("[{}] clear refresh token error: {}", FN_NAME, e);
78
            let e = ErrResp::ErrDb(Some(format!("clear refresh token error: {}", e)));
79
            return Err(e);
80
4
        }
81
    }
82

            
83
4
    Ok(StatusCode::NO_CONTENT)
84
4
}