You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-23 17:02:25 +03:00
Update MSC2965 OIDC Discovery implementation (#4064)
This commit is contained in:
committed by
GitHub
parent
be3913e8a5
commit
a26fc46ed4
@@ -16,7 +16,6 @@ limitations under the License.
|
||||
|
||||
import { IdTokenClaims, Log, OidcClient, SigninResponse, SigninState, WebStorageStateStore } from "oidc-client-ts";
|
||||
|
||||
import { IDelegatedAuthConfig } from "../client";
|
||||
import { subtleCrypto, TextEncoder } from "../crypto/crypto";
|
||||
import { logger } from "../logger";
|
||||
import { randomString } from "../randomstring";
|
||||
@@ -209,7 +208,7 @@ export const completeAuthorizationCodeGrant = async (
|
||||
code: string,
|
||||
state: string,
|
||||
): Promise<{
|
||||
oidcClientSettings: IDelegatedAuthConfig & { clientId: string };
|
||||
oidcClientSettings: { clientId: string; issuer: string };
|
||||
tokenResponse: BearerTokenResponse;
|
||||
homeserverUrl: string;
|
||||
idTokenClaims: IdTokenClaims;
|
||||
|
||||
@@ -14,36 +14,35 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MetadataService, OidcClientSettingsStore, SigningKey } from "oidc-client-ts";
|
||||
import { MetadataService, OidcClientSettingsStore } from "oidc-client-ts";
|
||||
|
||||
import { IDelegatedAuthConfig } from "../client";
|
||||
import { isValidatedIssuerMetadata, ValidatedIssuerMetadata, validateWellKnownAuthentication } from "./validate";
|
||||
import { isValidatedIssuerMetadata, validateOIDCIssuerWellKnown } from "./validate";
|
||||
import { Method, timeoutSignal } from "../http-api";
|
||||
import { OidcClientConfig } from "./index";
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
* Discover and validate delegated auth configuration
|
||||
* - m.authentication config is present and valid
|
||||
* - delegated auth issuer openid-configuration is reachable
|
||||
* - delegated auth issuer openid-configuration is configured correctly for us
|
||||
* Fetches https://oidc-issuer.example.com/.well-known/openid-configuration and other files linked therein.
|
||||
* When successful, validated metadata is returned
|
||||
* @param wellKnown - configuration object as returned
|
||||
* by the .well-known auto-discovery endpoint
|
||||
* @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
|
||||
*/
|
||||
export const discoverAndValidateAuthenticationConfig = async (
|
||||
authenticationConfig?: IDelegatedAuthConfig,
|
||||
): Promise<
|
||||
IDelegatedAuthConfig & {
|
||||
metadata: ValidatedIssuerMetadata;
|
||||
signingKeys?: SigningKey[];
|
||||
}
|
||||
> => {
|
||||
const homeserverAuthenticationConfig = validateWellKnownAuthentication(authenticationConfig);
|
||||
export const discoverAndValidateOIDCIssuerWellKnown = async (issuer: string): Promise<OidcClientConfig> => {
|
||||
const issuerOpenIdConfigUrl = new URL(".well-known/openid-configuration", issuer);
|
||||
const issuerWellKnownResponse = await fetch(issuerOpenIdConfigUrl, {
|
||||
method: Method.Get,
|
||||
signal: timeoutSignal(5000),
|
||||
});
|
||||
const issuerWellKnown = await issuerWellKnownResponse.json();
|
||||
const validatedIssuerConfig = validateOIDCIssuerWellKnown(issuerWellKnown);
|
||||
|
||||
// create a temporary settings store so we can use metadata service for discovery
|
||||
// create a temporary settings store, so we can use metadata service for discovery
|
||||
const settings = new OidcClientSettingsStore({
|
||||
authority: homeserverAuthenticationConfig.issuer,
|
||||
authority: 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
|
||||
});
|
||||
@@ -54,7 +53,7 @@ export const discoverAndValidateAuthenticationConfig = async (
|
||||
isValidatedIssuerMetadata(metadata);
|
||||
|
||||
return {
|
||||
...homeserverAuthenticationConfig,
|
||||
...validatedIssuerConfig,
|
||||
metadata,
|
||||
signingKeys,
|
||||
};
|
||||
|
||||
@@ -14,9 +14,21 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import type { SigningKey } from "oidc-client-ts";
|
||||
import { ValidatedIssuerConfig, ValidatedIssuerMetadata } from "./validate";
|
||||
|
||||
export * from "./authorize";
|
||||
export * from "./discovery";
|
||||
export * from "./error";
|
||||
export * from "./register";
|
||||
export * from "./tokenRefresher";
|
||||
export * from "./validate";
|
||||
|
||||
/**
|
||||
* Validated config for native OIDC authentication, as returned by {@link discoverAndValidateOIDCIssuerWellKnown}.
|
||||
* Contains metadata and signing keys from the issuer's well-known (https://oidc-issuer.example.com/.well-known/openid-configuration).
|
||||
*/
|
||||
export interface OidcClientConfig extends ValidatedIssuerConfig {
|
||||
metadata: ValidatedIssuerMetadata;
|
||||
signingKeys?: SigningKey[];
|
||||
}
|
||||
|
||||
@@ -14,11 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IDelegatedAuthConfig } from "../client";
|
||||
import { OidcClientConfig } from ".";
|
||||
import { OidcError } from "./error";
|
||||
import { Method } from "../http-api";
|
||||
import { logger } from "../logger";
|
||||
import { ValidatedIssuerConfig } from "./validate";
|
||||
import { NonEmptyArray } from "../@types/common";
|
||||
|
||||
/**
|
||||
@@ -112,13 +111,13 @@ const doRegistration = async (
|
||||
|
||||
/**
|
||||
* Attempts dynamic registration against the configured registration endpoint
|
||||
* @param delegatedAuthConfig - Auth config from ValidatedServerConfig
|
||||
* @param delegatedAuthConfig - Auth config from {@link discoverAndValidateOIDCIssuerWellKnown}
|
||||
* @param clientMetadata - The metadata for the client which to register
|
||||
* @returns Promise<string> resolved with registered clientId
|
||||
* @throws when registration is not supported, on failed request or invalid response
|
||||
*/
|
||||
export const registerOidcClient = async (
|
||||
delegatedAuthConfig: IDelegatedAuthConfig & ValidatedIssuerConfig,
|
||||
delegatedAuthConfig: OidcClientConfig,
|
||||
clientMetadata: OidcRegistrationClientMetadata,
|
||||
): Promise<string> => {
|
||||
if (!delegatedAuthConfig.registrationEndpoint) {
|
||||
|
||||
@@ -17,9 +17,8 @@ limitations under the License.
|
||||
import { IdTokenClaims, OidcClient, WebStorageStateStore } from "oidc-client-ts";
|
||||
|
||||
import { AccessTokens } from "../http-api";
|
||||
import { IDelegatedAuthConfig } from "../client";
|
||||
import { generateScope } from "./authorize";
|
||||
import { discoverAndValidateAuthenticationConfig } from "./discovery";
|
||||
import { discoverAndValidateOIDCIssuerWellKnown } from "./discovery";
|
||||
import { logger } from "../logger";
|
||||
|
||||
/**
|
||||
@@ -42,9 +41,9 @@ export class OidcTokenRefresher {
|
||||
|
||||
public constructor(
|
||||
/**
|
||||
* Delegated auth config as found in matrix client .well-known
|
||||
* The OIDC issuer as returned by the /auth_issuer API
|
||||
*/
|
||||
authConfig: IDelegatedAuthConfig,
|
||||
issuer: string,
|
||||
/**
|
||||
* id of this client as registered with the OP
|
||||
*/
|
||||
@@ -63,17 +62,17 @@ export class OidcTokenRefresher {
|
||||
*/
|
||||
private readonly idTokenClaims: IdTokenClaims,
|
||||
) {
|
||||
this.oidcClientReady = this.initialiseOidcClient(authConfig, clientId, deviceId, redirectUri);
|
||||
this.oidcClientReady = this.initialiseOidcClient(issuer, clientId, deviceId, redirectUri);
|
||||
}
|
||||
|
||||
private async initialiseOidcClient(
|
||||
authConfig: IDelegatedAuthConfig,
|
||||
issuer: string,
|
||||
clientId: string,
|
||||
deviceId: string,
|
||||
redirectUri: string,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const config = await discoverAndValidateAuthenticationConfig(authConfig);
|
||||
const config = await discoverAndValidateOIDCIssuerWellKnown(issuer);
|
||||
|
||||
const scope = generateScope(deviceId);
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { OidcMetadata, SigninResponse } from "oidc-client-ts";
|
||||
|
||||
import { IDelegatedAuthConfig } from "../client";
|
||||
import { logger } from "../logger";
|
||||
import { OidcError } from "./error";
|
||||
|
||||
@@ -35,31 +34,6 @@ export type ValidatedIssuerConfig = {
|
||||
accountManagementActionsSupported?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates MSC2965 m.authentication config
|
||||
* Returns valid configuration
|
||||
* @param wellKnown - client well known as returned from ./well-known/client/matrix
|
||||
* @returns config - when present and valid
|
||||
* @throws when config is not found or invalid
|
||||
*/
|
||||
export const validateWellKnownAuthentication = (authentication?: IDelegatedAuthConfig): IDelegatedAuthConfig => {
|
||||
if (!authentication) {
|
||||
throw new Error(OidcError.NotSupported);
|
||||
}
|
||||
|
||||
if (
|
||||
typeof authentication.issuer === "string" &&
|
||||
(!authentication.hasOwnProperty("account") || typeof authentication.account === "string")
|
||||
) {
|
||||
return {
|
||||
issuer: authentication.issuer,
|
||||
account: authentication.account,
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(OidcError.Misconfigured);
|
||||
};
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
!!value && typeof value === "object" && !Array.isArray(value);
|
||||
const requiredStringProperty = (wellKnown: Record<string, unknown>, key: string): boolean => {
|
||||
@@ -150,7 +124,11 @@ export type ValidatedIssuerMetadata = Partial<OidcMetadata> &
|
||||
| "response_types_supported"
|
||||
| "grant_types_supported"
|
||||
| "code_challenge_methods_supported"
|
||||
>;
|
||||
> & {
|
||||
// MSC2965 extensions to the OIDC spec
|
||||
account_management_uri?: string;
|
||||
account_management_actions_supported?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps validateOIDCIssuerWellKnown in a type assertion
|
||||
|
||||
Reference in New Issue
Block a user