add /openid/request_token and /openid/userinfo routes

heavily changed and improved by me

Co-authored-by: mikoto <avdb@keemail.me>
Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
strawberry 2024-07-02 21:51:11 -04:00
parent 5edd391e83
commit 1a64e42cfe
11 changed files with 151 additions and 7 deletions

View file

@ -1,6 +1,6 @@
use std::{collections::BTreeMap, mem::size_of, sync::Arc};
use conduit::{utils, warn, Error, Result};
use conduit::{debug_info, utils, warn, Error, Result};
use database::{Database, Map};
use ruma::{
api::client::{device::Device, error::ErrorKind, filter::FilterDefinition},
@ -28,6 +28,7 @@ pub struct Data {
userid_masterkeyid: Arc<Map>,
userid_selfsigningkeyid: Arc<Map>,
userid_usersigningkeyid: Arc<Map>,
openidtoken_expiresatuserid: Arc<Map>,
keychangeid_userid: Arc<Map>,
todeviceid_events: Arc<Map>,
userfilterid_filter: Arc<Map>,
@ -51,6 +52,7 @@ impl Data {
userid_masterkeyid: db["userid_masterkeyid"].clone(),
userid_selfsigningkeyid: db["userid_selfsigningkeyid"].clone(),
userid_usersigningkeyid: db["userid_usersigningkeyid"].clone(),
openidtoken_expiresatuserid: db["openidtoken_expiresatuserid"].clone(),
keychangeid_userid: db["keychangeid_userid"].clone(),
todeviceid_events: db["todeviceid_events"].clone(),
userfilterid_filter: db["userfilterid_filter"].clone(),
@ -920,6 +922,49 @@ impl Data {
Ok(None)
}
}
/// Creates an OpenID token, which can be used to prove that a user has
/// access to an account (primarily for integrations)
pub(super) fn create_openid_token(&self, user_id: &UserId, token: &str) -> Result<u64> {
let expires_in = services().globals.config.openid_token_ttl;
let expires_at = utils::millis_since_unix_epoch().saturating_add(expires_in * 1000);
let mut value = expires_at.to_be_bytes().to_vec();
value.extend_from_slice(user_id.as_bytes());
self.openidtoken_expiresatuserid
.insert(token.as_bytes(), value.as_slice())?;
Ok(expires_in)
}
/// Find out which user an OpenID access token belongs to.
pub(super) fn find_from_openid_token(&self, token: &str) -> Result<OwnedUserId> {
let Some(value) = self.openidtoken_expiresatuserid.get(token.as_bytes())? else {
return Err(Error::BadRequest(ErrorKind::Unauthorized, "OpenID token is unrecognised"));
};
let (expires_at_bytes, user_bytes) = value.split_at(0_u64.to_be_bytes().len());
let expires_at = u64::from_be_bytes(
expires_at_bytes
.try_into()
.map_err(|_| Error::bad_database("expires_at in openid_userid is invalid u64."))?,
);
if expires_at < utils::millis_since_unix_epoch() {
debug_info!("OpenID token is expired, removing");
self.openidtoken_expiresatuserid.remove(token.as_bytes())?;
return Err(Error::BadRequest(ErrorKind::Unauthorized, "OpenID token is expired"));
}
UserId::parse(
utils::string_from_bytes(user_bytes)
.map_err(|_| Error::bad_database("User ID in openid_userid is invalid unicode."))?,
)
.map_err(|_| Error::bad_database("User ID in openid_userid is invalid."))
}
}
/// Will only return with Some(username) if the password was not empty and the

View file

@ -477,6 +477,15 @@ impl Service {
pub fn get_filter(&self, user_id: &UserId, filter_id: &str) -> Result<Option<FilterDefinition>> {
self.db.get_filter(user_id, filter_id)
}
/// Creates an OpenID token, which can be used to prove that a user has
/// access to an account (primarily for integrations)
pub fn create_openid_token(&self, user_id: &UserId, token: &str) -> Result<u64> {
self.db.create_openid_token(user_id, token)
}
/// Find out which user an OpenID access token belongs to.
pub fn find_from_openid_token(&self, token: &str) -> Result<OwnedUserId> { self.db.find_from_openid_token(token) }
}
/// Ensure that a user only sees signatures from themselves and the target user