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; const code = this._code;
// TODO: cleanup settings storage // 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.getInt(`oidc_${this._state}_started_at`),
this.platform.settingsStorage.getString(`oidc_${this._state}_nonce`), this.platform.settingsStorage.getString(`oidc_${this._state}_nonce`),
this.platform.settingsStorage.getString(`oidc_${this._state}_code_verifier`), 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}_homeserver`),
this.platform.settingsStorage.getString(`oidc_${this._state}_issuer`), this.platform.settingsStorage.getString(`oidc_${this._state}_issuer`),
this.platform.settingsStorage.getString(`oidc_${this._state}_client_id`), this.platform.settingsStorage.getString(`oidc_${this._state}_client_id`),
this.platform.settingsStorage.getString(`oidc_${this._state}_account_management_url`),
]); ]);
const oidcApi = new OidcApi({ const oidcApi = new OidcApi({
@ -67,7 +68,7 @@ export class CompleteOIDCLoginViewModel extends ViewModel {
encoding: this._encoding, encoding: this._encoding,
crypto: this._crypto, 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); const status = await this._attemptLogin(method);
let error = ""; let error = "";
switch (status) { switch (status) {

View File

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

View File

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

View File

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

View File

@ -25,6 +25,7 @@ export class OIDCLoginMethod implements ILoginMethod {
private readonly _nonce: string; private readonly _nonce: string;
private readonly _redirectUri: string; private readonly _redirectUri: string;
private readonly _oidcApi: OidcApi; private readonly _oidcApi: OidcApi;
private readonly _accountManagementUrl?: string;
public readonly homeserver: string; public readonly homeserver: string;
constructor({ constructor({
@ -34,6 +35,7 @@ export class OIDCLoginMethod implements ILoginMethod {
homeserver, homeserver,
redirectUri, redirectUri,
oidcApi, oidcApi,
accountManagementUrl,
}: { }: {
nonce: string, nonce: string,
code: string, code: string,
@ -41,6 +43,7 @@ export class OIDCLoginMethod implements ILoginMethod {
homeserver: string, homeserver: string,
redirectUri: string, redirectUri: string,
oidcApi: OidcApi, oidcApi: OidcApi,
accountManagementUrl?: string,
}) { }) {
this._oidcApi = oidcApi; this._oidcApi = oidcApi;
this._code = code; this._code = code;
@ -48,6 +51,7 @@ export class OIDCLoginMethod implements ILoginMethod {
this._nonce = nonce; this._nonce = nonce;
this._redirectUri = redirectUri; this._redirectUri = redirectUri;
this.homeserver = homeserver; this.homeserver = homeserver;
this._accountManagementUrl = accountManagementUrl;
} }
async login(hsApi: HomeServerApi, _deviceName: string, log: ILogItem): Promise<Record<string, any>> { 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_issuer = this._oidcApi.issuer;
const oidc_client_id = await this._oidcApi.clientId(); 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; accessTokenExpiresAt?: number;
refreshToken?: string; refreshToken?: string;
oidcIssuer?: string; oidcIssuer?: string;
accountManagementUrl?: string;
lastUsed: number; lastUsed: number;
} }

View File

@ -42,6 +42,7 @@ async function getWellKnownResponse(homeserver, request) {
export async function lookupHomeserver(homeserver, request) { export async function lookupHomeserver(homeserver, request) {
homeserver = normalizeHomeserver(homeserver); homeserver = normalizeHomeserver(homeserver);
let issuer = null; let issuer = null;
let account = null;
const wellKnownResponse = await getWellKnownResponse(homeserver, request); const wellKnownResponse = await getWellKnownResponse(homeserver, request);
if (wellKnownResponse && wellKnownResponse.status === 200) { if (wellKnownResponse && wellKnownResponse.status === 200) {
const {body} = wellKnownResponse; const {body} = wellKnownResponse;
@ -54,6 +55,11 @@ export async function lookupHomeserver(homeserver, request) {
if (typeof wellKnownIssuer === "string") { if (typeof wellKnownIssuer === "string") {
issuer = wellKnownIssuer; 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 disabled: vm => vm.isLoggingOut
}, vm.i18n`Log out`)), }, 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( settingNodes.push(
t.h3("Key backup"), t.h3("Key backup"),
t.view(new KeyBackupSettingsView(vm.keyBackupViewModel)) t.view(new KeyBackupSettingsView(vm.keyBackupViewModel))