From f88789f42b880a0e7a711e1509eb8a627939dd3f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 17 Jan 2025 15:01:24 +0000 Subject: [PATCH] Switch OIDC primarily to new `/auth_metadata` API Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/client.ts | 54 +++++++++++++++++++++++++++ src/oidc/discovery.ts | 1 + src/oidc/validate.ts | 3 ++ src/rendezvous/MSC4108SignInWithQR.ts | 5 +-- 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/client.ts b/src/client.ts index 0e49b16091c..529695eb309 100644 --- a/src/client.ts +++ b/src/client.ts @@ -19,6 +19,7 @@ limitations under the License. */ import { Optional } from "matrix-events-sdk"; +import { MetadataService, OidcClientSettingsStore, OidcMetadata } from "oidc-client-ts"; import type { IDeviceKeys, IMegolmSessionData, IOneTimeKey } from "./@types/crypto.ts"; import { ISyncStateData, SetPresence, SyncApi, SyncApiOptions, SyncState } from "./sync.ts"; @@ -247,6 +248,12 @@ import { ImageInfo } from "./@types/media.ts"; import { Capabilities, ServerCapabilities } from "./serverCapabilities.ts"; import { sha256 } from "./digest.ts"; import { keyFromAuthData } from "./common-crypto/key-passphrase.ts"; +import { + discoverAndValidateOIDCIssuerWellKnown, + isValidatedIssuerMetadata, + OidcClientConfig, + validateOIDCIssuerWellKnown, +} from "./oidc/index.ts"; export type Store = IStore; @@ -10328,6 +10335,7 @@ export class MatrixClient extends TypedEventEmitter { + let authMetadata: OidcMetadata | undefined; + try { + authMetadata = await this.http.request(Method.Get, "/auth_metadata", undefined, undefined, { + prefix: ClientPrefix.Unstable + "/org.matrix.msc2965", + }); + } catch (e) { + if (e instanceof MatrixError && e.errcode === "M_UNRECOGNIZED") { + const { issuer } = await this.getAuthIssuer(); + return discoverAndValidateOIDCIssuerWellKnown(issuer); + } + throw e; + } + + const validatedIssuerConfig = validateOIDCIssuerWellKnown(authMetadata); + + // create a temporary settings store, so we can use metadata service for discovery + const settings = new OidcClientSettingsStore({ + authority: validatedIssuerConfig.issuer, + redirect_uri: "", // Not known yet, this is here to make the type checker happy + client_id: "", // Not known yet, this is here to make the type checker happy + }); + const metadataService = new MetadataService(settings); + const metadata = await metadataService.getMetadata(); + const signingKeys = (await metadataService.getSigningKeys()) ?? undefined; + + isValidatedIssuerMetadata(metadata); + + return { + ...validatedIssuerConfig, + metadata, + signingKeys, + }; + } } function getUnstableDelayQueryOpts(delayOpts: SendDelayedEventRequestOpts): QueryDict { diff --git a/src/oidc/discovery.ts b/src/oidc/discovery.ts index 656bb30a5c8..b06373d6a87 100644 --- a/src/oidc/discovery.ts +++ b/src/oidc/discovery.ts @@ -30,6 +30,7 @@ import { OidcClientConfig } from "./index.ts"; * @param issuer - the OIDC issuer as returned by the /auth_issuer API * @returns validated authentication metadata and optionally signing keys * @throws when delegated auth config is invalid or unreachable + * @deprecated in favour of {@link MatrixClient#getAuthMetadata} */ export const discoverAndValidateOIDCIssuerWellKnown = async (issuer: string): Promise => { const issuerOpenIdConfigUrl = new URL(".well-known/openid-configuration", issuer); diff --git a/src/oidc/validate.ts b/src/oidc/validate.ts index ce62e90eb6c..466506118d3 100644 --- a/src/oidc/validate.ts +++ b/src/oidc/validate.ts @@ -21,6 +21,7 @@ import { logger } from "../logger.ts"; import { OidcError } from "./error.ts"; export type ValidatedIssuerConfig = { + issuer: string; authorizationEndpoint: string; tokenEndpoint: string; registrationEndpoint?: string; @@ -78,6 +79,7 @@ export const validateOIDCIssuerWellKnown = (wellKnown: unknown): ValidatedIssuer } const isInvalid = [ + requiredStringProperty(wellKnown, "issuer"), requiredStringProperty(wellKnown, "authorization_endpoint"), requiredStringProperty(wellKnown, "token_endpoint"), requiredStringProperty(wellKnown, "revocation_endpoint"), @@ -92,6 +94,7 @@ export const validateOIDCIssuerWellKnown = (wellKnown: unknown): ValidatedIssuer if (!isInvalid) { return { + issuer: wellKnown["issuer"], authorizationEndpoint: wellKnown["authorization_endpoint"], tokenEndpoint: wellKnown["token_endpoint"], registrationEndpoint: wellKnown["registration_endpoint"], diff --git a/src/rendezvous/MSC4108SignInWithQR.ts b/src/rendezvous/MSC4108SignInWithQR.ts index a22b3149e5a..c8b2b54bf83 100644 --- a/src/rendezvous/MSC4108SignInWithQR.ts +++ b/src/rendezvous/MSC4108SignInWithQR.ts @@ -27,7 +27,7 @@ import { logger } from "../logger.ts"; import { MSC4108SecureChannel } from "./channels/MSC4108SecureChannel.ts"; import { MatrixError } from "../http-api/index.ts"; import { sleep } from "../utils.ts"; -import { DEVICE_CODE_SCOPE, discoverAndValidateOIDCIssuerWellKnown, OidcClientConfig } from "../oidc/index.ts"; +import { DEVICE_CODE_SCOPE, OidcClientConfig } from "../oidc/index.ts"; import { CryptoApi } from "../crypto-api/index.ts"; /** @@ -189,8 +189,7 @@ export class MSC4108SignInWithQR { // MSC4108-Flow: NewScanned -send protocols message let oidcClientConfig: OidcClientConfig | undefined; try { - const { issuer } = await this.client!.getAuthIssuer(); - oidcClientConfig = await discoverAndValidateOIDCIssuerWellKnown(issuer); + oidcClientConfig = await this.client!.getAuthMetadata(); } catch (e) { logger.error("Failed to discover OIDC metadata", e); }