2021-07-14 07:07:08 +00:00
use super ::{ DEVICE_ID_LENGTH , TOKEN_LENGTH } ;
2022-10-05 20:34:31 +02:00
use crate ::{ services , utils , Error , Result , Ruma } ;
2023-12-25 16:28:56 +01:00
use argon2 ::{ PasswordHash , PasswordVerifier } ;
2020-07-30 18:14:47 +02:00
use ruma ::{
api ::client ::{
error ::ErrorKind ,
2024-01-20 19:02:44 -05:00
session ::{
get_login_types ,
login ::{
self ,
v3 ::{ DiscoveryInfo , HomeserverInfo } ,
} ,
logout , logout_all ,
} ,
2022-12-14 13:09:10 +01:00
uiaa ::UserIdentifier ,
2020-07-30 18:14:47 +02:00
} ,
UserId ,
} ;
2021-02-07 17:38:45 +01:00
use serde ::Deserialize ;
2024-02-15 20:47:12 -05:00
use tracing ::{ debug , error , info , warn } ;
2021-02-07 17:38:45 +01:00
#[ derive(Debug, Deserialize) ]
struct Claims {
sub : String ,
2022-02-19 12:53:11 +01:00
//exp: usize,
2021-02-07 17:38:45 +01:00
}
2020-07-30 18:14:47 +02:00
2024-01-20 19:02:44 -05:00
/// # `GET /_matrix/client/v3/login`
2020-07-31 14:40:28 +02:00
///
2021-08-31 19:14:37 +02:00
/// Get the supported login types of this server. One of these should be used as the `type` field
2020-07-31 14:40:28 +02:00
/// when logging in.
2022-01-20 11:51:31 +01:00
pub async fn get_login_types_route (
2022-12-14 13:09:10 +01:00
_body : Ruma < get_login_types ::v3 ::Request > ,
2022-02-18 15:33:14 +01:00
) -> Result < get_login_types ::v3 ::Response > {
Ok ( get_login_types ::v3 ::Response ::new ( vec! [
get_login_types ::v3 ::LoginType ::Password ( Default ::default ( ) ) ,
2023-01-18 23:21:23 +01:00
get_login_types ::v3 ::LoginType ::ApplicationService ( Default ::default ( ) ) ,
2022-01-22 16:58:32 +01:00
] ) )
2020-07-30 18:14:47 +02:00
}
2024-01-20 19:02:44 -05:00
/// # `POST /_matrix/client/v3/login`
2020-07-31 14:40:28 +02:00
///
/// Authenticates the user and returns an access token it can use in subsequent requests.
///
2021-08-31 19:14:37 +02:00
/// - The user needs to authenticate using their password (or if enabled using a json web token)
/// - If `device_id` is known: invalidates old access token of that device
/// - If `device_id` is unknown: creates a new device
/// - Returns access token that is associated with the user and device
2020-07-31 14:40:28 +02:00
///
/// Note: You can use [`GET /_matrix/client/r0/login`](fn.get_supported_versions_route.html) to see
/// supported login types.
2022-12-14 13:09:10 +01:00
pub async fn login_route ( body : Ruma < login ::v3 ::Request > ) -> Result < login ::v3 ::Response > {
2020-07-30 18:14:47 +02:00
// Validate login method
2021-02-07 17:38:45 +01:00
// TODO: Other login methods
let user_id = match & body . login_info {
2024-02-15 20:47:12 -05:00
#[ allow(deprecated) ]
2022-12-14 13:09:10 +01:00
login ::v3 ::LoginInfo ::Password ( login ::v3 ::Password {
2021-03-18 19:38:08 +01:00
identifier ,
password ,
2024-02-15 20:47:12 -05:00
user ,
..
2021-10-13 10:16:45 +02:00
} ) = > {
2024-02-15 20:47:12 -05:00
debug! ( " Got password login type " ) ;
let username = if let Some ( UserIdentifier ::UserIdOrLocalpart ( user_id ) ) = identifier {
debug! ( " Using username from identifier field " ) ;
user_id . to_lowercase ( )
} else if let Some ( user_id ) = user {
warn! ( " User \" {} \" is attempting to login with the deprecated \" user \" field at \" /_matrix/client/v3/login \" . conduwuit implements this deprecated behaviour, but this is destined to be removed in a future Matrix release. " , user_id ) ;
2022-03-04 08:08:32 +08:00
user_id . to_lowercase ( )
2021-02-07 17:38:45 +01:00
} else {
2023-07-27 17:02:57 +00:00
warn! ( " Bad login type: {:?} " , & body . login_info ) ;
2021-02-07 17:38:45 +01:00
return Err ( Error ::BadRequest ( ErrorKind ::Forbidden , " Bad login type. " ) ) ;
} ;
2024-02-15 20:47:12 -05:00
2022-10-10 14:09:11 +02:00
let user_id =
UserId ::parse_with_server_name ( username , services ( ) . globals . server_name ( ) )
2024-02-15 20:47:12 -05:00
. map_err ( | e | {
warn! ( " Failed to parse username from user logging in: {} " , e ) ;
2022-10-10 14:09:11 +02:00
Error ::BadRequest ( ErrorKind ::InvalidUsername , " Username is invalid. " )
} ) ? ;
2024-02-15 20:47:12 -05:00
2022-10-05 20:34:31 +02:00
let hash = services ( )
. users
. password_hash ( & user_id ) ?
. ok_or ( Error ::BadRequest (
ErrorKind ::Forbidden ,
" Wrong username or password. " ,
) ) ? ;
2020-07-30 18:14:47 +02:00
if hash . is_empty ( ) {
return Err ( Error ::BadRequest (
ErrorKind ::UserDeactivated ,
2021-02-07 17:38:45 +01:00
" The user has been deactivated " ,
2020-07-30 18:14:47 +02:00
) ) ;
}
2024-02-15 20:47:12 -05:00
2023-12-25 16:28:56 +01:00
let Ok ( parsed_hash ) = PasswordHash ::new ( & hash ) else {
error! ( " error while hashing user {} " , user_id ) ;
return Err ( Error ::BadServerResponse ( " could not hash " ) ) ;
} ;
2024-02-15 20:47:12 -05:00
2023-12-25 16:28:56 +01:00
let hash_matches = services ( )
. globals
. argon
. verify_password ( password . as_bytes ( ) , & parsed_hash )
. is_ok ( ) ;
2024-02-15 20:47:12 -05:00
2020-07-30 18:14:47 +02:00
if ! hash_matches {
2021-02-07 17:38:45 +01:00
return Err ( Error ::BadRequest (
ErrorKind ::Forbidden ,
" Wrong username or password. " ,
) ) ;
2020-07-30 18:14:47 +02:00
}
user_id
2021-02-07 17:38:45 +01:00
}
2022-12-14 13:09:10 +01:00
login ::v3 ::LoginInfo ::Token ( login ::v3 ::Token { token } ) = > {
2024-02-15 20:47:12 -05:00
debug! ( " Got token login type " ) ;
2022-09-06 23:15:09 +02:00
if let Some ( jwt_decoding_key ) = services ( ) . globals . jwt_decoding_key ( ) {
2021-02-07 17:38:45 +01:00
let token = jsonwebtoken ::decode ::< Claims > (
2021-09-13 19:45:56 +02:00
token ,
jwt_decoding_key ,
2021-02-07 17:38:45 +01:00
& jsonwebtoken ::Validation ::default ( ) ,
)
2024-02-15 20:47:12 -05:00
. map_err ( | e | {
warn! ( " Failed to parse JWT token from user logging in: {} " , e ) ;
Error ::BadRequest ( ErrorKind ::InvalidUsername , " Token is invalid. " )
} ) ? ;
2022-08-29 07:15:55 +00:00
let username = token . claims . sub . to_lowercase ( ) ;
2024-02-15 20:47:12 -05:00
2022-09-06 23:15:09 +02:00
UserId ::parse_with_server_name ( username , services ( ) . globals . server_name ( ) ) . map_err (
2024-02-15 20:47:12 -05:00
| e | {
warn! ( " Failed to parse username from user logging in: {} " , e ) ;
Error ::BadRequest ( ErrorKind ::InvalidUsername , " Username is invalid. " )
} ,
2021-02-07 17:38:45 +01:00
) ?
} else {
return Err ( Error ::BadRequest (
ErrorKind ::Unknown ,
" Token login is not supported (server has no jwt decoding key). " ,
) ) ;
}
}
2024-02-15 20:47:12 -05:00
#[ allow(deprecated) ]
login ::v3 ::LoginInfo ::ApplicationService ( login ::v3 ::ApplicationService {
identifier ,
user ,
} ) = > {
debug! ( " Got appservice login type " ) ;
2023-01-18 23:21:23 +01:00
if ! body . from_appservice {
2024-02-15 20:47:12 -05:00
info! ( " User tried logging in as an appservice, but request body is not from a known/registered appservice " ) ;
2023-01-18 23:21:23 +01:00
return Err ( Error ::BadRequest (
ErrorKind ::Forbidden ,
2023-06-08 20:51:34 +02:00
" Forbidden login type. " ,
2023-01-18 23:21:23 +01:00
) ) ;
} ;
2024-02-15 20:47:12 -05:00
let username = if let Some ( UserIdentifier ::UserIdOrLocalpart ( user_id ) ) = identifier {
user_id . to_lowercase ( )
} else if let Some ( user_id ) = user {
warn! ( " Appservice \" {} \" is attempting to login with the deprecated \" user \" field at \" /_matrix/client/v3/login \" . conduwuit implements this deprecated behaviour, but this is destined to be removed in a future Matrix release. " , user_id ) ;
2023-01-18 23:21:23 +01:00
user_id . to_lowercase ( )
} else {
return Err ( Error ::BadRequest ( ErrorKind ::Forbidden , " Bad login type. " ) ) ;
} ;
2023-11-27 00:39:50 -05:00
UserId ::parse_with_server_name ( username , services ( ) . globals . server_name ( ) ) . map_err (
2024-02-15 20:47:12 -05:00
| e | {
warn! ( " Failed to parse username from appservice logging in: {} " , e ) ;
Error ::BadRequest ( ErrorKind ::InvalidUsername , " Username is invalid. " )
} ,
2023-11-27 00:39:50 -05:00
) ?
2023-01-18 23:21:23 +01:00
}
2021-10-13 10:16:45 +02:00
_ = > {
2023-07-27 17:02:57 +00:00
warn! ( " Unsupported or unknown login type: {:?} " , & body . login_info ) ;
2024-02-15 20:47:12 -05:00
debug! ( " JSON body: {:?} " , & body . json_body ) ;
2021-10-13 10:16:45 +02:00
return Err ( Error ::BadRequest (
ErrorKind ::Unknown ,
2024-02-15 20:47:12 -05:00
" Unsupported or unknown login type. " ,
2021-10-13 10:16:45 +02:00
) ) ;
}
2021-02-07 17:38:45 +01:00
} ;
2020-07-30 18:14:47 +02:00
// Generate new device id if the user didn't specify one
let device_id = body
. device_id
. clone ( )
. unwrap_or_else ( | | utils ::random_string ( DEVICE_ID_LENGTH ) . into ( ) ) ;
// Generate a new token for the device
let token = utils ::random_string ( TOKEN_LENGTH ) ;
2021-01-17 08:39:47 -07:00
// Determine if device_id was provided and exists in the db for this user
let device_exists = body . device_id . as_ref ( ) . map_or ( false , | device_id | {
2022-10-05 20:34:31 +02:00
services ( )
. users
2021-01-17 08:39:47 -07:00
. all_device_ids ( & user_id )
2021-03-04 12:35:12 +00:00
. any ( | x | x . as_ref ( ) . map_or ( false , | v | v = = device_id ) )
2021-01-17 08:39:47 -07:00
} ) ;
2021-01-16 22:15:45 -07:00
2021-01-17 08:39:47 -07:00
if device_exists {
2022-09-06 23:15:09 +02:00
services ( ) . users . set_token ( & user_id , & device_id , & token ) ? ;
2021-01-17 08:39:47 -07:00
} else {
2022-09-06 23:15:09 +02:00
services ( ) . users . create_device (
2021-01-16 22:15:45 -07:00
& user_id ,
& device_id ,
& token ,
body . initial_device_display_name . clone ( ) ,
) ? ;
}
2020-07-30 18:14:47 +02:00
2024-01-20 19:02:44 -05:00
// send client well-known if specified so the client knows to reconfigure itself
let client_discovery_info = DiscoveryInfo ::new ( HomeserverInfo ::new (
services ( )
. globals
. well_known_client ( )
. to_owned ( )
. unwrap_or ( " " . to_owned ( ) ) ,
) ) ;
2020-11-15 12:17:21 +01:00
info! ( " {} logged in " , user_id ) ;
2024-01-24 12:15:23 -05:00
// home_server is deprecated but apparently must still be sent despite it being deprecated over 6 years ago.
2024-01-20 19:02:44 -05:00
// initially i thought this macro was unnecessary, but ruma uses this same macro for the same reason so...
#[ allow(deprecated) ]
Ok ( login ::v3 ::Response {
user_id ,
access_token : token ,
device_id ,
well_known : {
if client_discovery_info . homeserver . base_url . as_str ( ) = = " " {
None
} else {
Some ( client_discovery_info )
}
} ,
expires_in : None ,
2024-01-24 12:15:23 -05:00
home_server : Some ( services ( ) . globals . server_name ( ) . to_owned ( ) ) ,
2024-01-20 19:02:44 -05:00
refresh_token : None ,
} )
2020-07-30 18:14:47 +02:00
}
2024-01-20 19:02:44 -05:00
/// # `POST /_matrix/client/v3/logout`
2020-07-31 14:40:28 +02:00
///
/// Log out the current device.
///
2021-08-31 19:14:37 +02:00
/// - Invalidates access token
/// - Deletes device metadata (device id, device display name, last seen ip, last seen ts)
/// - Forgets to-device events
/// - Triggers device list updates
2022-10-05 20:34:31 +02:00
pub async fn logout_route ( body : Ruma < logout ::v3 ::Request > ) -> Result < logout ::v3 ::Response > {
2020-10-18 20:33:12 +02:00
let sender_user = body . sender_user . as_ref ( ) . expect ( " user is authenticated " ) ;
let sender_device = body . sender_device . as_ref ( ) . expect ( " user is authenticated " ) ;
2020-07-30 18:14:47 +02:00
2022-09-06 23:15:09 +02:00
services ( ) . users . remove_device ( sender_user , sender_device ) ? ;
2020-10-21 21:28:02 +02:00
2024-01-09 19:28:17 -05:00
// send device list update for user after logout
services ( ) . users . mark_device_key_update ( sender_user ) ? ;
2022-02-18 15:33:14 +01:00
Ok ( logout ::v3 ::Response ::new ( ) )
2020-07-30 18:14:47 +02:00
}
2020-07-31 14:40:28 +02:00
/// # `POST /_matrix/client/r0/logout/all`
///
/// Log out all devices of this user.
///
/// - Invalidates all access tokens
2021-08-31 19:14:37 +02:00
/// - Deletes all device metadata (device id, device display name, last seen ip, last seen ts)
/// - Forgets all to-device events
/// - Triggers device list updates
2020-07-31 14:40:28 +02:00
///
/// Note: This is equivalent to calling [`GET /_matrix/client/r0/logout`](fn.logout_route.html)
/// from each device of this user.
2020-10-21 21:28:02 +02:00
pub async fn logout_all_route (
2022-02-18 15:33:14 +01:00
body : Ruma < logout_all ::v3 ::Request > ,
) -> Result < logout_all ::v3 ::Response > {
2020-10-18 20:33:12 +02:00
let sender_user = body . sender_user . as_ref ( ) . expect ( " user is authenticated " ) ;
2020-07-30 18:14:47 +02:00
2022-09-06 23:15:09 +02:00
for device_id in services ( ) . users . all_device_ids ( sender_user ) . flatten ( ) {
services ( ) . users . remove_device ( sender_user , & device_id ) ? ;
2020-07-30 18:14:47 +02:00
}
2024-01-09 19:28:17 -05:00
// send device list update for user after logout
services ( ) . users . mark_device_key_update ( sender_user ) ? ;
2022-02-18 15:33:14 +01:00
Ok ( logout_all ::v3 ::Response ::new ( ) )
2020-07-30 18:14:47 +02:00
}