add services::oidc::user_and_device_from_token(), use in auth

This commit is contained in:
lafleur 2025-08-12 02:31:40 +02:00
commit c4229509d9
3 changed files with 47 additions and 6 deletions

View file

@ -46,7 +46,7 @@ pub(crate) async fn authorize(
tracing::debug!("submitting OIDC authorisation for token : {token:#?}");
// Get the user id from the token and add it to the query.
let (owner_id, _) = services.oidc.get_user_for_token(token)?;
let (owner_id, _) = services.oidc.user_and_device_from_token(token)?;
let mut query_with_user_id = query.clone();
query_with_user_id.username = Some(owner_id.localpart().to_string());

View file

@ -12,10 +12,11 @@ pub mod registrar;
use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use conduwuit::Result;
use conduwuit::{Result, err};
use oxide_auth::{
frontends::simple::endpoint::{Generic, Vacant},
primitives::{
grant::Grant,
prelude::{
AuthMap, Authorizer, Client, Issuer, RandomGenerator, Registrar, TokenMap,
},
@ -23,16 +24,24 @@ use oxide_auth::{
},
};
use registrar::ClientMap;
use ruma::{OwnedDeviceId, OwnedUserId, UserId};
use crate::{globals, Dep};
struct Services {
globals: Dep<globals::Service>,
}
pub struct Service {
registrar: Mutex<ClientMap>,
authorizer: Mutex<AuthMap<RandomGenerator>>,
issuer: Mutex<TokenMap<RandomGenerator>>,
services: Services,
}
#[async_trait]
impl crate::Service for Service {
fn build(_args: crate::Args<'_>) -> Result<Arc<Self>> { Ok(Arc::new(Self::preconfigured())) }
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> { Ok(Arc::new(Self::preconfigured(args))) }
fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
}
@ -48,7 +57,7 @@ impl Service {
}
#[must_use]
pub fn preconfigured() -> Self {
pub(crate) fn preconfigured(args: crate::Args<'_>) -> Self {
Self {
registrar: Mutex::new(
vec![Client::public(
@ -70,9 +79,35 @@ impl Service {
// be read and parsed by anyone, but not maliciously created. However, they can not be
// revoked and thus don't offer even longer lived refresh tokens.
issuer: Mutex::new(TokenMap::new(RandomGenerator::new(16))),
services: Services {
globals: args.depend::<globals::Service>("globals"),
},
}
}
fn grant_from_token(&self, token: &str) -> Option<Grant> {
let issuer = self.issuer.lock().expect("lockable issuer");
issuer.recover_token(token).expect("infallible recover_token implementation")
}
pub fn user_and_device_from_token(&self, token: &str) -> Result<(OwnedUserId, OwnedDeviceId)> {
let Some(Grant { owner_id, client_id, .. }) = self.grant_from_token(token) else {
return Err(err!(Request(MissingToken("unknown token: {token:?}"))));
};
let server_name = self.services.globals.server_name();
let owner_id = UserId::parse_with_server_name(owner_id.clone(), server_name)
.map_err(|err|
err!(Request(InvalidUsername("invalid username {owner_id:?}: {err}")))
)?;
let device_id = OwnedDeviceId::try_from(client_id.clone())
.map_err(|err|
err!(Request(InvalidParam("invalid client_id {client_id:?}: {err}")))
)?;
Ok((owner_id, device_id))
}
/// The oxide-auth carry-all endpoint.
pub fn endpoint(
&self,

View file

@ -19,7 +19,7 @@ use ruma::{
use serde::{Deserialize, Serialize};
use serde_json::json;
use crate::{Dep, account_data, admin, globals, rooms};
use crate::{account_data, admin, globals, oidc, rooms, Dep};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserSuspension {
@ -40,6 +40,7 @@ struct Services {
server: Arc<Server>,
account_data: Dep<account_data::Service>,
admin: Dep<admin::Service>,
oidc: Dep<oidc::Service>,
globals: Dep<globals::Service>,
state_accessor: Dep<rooms::state_accessor::Service>,
state_cache: Dep<rooms::state_cache::Service>,
@ -76,6 +77,7 @@ impl crate::Service for Service {
server: args.server.clone(),
account_data: args.depend::<account_data::Service>("account_data"),
admin: args.depend::<admin::Service>("admin"),
oidc: args.depend::<oidc::Service>("oidc"),
globals: args.depend::<globals::Service>("globals"),
state_accessor: args
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
@ -224,8 +226,12 @@ impl Service {
/// Find out which user an access token belongs to.
pub async fn find_from_token(&self, token: &str) -> Result<(OwnedUserId, OwnedDeviceId)> {
if self.services.server.config.auth.as_ref().is_some_and(|auth| auth.enable_oidc_login) {
self.services.oidc.user_and_device_from_token(token)
} else {
self.db.token_userdeviceid.get(token).await.deserialized()
}
}
/// Returns an iterator over all users on this homeserver (offered for
/// compatibility)