use std::sync::Arc;
use axum::body::Bytes;
use axum::extract::State;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Redirect};
use axum_extra::extract::CookieJar;
use axum_extra::extract::cookie::{Cookie, SameSite};
use cookie::time::Duration;
use base64::{Engine as B64Engine, engine::general_purpose::URL_SAFE_NO_PAD as b64};
use ordinary_config::AuthConfig;
fn access_token_cookie(
jar: CookieJar,
token: &Bytes,
auth_config: &AuthConfig,
secure: bool,
) -> CookieJar {
let name = if secure {
tracing::info!("generated host access cookie");
"__Host-ORDINARY-ACCESS-TOKEN"
} else {
tracing::warn!("generated insecure access cookie");
"ORDINARY-ACCESS-TOKEN"
};
jar.add(
Cookie::build((name, b64.encode(token)))
.secure(secure)
.http_only(true)
.path("/")
.same_site(SameSite::Strict)
.max_age(Duration::seconds(i64::from(
auth_config.access_token.lifetime,
))),
)
}
pub async fn fetch_access_cookies(
State(state): State<Arc<crate::server::OrdinaryAppServerState>>,
jar: CookieJar,
body: Bytes,
) -> impl IntoResponse {
let span = tracing::info_span!("token", flv = %"cookie");
span.in_scope(|| match state.auth.access_get(&body) {
Ok(token) => {
let cookie = if state.auth.config.cookies_enabled {
Some(access_token_cookie(
jar,
&token,
&state.auth.config,
state.secure_cookies,
))
} else {
None
};
(StatusCode::OK, cookie, token)
}
Err(e) => {
tracing::warn!("{e}");
(StatusCode::UNAUTHORIZED, None, Bytes::new())
}
})
}
pub async fn fetch_access(
State(state): State<Arc<crate::server::OrdinaryAppServerState>>,
body: Bytes,
) -> impl IntoResponse {
let span = tracing::info_span!("token", flv = %"fetch");
span.in_scope(|| match state.auth.access_get(&body) {
Ok(token) => (StatusCode::OK, token),
Err(e) => {
tracing::warn!("{e}");
(StatusCode::UNAUTHORIZED, Bytes::new())
}
})
}
pub async fn redirect_access(
State(state): State<Arc<crate::server::OrdinaryAppServerState>>,
jar: CookieJar,
) -> Result<(CookieJar, Redirect), StatusCode> {
let span = tracing::info_span!("token", flv = %"redirect");
let cookie_name = if state.secure_cookies {
"__Secure-ORDINARY-REFRESH-TOKEN"
} else {
"ORDINARY-REFRESH-TOKEN"
};
span.in_scope(|| {
if let Some(refresh_token) = jar.get(cookie_name) {
if let Ok(refresh_token) = b64.decode(refresh_token.value()) {
if let Ok(access_token) = state
.auth
.access_get(&Bytes::copy_from_slice(&refresh_token[..]))
{
return Ok((
access_token_cookie(
jar,
&access_token,
&state.auth.config,
state.secure_cookies,
),
Redirect::to("/"),
));
}
} else {
tracing::error!("failed to decode refresh cookie");
}
} else {
tracing::error!("no refresh cookie");
}
Err(StatusCode::UNAUTHORIZED)
})
}