use http::header;
use openauth_core::api::ApiRequest;
use openauth_core::auth::session::{GetSessionInput, SessionAuth};
use openauth_core::context::AuthContext;
use openauth_core::cookies::Cookie;
use openauth_core::db::{DbAdapter, DbRecord, DbValue, Session, User};
use openauth_core::error::OpenAuthError;
use openauth_core::session::{CreateSessionInput, DbSessionStore};
use time::{Duration, OffsetDateTime};
use crate::cookies::request_cookie_header;
use crate::options::{PasskeyOptions, PasskeyRegistrationUser, ResolveRegistrationUserInput};
pub type CurrentSession = (Session, User, Vec<Cookie>);
pub fn registration_user(
options: &PasskeyOptions,
session: Option<&CurrentSession>,
context: Option<String>,
) -> Result<PasskeyRegistrationUser, RegistrationUserError> {
if options.registration.require_session {
return session
.map(|(_, user, _)| session_user(user))
.ok_or(RegistrationUserError::SessionRequired);
}
if let Some((_, user, _)) = session {
return Ok(session_user(user));
}
let Some(resolve_user) = &options.registration.resolve_user else {
return Err(RegistrationUserError::ResolveUserRequired);
};
let Some(user) = resolve_user(ResolveRegistrationUserInput { context }) else {
return Err(RegistrationUserError::ResolvedUserInvalid);
};
if user.id.is_empty() || user.name.is_empty() {
return Err(RegistrationUserError::ResolvedUserInvalid);
}
Ok(user)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RegistrationUserError {
SessionRequired,
ResolveUserRequired,
ResolvedUserInvalid,
}
fn session_user(user: &User) -> PasskeyRegistrationUser {
let name = if user.email.is_empty() {
user.id.clone()
} else {
user.email.clone()
};
PasskeyRegistrationUser {
id: user.id.clone(),
name: name.clone(),
display_name: Some(name),
}
}
pub async fn current_session(
context: &AuthContext,
request: &ApiRequest,
) -> Result<Option<CurrentSession>, OpenAuthError> {
let Some(adapter) = context.adapter() else {
return Ok(None);
};
let cookie_header = request_cookie_header(request).unwrap_or_default();
let Some(result) = SessionAuth::new(adapter.as_ref(), context)
.get_session(GetSessionInput::new(cookie_header))
.await?
else {
return Ok(None);
};
let Some(session) = result.session else {
return Ok(None);
};
let Some(user) = result.user else {
return Ok(None);
};
Ok(Some((session, user, result.cookies)))
}
pub async fn create_session_for_user(
adapter: &dyn DbAdapter,
context: &AuthContext,
request: &ApiRequest,
user: &User,
) -> Result<Session, OpenAuthError> {
let expires_at =
OffsetDateTime::now_utc() + Duration::seconds(context.session_config.expires_in as i64);
let mut input = CreateSessionInput::new(user.id.clone(), expires_at);
if let Some(ip_address) = request
.headers()
.get("x-forwarded-for")
.and_then(|value| value.to_str().ok())
.map(str::to_owned)
{
input = input.ip_address(ip_address);
}
if let Some(user_agent) = request
.headers()
.get(header::USER_AGENT)
.and_then(|value| value.to_str().ok())
.map(str::to_owned)
{
input = input.user_agent(user_agent);
}
input = input.additional_fields(additional_session_create_values(context));
DbSessionStore::new(adapter).create_session(input).await
}
fn additional_session_create_values(context: &AuthContext) -> DbRecord {
context
.options
.session
.additional_fields
.iter()
.map(|(name, field)| {
(
field.db_name.clone().unwrap_or_else(|| name.clone()),
field.default_value.clone().unwrap_or(DbValue::Null),
)
})
.collect()
}