Add support for access tokens using MAS
This commit is contained in:
@@ -5,8 +5,17 @@ homeserver:
|
||||
|
||||
user:
|
||||
mxid_localpart: baibot
|
||||
|
||||
# Authentication: set EITHER password OR access_token + device_id.
|
||||
#
|
||||
# Password-based login (traditional homeservers):
|
||||
password: baibot
|
||||
|
||||
# Access token login (for MAS/OIDC-enabled homeservers):
|
||||
# Generate a token via: mas-cli manage issue-compatibility-token <username> [device_id]
|
||||
# access_token: null
|
||||
# device_id: null
|
||||
|
||||
# The name the bot uses as a display name and when it refers to itself.
|
||||
# Leave empty to use the default (baibot).
|
||||
name: baibot
|
||||
|
||||
@@ -6,7 +6,7 @@ use mxlink::matrix_sdk::Room;
|
||||
use mxlink::matrix_sdk::media::{MediaFormat, MediaRequestParameters};
|
||||
use mxlink::matrix_sdk::ruma::api::client::profile::{AvatarUrl, DisplayName};
|
||||
use mxlink::matrix_sdk::ruma::{
|
||||
MilliSecondsSinceUnixEpoch, OwnedUserId, events::room::MediaSource,
|
||||
MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, events::room::MediaSource,
|
||||
};
|
||||
|
||||
use mxlink::{
|
||||
@@ -395,10 +395,25 @@ async fn create_matrix_link(config: &Config) -> anyhow::Result<MatrixLink> {
|
||||
let session_encryption_key = config.persistence.session_encryption_key()?;
|
||||
let db_dir_path: std::path::PathBuf = config.persistence.db_dir_path()?;
|
||||
|
||||
let login_creds = LoginCredentials::UserPassword(
|
||||
config.user.mxid_localpart.to_owned(),
|
||||
config.user.password.to_owned(),
|
||||
);
|
||||
let login_creds = if let Some(access_token) = &config.user.access_token {
|
||||
let server_name = &config.homeserver.server_name;
|
||||
let localpart = &config.user.mxid_localpart;
|
||||
let user_id = OwnedUserId::try_from(format!("@{localpart}:{server_name}"))
|
||||
.map_err(|e| anyhow::anyhow!("Invalid user ID: {e}"))?;
|
||||
let device_id = OwnedDeviceId::from(
|
||||
config.user.device_id.as_deref().expect("device_id must be set for access token auth"),
|
||||
);
|
||||
LoginCredentials::AccessToken {
|
||||
user_id,
|
||||
device_id,
|
||||
access_token: access_token.to_owned(),
|
||||
}
|
||||
} else {
|
||||
LoginCredentials::UserPassword(
|
||||
config.user.mxid_localpart.to_owned(),
|
||||
config.user.password.as_deref().expect("password must be set if access_token is not").to_owned(),
|
||||
)
|
||||
};
|
||||
|
||||
let login_encryption = LoginEncryption::new(
|
||||
config.user.encryption.recovery_passphrase.clone(),
|
||||
|
||||
@@ -29,7 +29,9 @@ pub fn load() -> anyhow::Result<Config> {
|
||||
cfg_env::BAIBOT_HOMESERVER_SERVER_NAME => config.homeserver.server_name = value,
|
||||
cfg_env::BAIBOT_HOMESERVER_URL => config.homeserver.url = value,
|
||||
cfg_env::BAIBOT_USER_MXID_LOCALPART => config.user.mxid_localpart = value,
|
||||
cfg_env::BAIBOT_USER_PASSWORD => config.user.password = value,
|
||||
cfg_env::BAIBOT_USER_PASSWORD => config.user.password = Some(value),
|
||||
cfg_env::BAIBOT_USER_ACCESS_TOKEN => config.user.access_token = Some(value),
|
||||
cfg_env::BAIBOT_USER_DEVICE_ID => config.user.device_id = Some(value),
|
||||
cfg_env::BAIBOT_USER_ENCRYPTION_RECOVERY_PASSPHRASE => {
|
||||
config.user.encryption.recovery_passphrase = Some(value);
|
||||
}
|
||||
|
||||
@@ -127,7 +127,15 @@ impl Avatar {
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ConfigUser {
|
||||
pub mxid_localpart: String,
|
||||
pub password: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub password: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub access_token: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub device_id: Option<String>,
|
||||
|
||||
#[serde(default = "super::defaults::name")]
|
||||
pub name: String,
|
||||
@@ -148,12 +156,7 @@ impl ConfigUser {
|
||||
));
|
||||
}
|
||||
|
||||
if self.password.is_empty() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"The user.password ({}) configuration must be set",
|
||||
super::env::BAIBOT_USER_PASSWORD
|
||||
));
|
||||
}
|
||||
self.validate_auth()?;
|
||||
|
||||
if self.name.is_empty() {
|
||||
return Err(anyhow::anyhow!(
|
||||
@@ -166,6 +169,29 @@ impl ConfigUser {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_auth(&self) -> anyhow::Result<()> {
|
||||
let has_password = self.password.as_deref().is_some_and(|p| !p.is_empty());
|
||||
let has_access_token = self.access_token.as_deref().is_some_and(|t| !t.is_empty());
|
||||
let has_device_id = self.device_id.as_deref().is_some_and(|d| !d.is_empty());
|
||||
|
||||
if !has_password && !has_access_token {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Either user.password ({}) or user.access_token ({}) must be set",
|
||||
super::env::BAIBOT_USER_PASSWORD,
|
||||
super::env::BAIBOT_USER_ACCESS_TOKEN
|
||||
));
|
||||
}
|
||||
|
||||
if has_access_token && !has_device_id {
|
||||
return Err(anyhow::anyhow!(
|
||||
"user.device_id ({}) must be set when using access token authentication",
|
||||
super::env::BAIBOT_USER_DEVICE_ID
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
|
||||
@@ -5,6 +5,8 @@ pub const BAIBOT_HOMESERVER_URL: &str = "BAIBOT_HOMESERVER_URL";
|
||||
|
||||
pub const BAIBOT_USER_MXID_LOCALPART: &str = "BAIBOT_USER_MXID_LOCALPART";
|
||||
pub const BAIBOT_USER_PASSWORD: &str = "BAIBOT_USER_PASSWORD";
|
||||
pub const BAIBOT_USER_ACCESS_TOKEN: &str = "BAIBOT_USER_ACCESS_TOKEN";
|
||||
pub const BAIBOT_USER_DEVICE_ID: &str = "BAIBOT_USER_DEVICE_ID";
|
||||
pub const BAIBOT_USER_NAME: &str = "BAIBOT_USER_NAME";
|
||||
pub const BAIBOT_USER_AVATAR: &str = "BAIBOT_USER_AVATAR";
|
||||
pub const BAIBOT_USER_ENCRYPTION_RECOVERY_PASSPHRASE: &str =
|
||||
|
||||
Reference in New Issue
Block a user