Skip to main content

stalwart_lib/common/src/auth/oauth/
introspect.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
5 */
6
7use crate::trc::{AddContext, AuthEvent, EventType};
8use serde::{Deserialize, Serialize};
9
10use crate::common::{Server, auth::AccessToken};
11
12#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
13pub struct OAuthIntrospect {
14    #[serde(default)]
15    pub active: bool,
16
17    #[serde(default)]
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub scope: Option<String>,
20
21    #[serde(default)]
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub client_id: Option<String>,
24
25    #[serde(default)]
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub username: Option<String>,
28
29    #[serde(default)]
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub token_type: Option<String>,
32
33    #[serde(default)]
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub exp: Option<i64>,
36
37    #[serde(default)]
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub iat: Option<i64>,
40
41    #[serde(default)]
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub nbf: Option<i64>,
44
45    #[serde(default)]
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub sub: Option<String>,
48}
49
50impl Server {
51    pub async fn introspect_access_token(
52        &self,
53        token: &str,
54        access_token: &AccessToken,
55    ) -> crate::trc::Result<OAuthIntrospect> {
56        match self.validate_access_token(None, token).await {
57            Ok(token_info) => Ok(OAuthIntrospect {
58                active: true,
59                client_id: Some(token_info.client_id),
60                username: if access_token.primary_id() == token_info.account_id {
61                    access_token.name.clone()
62                } else {
63                    self.get_access_token(token_info.account_id)
64                        .await
65                        .caused_by(crate::trc::location!())?
66                        .name
67                        .clone()
68                }
69                .into(),
70                token_type: Some("bearer".into()),
71                exp: Some(token_info.expiry as i64),
72                iat: Some(token_info.issued_at as i64),
73                ..Default::default()
74            }),
75            Err(err)
76                if matches!(
77                    err.event_type(),
78                    EventType::Auth(AuthEvent::Error) | EventType::Auth(AuthEvent::TokenExpired)
79                ) =>
80            {
81                Ok(OAuthIntrospect::default())
82            }
83            Err(err) => Err(err),
84        }
85    }
86}