Merge pull request #889 from vector-im/login_with_access_token

Add abiity to setup session immediately after registration without using /login
This commit is contained in:
Bruno Windels 2022-10-14 08:54:52 +00:00 committed by GitHub
commit 4d5f202d94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 70 additions and 34 deletions

View File

@ -137,7 +137,7 @@ export class Client {
async startRegistration(homeserver, username, password, initialDeviceDisplayName, flowSelector) {
const request = this._platform.request;
const hsApi = new HomeServerApi({homeserver, request});
const registration = new Registration(hsApi, {
const registration = new Registration(homeserver, hsApi, {
username,
password,
initialDeviceDisplayName,
@ -146,6 +146,16 @@ export class Client {
return registration;
}
/** Method to start client after registration or with given access token.
* To start the client after registering, use `startWithAuthData(registration.authData)`.
* `homeserver` won't be resolved or normalized using this method,
* use `lookupHomeserver` first if needed (not needed after registration) */
async startWithAuthData({accessToken, deviceId, userId, homeserver}) {
this._platform.logger.run("startWithAuthData", async (log) => {
await this._createSessionAfterAuth({accessToken, deviceId, userId, homeserver}, true, log);
});
}
async startWithLogin(loginMethod, {inspectAccountSetup} = {}) {
const currentStatus = this._status.get();
if (currentStatus !== LoadStatus.LoginFailed &&
@ -156,23 +166,17 @@ export class Client {
this._resetStatus();
await this._platform.logger.run("login", async log => {
this._status.set(LoadStatus.Login);
const clock = this._platform.clock;
let sessionInfo;
try {
const request = this._platform.request;
const hsApi = new HomeServerApi({homeserver: loginMethod.homeserver, request});
const loginData = await loginMethod.login(hsApi, "Hydrogen", log);
const sessionId = this.createNewSessionId();
sessionInfo = {
id: sessionId,
deviceId: loginData.device_id,
userId: loginData.user_id,
homeServer: loginMethod.homeserver, // deprecate this over time
homeserver: loginMethod.homeserver,
accessToken: loginData.access_token,
lastUsed: clock.now()
};
log.set("id", sessionId);
} catch (err) {
this._error = err;
if (err.name === "HomeServerError") {
@ -191,30 +195,45 @@ export class Client {
}
return;
}
let dehydratedDevice;
if (inspectAccountSetup) {
dehydratedDevice = await this._inspectAccountAfterLogin(sessionInfo, log);
if (dehydratedDevice) {
sessionInfo.deviceId = dehydratedDevice.deviceId;
}
}
await this._platform.sessionInfoStorage.add(sessionInfo);
// loading the session can only lead to
// LoadStatus.Error in case of an error,
// so separate try/catch
try {
await this._loadSessionInfo(sessionInfo, dehydratedDevice, log);
log.set("status", this._status.get());
} catch (err) {
log.catch(err);
// free olm Account that might be contained
dehydratedDevice?.dispose();
this._error = err;
this._status.set(LoadStatus.Error);
}
await this._createSessionAfterAuth(sessionInfo, inspectAccountSetup, log);
});
}
async _createSessionAfterAuth({deviceId, userId, accessToken, homeserver}, inspectAccountSetup, log) {
const id = this.createNewSessionId();
const lastUsed = this._platform.clock.now();
const sessionInfo = {
id,
deviceId,
userId,
homeServer: homeserver, // deprecate this over time
homeserver,
accessToken,
lastUsed,
};
let dehydratedDevice;
if (inspectAccountSetup) {
dehydratedDevice = await this._inspectAccountAfterLogin(sessionInfo, log);
if (dehydratedDevice) {
sessionInfo.deviceId = dehydratedDevice.deviceId;
}
}
await this._platform.sessionInfoStorage.add(sessionInfo);
// loading the session can only lead to
// LoadStatus.Error in case of an error,
// so separate try/catch
try {
await this._loadSessionInfo(sessionInfo, dehydratedDevice, log);
log.set("status", this._status.get());
} catch (err) {
log.catch(err);
// free olm Account that might be contained
dehydratedDevice?.dispose();
this._error = err;
this._status.set(LoadStatus.Error);
}
}
async _loadSessionInfo(sessionInfo, dehydratedDevice, log) {
log.set("appVersion", this._platform.version);
const clock = this._platform.clock;

View File

@ -164,7 +164,7 @@ export class HomeServerApi {
return this._unauthedRequest("GET", this._url("/login"));
}
register(username: string | null, password: string, initialDeviceDisplayName: string, auth?: Record<string, any>, inhibitLogin: boolean = true , options: BaseRequestOptions = {}): IHomeServerRequest {
register(username: string | null, password: string, initialDeviceDisplayName: string, auth?: Record<string, any>, inhibitLogin: boolean = false , options: BaseRequestOptions = {}): IHomeServerRequest {
options.allowedStatusCodes = [401];
const body: any = {
auth,

View File

@ -25,6 +25,7 @@ import type {
RegistrationResponseMoreDataNeeded,
RegistrationResponse,
RegistrationResponseSuccess,
AuthData,
RegistrationParams,
} from "./types";
@ -34,9 +35,11 @@ export class Registration {
private readonly _hsApi: HomeServerApi;
private readonly _accountDetails: AccountDetails;
private readonly _flowSelector: FlowSelector;
private _sessionInfo?: RegistrationResponseSuccess
private _registerResponse?: RegistrationResponseSuccess;
public readonly homeserver: string;
constructor(hsApi: HomeServerApi, accountDetails: AccountDetails, flowSelector?: FlowSelector) {
constructor(homeserver: string, hsApi: HomeServerApi, accountDetails: AccountDetails, flowSelector?: FlowSelector) {
this.homeserver = homeserver;
this._hsApi = hsApi;
this._accountDetails = accountDetails;
this._flowSelector = flowSelector ?? (flows => flows[0]);
@ -91,7 +94,7 @@ export class Registration {
private async parseRegistrationResponse(response: RegistrationResponse, currentStage: BaseRegistrationStage) {
switch (response.status) {
case 200:
this._sessionInfo = response;
this._registerResponse = response;
return undefined;
case 401:
if (response.completed?.includes(currentStage.type)) {
@ -117,7 +120,14 @@ export class Registration {
}
}
get sessionInfo(): RegistrationResponseSuccess | undefined {
return this._sessionInfo;
get authData(): AuthData | undefined {
if (this._registerResponse) {
return {
accessToken: this._registerResponse.access_token,
homeserver: this.homeserver,
userId: this._registerResponse.user_id,
deviceId: this._registerResponse.device_id,
};
}
}
}

View File

@ -38,6 +38,13 @@ export type RegistrationResponseSuccess = {
status: 200;
}
export type AuthData = {
userId: string;
deviceId: string;
homeserver: string;
accessToken?: string;
}
export type RegistrationFlow = {
stages: string[];
}