Merge branch 'jwt-token-fixed' into 'master'
Add 'm.login.token' authentication See merge request famedly/conduit!5
This commit is contained in:
		
						commit
						098ecb2ca3
					
				
					 7 changed files with 150 additions and 29 deletions
				
			
		
							
								
								
									
										86
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										86
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							|  | @ -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" | ||||||
|  |  | ||||||
|  | @ -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"] | ||||||
|  |  | ||||||
|  | @ -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}, | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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 { | ||||||
|  |  | ||||||
|  | @ -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() | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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}, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue