diff --git a/src/matrix/Client.js b/src/matrix/Client.js index 6ba1a4b1..def473bb 100644 --- a/src/matrix/Client.js +++ b/src/matrix/Client.js @@ -482,6 +482,17 @@ export class Client { request: this._platform.request }); await hsApi.logout({log}).response(); + const oidcApi = new OidcApi({ + issuer: sessionInfo.oidcIssuer, + clientId: sessionInfo.oidcClientId, + request: this._platform.request, + encoding: this._platform.encoding, + crypto: this._platform.crypto, + }); + await oidcApi.revokeToken({ token: sessionInfo.accessToken, type: "access" }); + if (sessionInfo.refreshToken) { + await oidcApi.revokeToken({ token: sessionInfo.refreshToken, type: "refresh" }); + } } catch (err) {} await this.deleteSession(log); }); diff --git a/src/matrix/net/OidcApi.ts b/src/matrix/net/OidcApi.ts index 1d0db462..5a801952 100644 --- a/src/matrix/net/OidcApi.ts +++ b/src/matrix/net/OidcApi.ts @@ -203,6 +203,11 @@ export class OidcApi { return metadata["registration_endpoint"]; } + async revocationEndpoint(): Promise { + const metadata = await this.metadata(); + return metadata["revocation_endpoint"]; + } + generateDeviceScope(): String { const deviceId = randomString(10); return `urn:matrix:device:${deviceId}`; @@ -281,4 +286,35 @@ export class OidcApi { return token; } + + async revokeToken({ + token, + type, + }: { token: string, type: "refresh" | "access" }): Promise { + const revocationEndpoint = await this.revocationEndpoint(); + if (!revocationEndpoint) { + return; + } + + const params = new URLSearchParams(); + params.append("token_type", type); + params.append("token", token); + params.append("client_id", await this.clientId()); + const body = params.toString(); + + const headers = new Map(); + headers.set("Content-Type", "application/x-www-form-urlencoded"); + + const req = this._requestFn(revocationEndpoint, { + method: "POST", + headers, + format: "json", + body, + }); + + const res = await req.response(); + if (res.status >= 400) { + throw new Error("failed to revoke token"); + } + } }