@@ -386,10 +386,13 @@ async fn user_password_login(
386386 username : String ,
387387 password : String ,
388388) -> Result < ( CompatSession , User ) , RouteError > {
389+ // Try getting the localpart out of the MXID
390+ let username = homeserver. localpart ( & username) . unwrap_or ( & username) ;
391+
389392 // Find the user
390393 let user = repo
391394 . user ( )
392- . find_by_username ( & username)
395+ . find_by_username ( username)
393396 . await ?
394397 . filter ( mas_data_model:: User :: is_valid)
395398 . ok_or ( RouteError :: UserNotFound ) ?;
@@ -539,52 +542,43 @@ mod tests {
539542 assert_eq ! ( body[ "errcode" ] , "M_UNRECOGNIZED" ) ;
540543 }
541544
542- /// Test that a user can login with a password using the Matrix
543- /// compatibility API.
544- #[ sqlx:: test( migrator = "mas_storage_pg::MIGRATOR" ) ]
545- async fn test_user_password_login ( pool : PgPool ) {
546- setup ( ) ;
547- let state = TestState :: from_pool ( pool) . await . unwrap ( ) ;
548-
549- // Let's provision a user and add a password to it. This part is hard to test
550- // with just HTTP requests, so we'll use the repository directly.
545+ async fn user_with_password ( state : & TestState , username : & str , password : & str ) {
546+ let mut rng = state. rng ( ) ;
551547 let mut repo = state. repository ( ) . await . unwrap ( ) ;
552548
553549 let user = repo
554550 . user ( )
555- . add ( & mut state. rng ( ) , & state. clock , "alice" . to_owned ( ) )
551+ . add ( & mut rng, & state. clock , username. to_owned ( ) )
552+ . await
553+ . unwrap ( ) ;
554+ let ( version, hash) = state
555+ . password_manager
556+ . hash ( & mut rng, Zeroizing :: new ( password. as_bytes ( ) . to_vec ( ) ) )
556557 . await
557558 . unwrap ( ) ;
558559
560+ repo. user_password ( )
561+ . add ( & mut rng, & state. clock , & user, version, hash, None )
562+ . await
563+ . unwrap ( ) ;
559564 let mxid = state. homeserver_connection . mxid ( & user. username ) ;
560565 state
561566 . homeserver_connection
562567 . provision_user ( & ProvisionRequest :: new ( mxid, & user. sub ) )
563568 . await
564569 . unwrap ( ) ;
565570
566- let ( version, hashed_password) = state
567- . password_manager
568- . hash (
569- & mut state. rng ( ) ,
570- Zeroizing :: new ( "password" . to_owned ( ) . into_bytes ( ) ) ,
571- )
572- . await
573- . unwrap ( ) ;
571+ repo. save ( ) . await . unwrap ( ) ;
572+ }
574573
575- repo. user_password ( )
576- . add (
577- & mut state. rng ( ) ,
578- & state. clock ,
579- & user,
580- version,
581- hashed_password,
582- None ,
583- )
584- . await
585- . unwrap ( ) ;
574+ /// Test that a user can login with a password using the Matrix
575+ /// compatibility API.
576+ #[ sqlx:: test( migrator = "mas_storage_pg::MIGRATOR" ) ]
577+ async fn test_user_password_login ( pool : PgPool ) {
578+ setup ( ) ;
579+ let state = TestState :: from_pool ( pool) . await . unwrap ( ) ;
586580
587- repo . save ( ) . await . unwrap ( ) ;
581+ user_with_password ( & state , "alice" , "password" ) . await ;
588582
589583 // Now let's try to login with the password, without asking for a refresh token.
590584 let request = Request :: post ( "/_matrix/client/v3/login" ) . json ( serde_json:: json!( {
@@ -662,6 +656,50 @@ mod tests {
662656 assert_eq ! ( body, old_body) ;
663657 }
664658
659+ /// Test that a user can login with a password using the Matrix
660+ /// compatibility API, using a MXID as identifier
661+ #[ sqlx:: test( migrator = "mas_storage_pg::MIGRATOR" ) ]
662+ async fn test_user_password_login_mxid ( pool : PgPool ) {
663+ setup ( ) ;
664+ let state = TestState :: from_pool ( pool) . await . unwrap ( ) ;
665+
666+ user_with_password ( & state, "alice" , "password" ) . await ;
667+
668+ // Login with a full MXID as identifier
669+ let request = Request :: post ( "/_matrix/client/v3/login" ) . json ( serde_json:: json!( {
670+ "type" : "m.login.password" ,
671+ "identifier" : {
672+ "type" : "m.id.user" ,
673+ "user" : "@alice:example.com" ,
674+ } ,
675+ "password" : "password" ,
676+ } ) ) ;
677+
678+ let response = state. request ( request) . await ;
679+ response. assert_status ( StatusCode :: OK ) ;
680+ let body: ResponseBody = response. json ( ) ;
681+ assert ! ( !body. access_token. is_empty( ) ) ;
682+ assert_eq ! ( body. device_id. as_ref( ) . unwrap( ) . as_str( ) . len( ) , 10 ) ;
683+ assert_eq ! ( body. user_id, "@alice:example.com" ) ;
684+ assert_eq ! ( body. refresh_token, None ) ;
685+ assert_eq ! ( body. expires_in_ms, None ) ;
686+
687+ // With a MXID, but with the wrong server name
688+ let request = Request :: post ( "/_matrix/client/v3/login" ) . json ( serde_json:: json!( {
689+ "type" : "m.login.password" ,
690+ "identifier" : {
691+ "type" : "m.id.user" ,
692+ "user" : "@alice:something.corp" ,
693+ } ,
694+ "password" : "password" ,
695+ } ) ) ;
696+
697+ let response = state. request ( request) . await ;
698+ response. assert_status ( StatusCode :: FORBIDDEN ) ;
699+ let body: serde_json:: Value = response. json ( ) ;
700+ assert_eq ! ( body[ "errcode" ] , "M_FORBIDDEN" ) ;
701+ }
702+
665703 /// Test that password logins are rate limited.
666704 #[ sqlx:: test( migrator = "mas_storage_pg::MIGRATOR" ) ]
667705 async fn test_password_login_rate_limit ( pool : PgPool ) {
0 commit comments