@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
17- import { createClient } from 'matrix-js-sdk/src/matrix' ;
17+ import { AuthType , createClient } from 'matrix-js-sdk/src/matrix' ;
1818import React , { Fragment , ReactNode } from 'react' ;
1919import { MatrixClient } from "matrix-js-sdk/src/client" ;
2020import classNames from "classnames" ;
@@ -34,10 +34,17 @@ import RegistrationForm from '../../views/auth/RegistrationForm';
3434import AccessibleButton from '../../views/elements/AccessibleButton' ;
3535import AuthBody from "../../views/auth/AuthBody" ;
3636import AuthHeader from "../../views/auth/AuthHeader" ;
37- import InteractiveAuth from "../InteractiveAuth" ;
37+ import InteractiveAuth , { InteractiveAuthCallback } from "../InteractiveAuth" ;
3838import Spinner from "../../views/elements/Spinner" ;
3939import { AuthHeaderDisplay } from './header/AuthHeaderDisplay' ;
4040import { 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+ } ;
4148
4249interface IProps {
4350 serverConfig : ValidatedServerConfig ;
@@ -287,9 +294,10 @@ export default class Registration extends React.Component<IProps, IState> {
287294 ) ;
288295 } ;
289296
290- private onUIAuthFinished = async ( success : boolean , response : any ) => {
297+ private onUIAuthFinished : InteractiveAuthCallback = async ( success , response ) => {
298+ debuglog ( "Registration: ui authentication finished: " , { success, response } ) ;
291299 if ( ! success ) {
292- let errorText = response . message || response . toString ( ) ;
300+ let errorText : ReactNode = response . message || response . toString ( ) ;
293301 // can we give a better error message?
294302 if ( response . errcode === 'M_RESOURCE_LIMIT_EXCEEDED' ) {
295303 const errorTop = messageForResourceLimitError (
@@ -312,10 +320,10 @@ export default class Registration extends React.Component<IProps, IState> {
312320 < p > { errorTop } </ p >
313321 < p > { errorDetail } </ p >
314322 </ 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 ) ) {
316324 let msisdnAvailable = false ;
317325 for ( const flow of response . available_flows ) {
318- msisdnAvailable = msisdnAvailable || flow . stages . includes ( 'm.login.msisdn' ) ;
326+ msisdnAvailable = msisdnAvailable || flow . stages . includes ( AuthType . Msisdn ) ;
319327 }
320328 if ( ! msisdnAvailable ) {
321329 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> {
351359 // starting the registration process. This isn't perfect since it's possible
352360 // the user had a separate guest session they didn't actually mean to replace.
353361 const [ sessionOwner , sessionIsGuest ] = await Lifecycle . getStoredSessionOwner ( ) ;
354- if ( sessionOwner && ! sessionIsGuest && sessionOwner !== response . userId ) {
362+ if ( sessionOwner && ! sessionIsGuest && sessionOwner !== response . user_id ) {
355363 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.` ,
357365 ) ;
358366 newState . differentLoggedInUserId = sessionOwner ;
359367 }
360368
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
362387 await this . props . onLoggedIn ( {
363388 userId : response . user_id ,
364389 deviceId : response . device_id ,
@@ -416,26 +441,17 @@ export default class Registration extends React.Component<IProps, IState> {
416441 } ;
417442
418443 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-
430444 const registerParams = {
431445 username : this . state . formVals . username ,
432446 password : this . state . formVals . password ,
433447 initial_device_display_name : this . props . defaultDeviceDisplayName ,
434448 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
435451 inhibit_login : undefined ,
436452 } ;
437453 if ( auth ) registerParams . auth = auth ;
438- if ( inhibitLogin !== undefined && inhibitLogin !== null ) registerParams . inhibit_login = inhibitLogin ;
454+ debuglog ( "Registration: sending registration request:" , auth ) ;
439455 return this . state . matrixClient . registerRequest ( registerParams ) ;
440456 } ;
441457
@@ -597,22 +613,22 @@ export default class Registration extends React.Component<IProps, IState> {
597613 { _t ( "Continue with previous account" ) }
598614 </ AccessibleButton > </ p >
599615 </ 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 > ;
608616 } 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
612619 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." , { } ,
614621 {
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 > ,
616632 } ,
617633 ) } </ h3 > ;
618634 }
0 commit comments