Add support for access tokens using MAS
This commit is contained in:
@@ -5,8 +5,17 @@ homeserver:
|
|||||||
|
|
||||||
user:
|
user:
|
||||||
mxid_localpart: baibot
|
mxid_localpart: baibot
|
||||||
|
|
||||||
|
# Authentication: set EITHER password OR access_token + device_id.
|
||||||
|
#
|
||||||
|
# Password-based login (traditional homeservers):
|
||||||
password: baibot
|
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.
|
# The name the bot uses as a display name and when it refers to itself.
|
||||||
# Leave empty to use the default (baibot).
|
# Leave empty to use the default (baibot).
|
||||||
name: baibot
|
name: baibot
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use mxlink::matrix_sdk::Room;
|
|||||||
use mxlink::matrix_sdk::media::{MediaFormat, MediaRequestParameters};
|
use mxlink::matrix_sdk::media::{MediaFormat, MediaRequestParameters};
|
||||||
use mxlink::matrix_sdk::ruma::api::client::profile::{AvatarUrl, DisplayName};
|
use mxlink::matrix_sdk::ruma::api::client::profile::{AvatarUrl, DisplayName};
|
||||||
use mxlink::matrix_sdk::ruma::{
|
use mxlink::matrix_sdk::ruma::{
|
||||||
MilliSecondsSinceUnixEpoch, OwnedUserId, events::room::MediaSource,
|
MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedUserId, events::room::MediaSource,
|
||||||
};
|
};
|
||||||
|
|
||||||
use mxlink::{
|
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 session_encryption_key = config.persistence.session_encryption_key()?;
|
||||||
let db_dir_path: std::path::PathBuf = config.persistence.db_dir_path()?;
|
let db_dir_path: std::path::PathBuf = config.persistence.db_dir_path()?;
|
||||||
|
|
||||||
let login_creds = LoginCredentials::UserPassword(
|
let login_creds = if let Some(access_token) = &config.user.access_token {
|
||||||
config.user.mxid_localpart.to_owned(),
|
let server_name = &config.homeserver.server_name;
|
||||||
config.user.password.to_owned(),
|
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(
|
let login_encryption = LoginEncryption::new(
|
||||||
config.user.encryption.recovery_passphrase.clone(),
|
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_SERVER_NAME => config.homeserver.server_name = value,
|
||||||
cfg_env::BAIBOT_HOMESERVER_URL => config.homeserver.url = 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_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 => {
|
cfg_env::BAIBOT_USER_ENCRYPTION_RECOVERY_PASSPHRASE => {
|
||||||
config.user.encryption.recovery_passphrase = Some(value);
|
config.user.encryption.recovery_passphrase = Some(value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,7 +127,15 @@ impl Avatar {
|
|||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ConfigUser {
|
pub struct ConfigUser {
|
||||||
pub mxid_localpart: String,
|
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")]
|
#[serde(default = "super::defaults::name")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -148,12 +156,7 @@ impl ConfigUser {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.password.is_empty() {
|
self.validate_auth()?;
|
||||||
return Err(anyhow::anyhow!(
|
|
||||||
"The user.password ({}) configuration must be set",
|
|
||||||
super::env::BAIBOT_USER_PASSWORD
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.name.is_empty() {
|
if self.name.is_empty() {
|
||||||
return Err(anyhow::anyhow!(
|
return Err(anyhow::anyhow!(
|
||||||
@@ -166,6 +169,29 @@ impl ConfigUser {
|
|||||||
|
|
||||||
Ok(())
|
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)]
|
#[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_MXID_LOCALPART: &str = "BAIBOT_USER_MXID_LOCALPART";
|
||||||
pub const BAIBOT_USER_PASSWORD: &str = "BAIBOT_USER_PASSWORD";
|
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_NAME: &str = "BAIBOT_USER_NAME";
|
||||||
pub const BAIBOT_USER_AVATAR: &str = "BAIBOT_USER_AVATAR";
|
pub const BAIBOT_USER_AVATAR: &str = "BAIBOT_USER_AVATAR";
|
||||||
pub const BAIBOT_USER_ENCRYPTION_RECOVERY_PASSPHRASE: &str =
|
pub const BAIBOT_USER_ENCRYPTION_RECOVERY_PASSPHRASE: &str =
|
||||||
|
|||||||
Reference in New Issue
Block a user