@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
- import { createClient } from 'matrix-js-sdk/src/matrix' ;
17
+ import { AuthType , createClient } from 'matrix-js-sdk/src/matrix' ;
18
18
import React , { Fragment , ReactNode } from 'react' ;
19
19
import { MatrixClient } from "matrix-js-sdk/src/client" ;
20
20
import classNames from "classnames" ;
@@ -34,10 +34,17 @@ import RegistrationForm from '../../views/auth/RegistrationForm';
34
34
import AccessibleButton from '../../views/elements/AccessibleButton' ;
35
35
import AuthBody from "../../views/auth/AuthBody" ;
36
36
import AuthHeader from "../../views/auth/AuthHeader" ;
37
- import InteractiveAuth from "../InteractiveAuth" ;
37
+ import InteractiveAuth , { InteractiveAuthCallback } from "../InteractiveAuth" ;
38
38
import Spinner from "../../views/elements/Spinner" ;
39
39
import { AuthHeaderDisplay } from './header/AuthHeaderDisplay' ;
40
40
import { AuthHeaderProvider } from './header/AuthHeaderProvider' ;
41
+ import SettingsStore from '../../../settings/SettingsStore' ;
42
+
43
+ const debuglog = ( ...args : any [ ] ) => {
44
+ if ( SettingsStore . getValue ( "debug_registration" ) ) {
45
+ logger . log . call ( console , "Registration debuglog:" , ...args ) ;
46
+ }
47
+ } ;
41
48
42
49
interface IProps {
43
50
serverConfig : ValidatedServerConfig ;
@@ -287,9 +294,10 @@ export default class Registration extends React.Component<IProps, IState> {
287
294
) ;
288
295
} ;
289
296
290
- private onUIAuthFinished = async ( success : boolean , response : any ) => {
297
+ private onUIAuthFinished : InteractiveAuthCallback = async ( success , response ) => {
298
+ debuglog ( "Registration: ui authentication finished: " , { success, response } ) ;
291
299
if ( ! success ) {
292
- let errorText = response . message || response . toString ( ) ;
300
+ let errorText : ReactNode = response . message || response . toString ( ) ;
293
301
// can we give a better error message?
294
302
if ( response . errcode === 'M_RESOURCE_LIMIT_EXCEEDED' ) {
295
303
const errorTop = messageForResourceLimitError (
@@ -312,10 +320,10 @@ export default class Registration extends React.Component<IProps, IState> {
312
320
< p > { errorTop } </ p >
313
321
< p > { errorDetail } </ p >
314
322
</ div > ;
315
- } else if ( response . required_stages && response . required_stages . indexOf ( 'm.login.msisdn' ) > - 1 ) {
323
+ } else if ( response . required_stages && response . required_stages . includes ( AuthType . Msisdn ) ) {
316
324
let msisdnAvailable = false ;
317
325
for ( const flow of response . available_flows ) {
318
- msisdnAvailable = msisdnAvailable || flow . stages . includes ( 'm.login.msisdn' ) ;
326
+ msisdnAvailable = msisdnAvailable || flow . stages . includes ( AuthType . Msisdn ) ;
319
327
}
320
328
if ( ! msisdnAvailable ) {
321
329
errorText = _t ( 'This server does not support authentication with a phone number.' ) ;
@@ -351,14 +359,31 @@ export default class Registration extends React.Component<IProps, IState> {
351
359
// starting the registration process. This isn't perfect since it's possible
352
360
// the user had a separate guest session they didn't actually mean to replace.
353
361
const [ sessionOwner , sessionIsGuest ] = await Lifecycle . getStoredSessionOwner ( ) ;
354
- if ( sessionOwner && ! sessionIsGuest && sessionOwner !== response . userId ) {
362
+ if ( sessionOwner && ! sessionIsGuest && sessionOwner !== response . user_id ) {
355
363
logger . log (
356
- `Found a session for ${ sessionOwner } but ${ response . userId } has just registered.` ,
364
+ `Found a session for ${ sessionOwner } but ${ response . user_id } has just registered.` ,
357
365
) ;
358
366
newState . differentLoggedInUserId = sessionOwner ;
359
367
}
360
368
361
- if ( response . access_token ) {
369
+ // if we don't have an email at all, only one client can be involved in this flow, and we can directly log in.
370
+ //
371
+ // if we've got an email, it needs to be verified. in that case, two clients can be involved in this flow, the
372
+ // original client starting the process and the client that submitted the verification token. After the token
373
+ // has been submitted, it can not be used again.
374
+ //
375
+ // we can distinguish them based on whether the client has form values saved (if so, it's the one that started
376
+ // the registration), or whether it doesn't have any form values saved (in which case it's the client that
377
+ // verified the email address)
378
+ //
379
+ // as the client that started registration may be gone by the time we've verified the email, and only the client
380
+ // that verified the email is guaranteed to exist, we'll always do the login in that client.
381
+ const hasEmail = Boolean ( this . state . formVals . email ) ;
382
+ const hasAccessToken = Boolean ( response . access_token ) ;
383
+ debuglog ( "Registration: ui auth finished:" , { hasEmail, hasAccessToken } ) ;
384
+ if ( ! hasEmail && hasAccessToken ) {
385
+ // we'll only try logging in if we either have no email to verify at all or we're the client that verified
386
+ // the email, not the client that started the registration flow
362
387
await this . props . onLoggedIn ( {
363
388
userId : response . user_id ,
364
389
deviceId : response . device_id ,
@@ -416,26 +441,17 @@ export default class Registration extends React.Component<IProps, IState> {
416
441
} ;
417
442
418
443
private makeRegisterRequest = auth => {
419
- // We inhibit login if we're trying to register with an email address: this
420
- // avoids a lot of complex race conditions that can occur if we try to log
421
- // the user in one one or both of the tabs they might end up with after
422
- // clicking the email link.
423
- let inhibitLogin = Boolean ( this . state . formVals . email ) ;
424
-
425
- // Only send inhibitLogin if we're sending username / pw params
426
- // (Since we need to send no params at all to use the ones saved in the
427
- // session).
428
- if ( ! this . state . formVals . password ) inhibitLogin = null ;
429
-
430
444
const registerParams = {
431
445
username : this . state . formVals . username ,
432
446
password : this . state . formVals . password ,
433
447
initial_device_display_name : this . props . defaultDeviceDisplayName ,
434
448
auth : undefined ,
449
+ // we still want to avoid the race conditions involved with multiple clients handling registration, but
450
+ // we'll handle these after we've received the access_token in onUIAuthFinished
435
451
inhibit_login : undefined ,
436
452
} ;
437
453
if ( auth ) registerParams . auth = auth ;
438
- if ( inhibitLogin !== undefined && inhibitLogin !== null ) registerParams . inhibit_login = inhibitLogin ;
454
+ debuglog ( "Registration: sending registration request:" , auth ) ;
439
455
return this . state . matrixClient . registerRequest ( registerParams ) ;
440
456
} ;
441
457
@@ -597,22 +613,22 @@ export default class Registration extends React.Component<IProps, IState> {
597
613
{ _t ( "Continue with previous account" ) }
598
614
</ AccessibleButton > </ p >
599
615
</ div > ;
600
- } else if ( this . state . formVals . password ) {
601
- // We're the client that started the registration
602
- regDoneText = < h3 > { _t (
603
- "<a>Log in</a> to your new account." , { } ,
604
- {
605
- a : ( sub ) => < a href = "#/login" onClick = { this . onLoginClickWithCheck } > { sub } </ a > ,
606
- } ,
607
- ) } </ h3 > ;
608
616
} else {
609
- // We're not the original client: the user probably got to us by clicking the
610
- // email validation link. We can't offer a 'go straight to your account' link
611
- // as we don't have the original creds.
617
+ // regardless of whether we're the client that started the registration or not, we should
618
+ // try our credentials anyway
612
619
regDoneText = < h3 > { _t (
613
- "You can now close this window or <a>log in</a> to your new account." , { } ,
620
+ "<a>Log in</a> to your new account." , { } ,
614
621
{
615
- a : ( sub ) => < a href = "#/login" onClick = { this . onLoginClickWithCheck } > { sub } </ a > ,
622
+ a : ( sub ) => < AccessibleButton
623
+ element = "span"
624
+ className = "mx_linkButton"
625
+ onClick = { async event => {
626
+ const sessionLoaded = await this . onLoginClickWithCheck ( event ) ;
627
+ if ( sessionLoaded ) {
628
+ dis . dispatch ( { action : "view_home_page" } ) ;
629
+ }
630
+ } }
631
+ > { sub } </ AccessibleButton > ,
616
632
} ,
617
633
) } </ h3 > ;
618
634
}
0 commit comments