drogue_bazaar/actix/auth/authentication/
mod.rs1mod middleware;
2
3use crate::auth::{openid, pat, AuthError, UserInformation};
4use ::openid::{Claims, CustomClaims};
5use chrono::{DateTime, LocalResult, TimeZone, Utc};
6pub use middleware::AuthenticatedUntil;
7use tracing::instrument;
8
9pub enum Credentials {
11 OpenIDToken(String),
13 AccessToken(UsernameAndToken),
15 Anonymous,
17}
18
19pub struct UsernameAndToken {
20 pub username: String,
21 pub access_token: Option<String>,
22}
23
24#[derive(Clone, Debug)]
42pub enum AuthN {
43 Disabled,
45 Enabled {
49 openid: Option<openid::Authenticator>,
50 token: Option<pat::Authenticator>,
51 },
52}
53
54impl From<(Option<openid::Authenticator>, Option<pat::Authenticator>)> for AuthN {
56 fn from(auth: (Option<openid::Authenticator>, Option<pat::Authenticator>)) -> Self {
57 let (openid, token) = auth;
58 if openid.is_none() {
59 AuthN::Disabled
60 } else {
61 AuthN::Enabled { openid, token }
62 }
63 }
64}
65
66impl AuthN {
67 #[instrument(skip_all, err)]
68 async fn authenticate(
69 &self,
70 credentials: Credentials,
71 ) -> Result<(UserInformation, Option<DateTime<Utc>>), AuthError> {
72 match self {
73 Self::Disabled => {
74 Ok((UserInformation::Anonymous, None))
76 }
77 Self::Enabled { openid, token } => match credentials {
78 Credentials::AccessToken(creds) => {
79 if let Some(token) = token {
80 if creds.access_token.is_none() {
81 log::debug!("Cannot authenticate : empty access token.");
82 return Err(AuthError::InvalidRequest(String::from(
83 "No access token provided.",
84 )));
85 }
86 let auth_response = token
87 .authenticate(pat::Request {
88 user_id: creds.username.clone(),
89 access_token: creds.access_token.clone().unwrap_or_default(),
90 })
91 .await
92 .map_err(|e| AuthError::Internal(e.to_string()))?;
93 match auth_response.outcome {
94 pat::Outcome::Known(details) => {
95 Ok((UserInformation::Authenticated(details), None))
96 }
97 pat::Outcome::Unknown => {
98 log::debug!("Unknown access token");
99 Err(AuthError::Forbidden)
100 }
101 }
102 } else {
103 log::debug!("Access token authentication disabled");
104 Err(AuthError::InvalidRequest(
105 "Access token authentication disabled".to_string(),
106 ))
107 }
108 }
109 Credentials::OpenIDToken(token) => {
110 if let Some(openid) = openid {
111 match openid.validate_token(&token).await {
112 Ok(token) => Ok((
113 UserInformation::Authenticated(token.clone().into()),
114 Some(to_expiration(token.standard_claims().exp())?),
115 )),
116 Err(err) => {
117 log::debug!("Authentication error: {err}");
118 Err(AuthError::Forbidden)
119 }
120 }
121 } else {
122 log::debug!("Open ID authentication disabled");
123 Err(AuthError::InvalidRequest(
124 "Open ID authentication disabled".to_string(),
125 ))
126 }
127 }
128 Credentials::Anonymous => Ok((UserInformation::Anonymous, None)),
129 },
130 }
131 }
132}
133
134fn to_expiration(exp: i64) -> Result<DateTime<Utc>, AuthError> {
136 match Utc.timestamp_opt(exp, 0) {
137 LocalResult::None => Err(AuthError::Internal(
138 "Unable to convert timestamp".to_string(),
139 )),
140 LocalResult::Single(exp) => Ok(exp),
141 LocalResult::Ambiguous(min, _) => Ok(min),
142 }
143}