Add 'm.login.token' authentication

next
Timo Kösters 2021-02-07 17:38:45 +01:00
parent 3588dcd6d0
commit d49911c5e0
No known key found for this signature in database
GPG Key ID: 24DA7517711A2BA4
7 changed files with 150 additions and 29 deletions

86
Cargo.lock generated
View File

@ -182,6 +182,19 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time 0.1.44",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "color_quant" name = "color_quant"
version = "1.1.0" version = "1.1.0"
@ -197,6 +210,7 @@ dependencies = [
"http", "http",
"image", "image",
"js_int", "js_int",
"jsonwebtoken",
"log", "log",
"rand", "rand",
"regex", "regex",
@ -243,7 +257,7 @@ version = "0.15.0-dev"
source = "git+https://github.com/SergioBenitez/cookie-rs.git?rev=1c3ca83#1c3ca838543b60a4448d279dc4b903cc7a2bc22a" source = "git+https://github.com/SergioBenitez/cookie-rs.git?rev=1c3ca83#1c3ca838543b60a4448d279dc4b903cc7a2bc22a"
dependencies = [ dependencies = [
"percent-encoding", "percent-encoding",
"time", "time 0.2.23",
"version_check", "version_check",
] ]
@ -578,7 +592,7 @@ checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
dependencies = [ dependencies = [
"cfg-if 0.1.10", "cfg-if 0.1.10",
"libc", "libc",
"wasi", "wasi 0.9.0+wasi-snapshot-preview1",
] ]
[[package]] [[package]]
@ -850,6 +864,20 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "jsonwebtoken"
version = "7.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32"
dependencies = [
"base64 0.12.3",
"pem",
"ring",
"serde",
"serde_json",
"simple_asn1",
]
[[package]] [[package]]
name = "kernel32-sys" name = "kernel32-sys"
version = "0.2.2" version = "0.2.2"
@ -1044,6 +1072,17 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "num-bigint"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -1204,6 +1243,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "pem"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c220d01f863d13d96ca82359d1e81e64a7c6bf0637bcde7b2349630addf0c6"
dependencies = [
"base64 0.13.0",
"once_cell",
"regex",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.1.0" version = "2.1.0"
@ -1538,7 +1588,7 @@ dependencies = [
"rocket_http", "rocket_http",
"serde", "serde",
"state", "state",
"time", "time 0.2.23",
"tokio", "tokio",
"ubyte", "ubyte",
"version_check", "version_check",
@ -1575,7 +1625,7 @@ dependencies = [
"ref-cast", "ref-cast",
"smallvec", "smallvec",
"state", "state",
"time", "time 0.2.23",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls",
"uncased", "uncased",
@ -1969,6 +2019,17 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "simple_asn1"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b"
dependencies = [
"chrono",
"num-bigint",
"num-traits",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.2" version = "0.4.2"
@ -2168,6 +2229,17 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.2.23" version = "0.2.23"
@ -2498,6 +2570,12 @@ version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.69" version = "0.2.69"

View File

@ -63,6 +63,8 @@ ring = "0.16.19"
trust-dns-resolver = "0.19.6" trust-dns-resolver = "0.19.6"
# Used to find matching events for appservices # Used to find matching events for appservices
regex = "1.4.2" regex = "1.4.2"
# jwt jsonwebtokens
jsonwebtoken = "7.2.0"
[features] [features]
default = ["conduit_bin"] default = ["conduit_bin"]

View File

@ -1,6 +1,6 @@
use crate::{utils, Error, Result}; use crate::{utils, Error, Result};
use http::header::{HeaderValue, CONTENT_TYPE}; use http::header::{HeaderValue, CONTENT_TYPE};
use log::{info, warn}; use log::warn;
use ruma::api::OutgoingRequest; use ruma::api::OutgoingRequest;
use std::{ use std::{
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},

View File

@ -8,6 +8,13 @@ use ruma::{
}, },
UserId, UserId,
}; };
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Claims {
sub: String,
exp: usize,
}
#[cfg(feature = "conduit_bin")] #[cfg(feature = "conduit_bin")]
use rocket::{get, post}; use rocket::{get, post};
@ -40,40 +47,62 @@ pub async fn login_route(
body: Ruma<login::Request<'_>>, body: Ruma<login::Request<'_>>,
) -> ConduitResult<login::Response> { ) -> ConduitResult<login::Response> {
// Validate login method // Validate login method
let user_id = // TODO: Other login methods
// TODO: Other login methods let user_id = match &body.login_info {
if let (login::IncomingUserInfo::MatrixId(username), login::IncomingLoginInfo::Password { password }) = login::IncomingLoginInfo::Password { password } => {
(&body.user, &body.login_info) let username = if let login::IncomingUserInfo::MatrixId(matrix_id) = &body.user {
{ matrix_id
let user_id = UserId::parse_with_server_name(username.to_string(), db.globals.server_name()) } else {
.map_err(|_| Error::BadRequest( return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type."));
ErrorKind::InvalidUsername, };
"Username is invalid." let user_id =
))?; UserId::parse_with_server_name(username.to_owned(), db.globals.server_name())
let hash = db.users.password_hash(&user_id)? .map_err(|_| {
.ok_or(Error::BadRequest( Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid.")
ErrorKind::Forbidden, })?;
"Wrong username or password." let hash = db.users.password_hash(&user_id)?.ok_or(Error::BadRequest(
))?; ErrorKind::Forbidden,
"Wrong username or password.",
))?;
if hash.is_empty() { if hash.is_empty() {
return Err(Error::BadRequest( return Err(Error::BadRequest(
ErrorKind::UserDeactivated, ErrorKind::UserDeactivated,
"The user has been deactivated" "The user has been deactivated",
)); ));
} }
let hash_matches = let hash_matches = argon2::verify_encoded(&hash, password.as_bytes()).unwrap_or(false);
argon2::verify_encoded(&hash, password.as_bytes()).unwrap_or(false);
if !hash_matches { if !hash_matches {
return Err(Error::BadRequest(ErrorKind::Forbidden, "Wrong username or password.")); return Err(Error::BadRequest(
ErrorKind::Forbidden,
"Wrong username or password.",
));
} }
user_id user_id
} else { }
return Err(Error::BadRequest(ErrorKind::Forbidden, "Bad login type.")); login::IncomingLoginInfo::Token { token } => {
}; if let Some(jwt_decoding_key) = db.globals.jwt_decoding_key() {
let token = jsonwebtoken::decode::<Claims>(
&token,
&jwt_decoding_key,
&jsonwebtoken::Validation::default(),
)
.map_err(|_| Error::BadRequest(ErrorKind::InvalidUsername, "Token is invalid."))?;
let username = token.claims.sub;
UserId::parse_with_server_name(username, db.globals.server_name()).map_err(
|_| Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."),
)?
} else {
return Err(Error::BadRequest(
ErrorKind::Unknown,
"Token login is not supported (server has no jwt decoding key).",
));
}
}
};
// Generate new device id if the user didn't specify one // Generate new device id if the user didn't specify one
let device_id = body let device_id = body

View File

@ -38,6 +38,7 @@ pub struct Config {
allow_encryption: bool, allow_encryption: bool,
#[serde(default = "false_fn")] #[serde(default = "false_fn")]
allow_federation: bool, allow_federation: bool,
jwt_secret: Option<String>,
} }
fn false_fn() -> bool { fn false_fn() -> bool {

View File

@ -11,12 +11,13 @@ pub const COUNTER: &str = "c";
#[derive(Clone)] #[derive(Clone)]
pub struct Globals { pub struct Globals {
pub actual_destination_cache: Arc<RwLock<HashMap<Box<ServerName>, (String, Option<String>)>>>, // actual_destination, host
pub(super) globals: sled::Tree, pub(super) globals: sled::Tree,
config: Config, config: Config,
keypair: Arc<ruma::signatures::Ed25519KeyPair>, keypair: Arc<ruma::signatures::Ed25519KeyPair>,
reqwest_client: reqwest::Client, reqwest_client: reqwest::Client,
pub actual_destination_cache: Arc<RwLock<HashMap<Box<ServerName>, (String, Option<String>)>>>, // actual_destination, host
dns_resolver: TokioAsyncResolver, dns_resolver: TokioAsyncResolver,
jwt_decoding_key: Option<jsonwebtoken::DecodingKey<'static>>,
} }
impl Globals { impl Globals {
@ -62,6 +63,11 @@ impl Globals {
.build() .build()
.unwrap(); .unwrap();
let jwt_decoding_key = config
.jwt_secret
.as_ref()
.map(|secret| jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()).into_static());
Ok(Self { Ok(Self {
globals, globals,
config, config,
@ -73,6 +79,7 @@ impl Globals {
Error::bad_config("Failed to set up trust dns resolver with system config.") Error::bad_config("Failed to set up trust dns resolver with system config.")
})?, })?,
actual_destination_cache: Arc::new(RwLock::new(HashMap::new())), actual_destination_cache: Arc::new(RwLock::new(HashMap::new())),
jwt_decoding_key,
}) })
} }
@ -126,4 +133,8 @@ impl Globals {
pub fn dns_resolver(&self) -> &TokioAsyncResolver { pub fn dns_resolver(&self) -> &TokioAsyncResolver {
&self.dns_resolver &self.dns_resolver
} }
pub fn jwt_decoding_key(&self) -> Option<&jsonwebtoken::DecodingKey<'_>> {
self.jwt_decoding_key.as_ref()
}
} }

View File

@ -8,7 +8,7 @@ use std::{
use crate::{appservice_server, server_server, utils, Error, PduEvent, Result}; use crate::{appservice_server, server_server, utils, Error, PduEvent, Result};
use federation::transactions::send_transaction_message; use federation::transactions::send_transaction_message;
use log::{error, info}; use log::info;
use rocket::futures::stream::{FuturesUnordered, StreamExt}; use rocket::futures::stream::{FuturesUnordered, StreamExt};
use ruma::{ use ruma::{
api::{appservice, federation, OutgoingRequest}, api::{appservice, federation, OutgoingRequest},