import { MatrixClient } from "./MatrixClient" ; /**
 * Functions for interacting with Matrix prior to having an access token. Intended
 * to be used for logging in/registering to get a MatrixClient instance.
 *
 * By design, this limits the options used to create the MatrixClient. To specify
 * custom elements to the client, get the access token from the returned client
 * and create a new MatrixClient instance. Due to the nature of Matrix, it is
 * also recommended to use the homeserverUrl from the generated MatrixClient as
 * it may be different from that given to the MatrixAuth class.
 */ export class MatrixAuth { param { string } homeserverUrl public constructor ( private homeserverUrl ) { } /**
     * Generate a client with no access token so we can reuse the doRequest
     * logic already written.
     */ private createTemplateClient ( ) { return new MatrixClient ( this . homeserverUrl , "" ) ; } param { string } localpart param { string } password param { string } deviceName returns { Promise < MatrixClient > } public async passwordRegister ( localpart , password , deviceName ? ) { const body = { username : localpart , password : password , initial_device_display_name : deviceName , } ; let response ; try { response = await this . createTemplateClient ( ) . doRequest ( "POST" , "/_matrix/client/r0/register" , null , body ) ; } catch ( e ) { if ( e . statusCode === 401 ) { if ( typeof ( e . body ) === "string" ) e . body = JSON . parse ( e . body ) ; if ( ! e . body ) throw new Error ( JSON . stringify ( Object . keys ( e ) ) ) ; const sessionId = e . body [ 'session' ] ; const expectedFlow = [ "m.login.dummy" ] ; let hasFlow = false ; for ( const flow of e . body [ 'flows' ] ) { const stages = flow [ 'stages' ] ; if ( stages . length !== expectedFlow . length ) continue ; let stagesMatch = true ; for ( let i = 0 ; i < stages . length ; i ++ ) { if ( stages [ i ] !== expectedFlow [ i ] ) { stagesMatch = false ; break ; } } if ( stagesMatch ) { hasFlow = true ; break ; } } if ( ! hasFlow ) throw new Error ( "Failed to find appropriate login flow in User-Interactive Authentication" ) ; body [ 'auth' ] = { type : expectedFlow [ 0 ] , session : sessionId , } ; response = await this . createTemplateClient ( ) . doRequest ( "POST" , "/_matrix/client/r0/register" , null , body ) ; } } if ( ! response ) throw new Error ( "Failed to register" ) ; const accessToken = response [ 'access_token' ] ; if ( ! accessToken ) throw new Error ( "No access token returned" ) ; return new MatrixClient ( this . homeserverUrl , accessToken ) ; } param { string } username param { string } password param { string } deviceName returns { Promise < MatrixClient > } public async passwordLogin ( username , password , deviceName ? ) { const body = { type : "m.login.password" , identifier : { type : "m.id.user" , user : username , } , password : password , initial_device_display_name : deviceName , } ; const response = await this . createTemplateClient ( ) . doRequest ( "POST" , "/_matrix/client/r0/login" , null , body ) ; const accessToken = response [ "access_token" ] ; if ( ! accessToken ) throw new Error ( "Expected access token in response - got nothing" ) ; let homeserverUrl = this . homeserverUrl ; if ( response [ 'well_known' ] && response [ 'well_known' ] [ 'm.homeserver' ] && response [ 'well_known' ] [ 'm.homeserver' ] [ 'base_url' ] ) { homeserverUrl = response [ 'well_known' ] [ 'm.homeserver' ] [ 'base_url' ] ; } return new MatrixClient ( homeserverUrl , accessToken ) ; } }