Link out to OIDC account management URL if available

This commit is contained in:
Hugh Nimmo-Smith 2022-07-31 17:17:00 +01:00
parent a0ee8a9607
commit 3dc4a4c690
8 changed files with 36 additions and 6 deletions

View File

@ -50,7 +50,7 @@ export class CompleteOIDCLoginViewModel extends ViewModel {
}
const code = this._code;
// TODO: cleanup settings storage
const [startedAt, nonce, codeVerifier, redirectUri, homeserver, issuer, clientId] = await Promise.all([
const [startedAt, nonce, codeVerifier, redirectUri, homeserver, issuer, clientId, accountManagementUrl] = await Promise.all([
this.platform.settingsStorage.getInt(`oidc_${this._state}_started_at`),
this.platform.settingsStorage.getString(`oidc_${this._state}_nonce`),
this.platform.settingsStorage.getString(`oidc_${this._state}_code_verifier`),
@ -58,6 +58,7 @@ export class CompleteOIDCLoginViewModel extends ViewModel {
this.platform.settingsStorage.getString(`oidc_${this._state}_homeserver`),
this.platform.settingsStorage.getString(`oidc_${this._state}_issuer`),
this.platform.settingsStorage.getString(`oidc_${this._state}_client_id`),
this.platform.settingsStorage.getString(`oidc_${this._state}_account_management_url`),
]);
const oidcApi = new OidcApi({
@ -67,7 +68,7 @@ export class CompleteOIDCLoginViewModel extends ViewModel {
encoding: this._encoding,
crypto: this._crypto,
});
const method = new OIDCLoginMethod({oidcApi, nonce, codeVerifier, code, homeserver, startedAt, redirectUri});
const method = new OIDCLoginMethod({oidcApi, nonce, codeVerifier, code, homeserver, startedAt, redirectUri, accountManagementUrl});
const status = await this._attemptLogin(method);
let error = "";
switch (status) {

View File

@ -22,6 +22,7 @@ export class StartOIDCLoginViewModel extends ViewModel {
super(options);
this._isBusy = true;
this._issuer = options.loginOptions.oidc.issuer;
this._accountManagementUrl = options.loginOptions.oidc.account;
this._homeserver = options.loginOptions.homeserver;
this._api = new OidcApi({
issuer: this._issuer,
@ -70,6 +71,7 @@ export class StartOIDCLoginViewModel extends ViewModel {
this.platform.settingsStorage.setString(`oidc_${p.state}_homeserver`, this._homeserver),
this.platform.settingsStorage.setString(`oidc_${p.state}_issuer`, this._issuer),
this.platform.settingsStorage.setString(`oidc_${p.state}_client_id`, clientId),
this.platform.settingsStorage.setString(`oidc_${p.state}_account_management_url`, this._accountManagementUrl),
]);
const link = await this._api.authorizationEndpoint(p);

View File

@ -53,6 +53,7 @@ export class SettingsViewModel extends ViewModel {
this.pushNotifications = new PushNotificationStatus();
this._activeTheme = undefined;
this._logsFeedbackMessage = undefined;
this._accountManagementUrl = null;
}
get _session() {
@ -82,9 +83,16 @@ export class SettingsViewModel extends ViewModel {
if (!import.meta.env.DEV) {
this._activeTheme = await this.platform.themeLoader.getActiveTheme();
}
const {accountManagementUrl} = await this.platform.sessionInfoStorage.get(this._client._sessionId);
this._accountManagementUrl = accountManagementUrl;
this.emitChange("");
}
get accountManagementUrl() {
return this._accountManagementUrl;
}
get closeUrl() {
return this._closeUrl;
}

View File

@ -125,7 +125,7 @@ export class Client {
queryLogin(initialHomeserver) {
return new AbortableOperation(async setAbortable => {
const { homeserver, issuer } = await lookupHomeserver(initialHomeserver, (url, options) => {
const { homeserver, issuer, account } = await lookupHomeserver(initialHomeserver, (url, options) => {
return setAbortable(this._platform.request(url, options));
});
if (issuer) {
@ -140,7 +140,7 @@ export class Client {
return {
homeserver,
oidc: { issuer },
oidc: { issuer, account },
};
} catch (e) {
console.log(e);
@ -202,6 +202,7 @@ export class Client {
if (loginData.oidc_issuer) {
sessionInfo.oidcIssuer = loginData.oidc_issuer;
sessionInfo.oidcClientId = loginData.oidc_client_id;
sessionInfo.accountManagementUrl = loginData.oidc_account_management_url;
}
log.set("id", sessionId);

View File

@ -25,6 +25,7 @@ export class OIDCLoginMethod implements ILoginMethod {
private readonly _nonce: string;
private readonly _redirectUri: string;
private readonly _oidcApi: OidcApi;
private readonly _accountManagementUrl?: string;
public readonly homeserver: string;
constructor({
@ -34,6 +35,7 @@ export class OIDCLoginMethod implements ILoginMethod {
homeserver,
redirectUri,
oidcApi,
accountManagementUrl,
}: {
nonce: string,
code: string,
@ -41,6 +43,7 @@ export class OIDCLoginMethod implements ILoginMethod {
homeserver: string,
redirectUri: string,
oidcApi: OidcApi,
accountManagementUrl?: string,
}) {
this._oidcApi = oidcApi;
this._code = code;
@ -48,6 +51,7 @@ export class OIDCLoginMethod implements ILoginMethod {
this._nonce = nonce;
this._redirectUri = redirectUri;
this.homeserver = homeserver;
this._accountManagementUrl = accountManagementUrl;
}
async login(hsApi: HomeServerApi, _deviceName: string, log: ILogItem): Promise<Record<string, any>> {
@ -68,6 +72,6 @@ export class OIDCLoginMethod implements ILoginMethod {
const oidc_issuer = this._oidcApi.issuer;
const oidc_client_id = await this._oidcApi.clientId();
return { oidc_issuer, oidc_client_id, access_token, refresh_token, expires_in, user_id, device_id };
return { oidc_issuer, oidc_client_id, access_token, refresh_token, expires_in, user_id, device_id, oidc_account_management_url: this._accountManagementUrl };
}
}

View File

@ -24,6 +24,7 @@ interface ISessionInfo {
accessTokenExpiresAt?: number;
refreshToken?: string;
oidcIssuer?: string;
accountManagementUrl?: string;
lastUsed: number;
}

View File

@ -42,6 +42,7 @@ async function getWellKnownResponse(homeserver, request) {
export async function lookupHomeserver(homeserver, request) {
homeserver = normalizeHomeserver(homeserver);
let issuer = null;
let account = null;
const wellKnownResponse = await getWellKnownResponse(homeserver, request);
if (wellKnownResponse && wellKnownResponse.status === 200) {
const {body} = wellKnownResponse;
@ -54,6 +55,11 @@ export async function lookupHomeserver(homeserver, request) {
if (typeof wellKnownIssuer === "string") {
issuer = wellKnownIssuer;
}
const wellKnownAccount = body["org.matrix.msc2965.authentication"]?.["account"];
if (typeof wellKnownAccount === "string") {
account = wellKnownAccount;
}
return {homeserver, issuer};
}
return {homeserver, issuer, account};
}

View File

@ -47,6 +47,13 @@ export class SettingsView extends TemplateView {
disabled: vm => vm.isLoggingOut
}, vm.i18n`Log out`)),
);
settingNodes.push(
t.if(vm => vm.accountManagementUrl, t => {
return t.p([vm.i18n`You can manage your account `, t.a({href: vm.accountManagementUrl, target: "_blank"}, vm.i18n`here`), "."]);
}),
);
settingNodes.push(
t.h3("Key backup"),
t.view(new KeyBackupSettingsView(vm.keyBackupViewModel))