use super::{AuthContext, AuthContextSource, ScopeSet};
use crate::internal::domain::{
AccountIdHash, ErrorCode, GatewayError, LocalUserId, RequestId, SessionId,
};
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use time::OffsetDateTime;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OAuthContextInput {
pub subject: String,
pub issuer: String,
pub audience: String,
pub scopes: BTreeSet<String>,
pub expires_at: OffsetDateTime,
pub token_id_hash: Option<AccountIdHash>,
pub request_id: RequestId,
pub session_id: SessionId,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct RemoteAuthContext {
pub user_id: LocalUserId,
pub subject: String,
pub issuer: String,
pub audience: String,
pub scopes: ScopeSet,
#[serde(with = "time::serde::rfc3339")]
pub expires_at: OffsetDateTime,
pub token_id_hash: Option<AccountIdHash>,
pub request_id: RequestId,
pub session_id: SessionId,
}
pub fn remote_auth_context_from_input(
input: OAuthContextInput,
) -> Result<RemoteAuthContext, GatewayError> {
if input.subject.trim().is_empty() {
return Err(GatewayError::new(
ErrorCode::AuthTokenInvalid,
"OAuth subject is empty",
false,
Some("Use a token with a stable subject".to_string()),
));
}
let user_id =
LocalUserId::new(format!("{}:{}", input.issuer, input.subject)).ok_or_else(|| {
GatewayError::new(
ErrorCode::AuthTokenInvalid,
"Remote user id could not be derived",
false,
Some("Use a token with issuer and subject".to_string()),
)
})?;
let scopes = ScopeSet::local_with_live(input.scopes)?;
Ok(RemoteAuthContext {
user_id,
subject: input.subject,
issuer: input.issuer,
audience: input.audience,
scopes,
expires_at: input.expires_at,
token_id_hash: input.token_id_hash,
request_id: input.request_id,
session_id: input.session_id,
})
}
impl From<RemoteAuthContext> for AuthContext {
fn from(context: RemoteAuthContext) -> Self {
Self {
source: AuthContextSource::RemoteOauth,
user_id: context.user_id,
scopes: context.scopes,
request_id: context.request_id,
session_id: context.session_id,
}
}
}