Adapt OAuth2 implementation to Matrix Spec v1.18 (#34026)
* Adapt OAuth2 implementation to Matrix Spec v1.18 * Handle more cases of oidc->oauth * Fix test * Fix read back of oauth2 context * Iterate * Fix tests * Discard changes to apps/web/playwright/e2e/settings/account-user-settings-tab.spec.ts * Fix test * Fix test * Fix test * Potential fix for pull request finding 'Unused variable, import, function or class' Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> * Iterate * Iterate * Fix test --------- Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
parent
38e29c51c4
commit
2bc9656957
@@ -57,6 +57,7 @@ describe("<LoggedInView />", () => {
|
||||
setExtendedProfileProperty: jest.fn().mockResolvedValue(undefined),
|
||||
deleteExtendedProfileProperty: jest.fn().mockResolvedValue(undefined),
|
||||
doesServerSupportExtendedProfiles: jest.fn().mockResolvedValue(true),
|
||||
getAuthMetadata: jest.fn().mockRejectedValue(new Error("Legacy auth")),
|
||||
});
|
||||
const mediaHandler = new MediaHandler(mockClient);
|
||||
const mockSdkContext = new TestSDKContext();
|
||||
|
||||
@@ -9,13 +9,19 @@ Please see LICENSE files in the repository root for full details.
|
||||
import React, { type ComponentProps, createRef, type RefObject } from "react";
|
||||
import { fireEvent, render, type RenderResult, screen, waitFor, within, act } from "jest-matrix-react";
|
||||
import { type Mocked, mocked } from "jest-mock-vitest-adapter";
|
||||
import { ClientEvent, type MatrixClient, MatrixEvent, Room, SyncState } from "matrix-js-sdk/src/matrix";
|
||||
import {
|
||||
ClientEvent,
|
||||
type MatrixClient,
|
||||
MatrixEvent,
|
||||
Room,
|
||||
SyncState,
|
||||
OAuth2,
|
||||
type BearerTokenResponse,
|
||||
OAuth2Error,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { type MediaHandler } from "matrix-js-sdk/src/webrtc/mediaHandler";
|
||||
import * as MatrixJs from "matrix-js-sdk/src/matrix";
|
||||
import { completeAuthorizationCodeGrant } from "matrix-js-sdk/src/oidc/authorize";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { OidcError } from "matrix-js-sdk/src/oidc/error";
|
||||
import { type BearerTokenResponse } from "matrix-js-sdk/src/oidc/validate";
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
import {
|
||||
CryptoEvent,
|
||||
@@ -45,7 +51,7 @@ import {
|
||||
unmockClientPeg,
|
||||
} from "../../../test-utils";
|
||||
import * as leaveRoomUtils from "../../../../src/utils/leave-behaviour";
|
||||
import { OidcClientError } from "../../../../src/utils/oidc/error";
|
||||
import { OAuthClientError } from "../../../../src/utils/oauth/error";
|
||||
import { CallStore } from "../../../../src/stores/CallStore";
|
||||
import { type Call } from "../../../../src/models/Call";
|
||||
import { PosthogAnalytics } from "../../../../src/PosthogAnalytics";
|
||||
@@ -68,12 +74,9 @@ import { ShareFormat } from "../../../../src/dispatcher/payloads/SharePayload.ts
|
||||
import { clearStorage } from "../../../../src/Lifecycle";
|
||||
import UserSettingsDialog from "../../../../src/components/views/dialogs/UserSettingsDialog.tsx";
|
||||
import { SDKContextClass } from "../../../../src/contexts/SDKContextClass";
|
||||
import { makeDelegatedAuthConfig } from "../../../test-utils/oidc.ts";
|
||||
import { makeDelegatedAuthMetadata } from "../../../test-utils/auth.ts";
|
||||
import { type QrLoginCredentials } from "../../../../src/components/views/auth/LoginWithQR.tsx";
|
||||
|
||||
jest.mock("matrix-js-sdk/src/oidc/authorize", () => ({
|
||||
completeAuthorizationCodeGrant: jest.fn(),
|
||||
}));
|
||||
import { storeAuthContext } from "../../../../src/utils/oauth/persistOAuthSettings.ts";
|
||||
|
||||
// Stub out ThemeWatcher as the necessary bits for themes are done in element-web's index.html and thus are lacking here,
|
||||
// plus JSDOM's implementation of CSSStyleDeclaration has a bunch of differences to real browsers which cause issues.
|
||||
@@ -193,6 +196,7 @@ describe("<MatrixChat />", () => {
|
||||
logout: jest.fn(),
|
||||
getDeviceId: jest.fn(),
|
||||
forget: () => Promise.resolve(),
|
||||
getAuthMetadata: jest.fn().mockRejectedValue(new Error("Legacy auth")),
|
||||
});
|
||||
let mockClient: Mocked<MatrixClient>;
|
||||
const serverConfig = {
|
||||
@@ -268,6 +272,10 @@ describe("<MatrixChat />", () => {
|
||||
bootstrapDeferred = Promise.withResolvers();
|
||||
|
||||
await clearAllModals();
|
||||
|
||||
jest.spyOn(OAuth2.prototype, "completeAuthorizationCodeGrant").mockImplementation(
|
||||
(code) => new Promise<BearerTokenResponse>(() => {}),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -341,16 +349,16 @@ describe("<MatrixChat />", () => {
|
||||
|
||||
describe("qr login", () => {
|
||||
beforeEach(() => {
|
||||
const authConfig = makeDelegatedAuthConfig();
|
||||
const authConfig = makeDelegatedAuthMetadata();
|
||||
defaultProps.config.validated_server_config!.delegatedAuthentication = authConfig;
|
||||
fetchMock.post(authConfig.registration_endpoint!, { client_id: "abc123" });
|
||||
mockPlatformPeg({
|
||||
getOidcClientMetadata: jest.fn().mockReturnValue({
|
||||
clientName: "App name",
|
||||
clientUri: "https://company",
|
||||
redirectUris: ["https://app"],
|
||||
logoUri: "https://company/logo.png",
|
||||
applicationType: "web",
|
||||
getOAuthClientMetadata: jest.fn().mockReturnValue({
|
||||
client_name: "App name",
|
||||
client_uri: "https://company",
|
||||
redirect_uris: ["https://app"],
|
||||
logo_uri: "https://company/logo.png",
|
||||
application_type: "web",
|
||||
}),
|
||||
});
|
||||
jest.spyOn(qrLogin, "signInByGeneratingQR").mockReturnValue(new Promise(() => {}));
|
||||
@@ -384,8 +392,6 @@ describe("<MatrixChat />", () => {
|
||||
accessToken: "at",
|
||||
homeserverUrl: "https://homeserver",
|
||||
clientId: "ci",
|
||||
idToken: "it",
|
||||
issuer: defaultProps.config.validated_server_config!.delegatedAuthentication!.issuer,
|
||||
deviceId: "di",
|
||||
secrets: {
|
||||
cross_signing: {
|
||||
@@ -452,7 +458,6 @@ describe("<MatrixChat />", () => {
|
||||
});
|
||||
|
||||
describe("when query params have a OIDC params", () => {
|
||||
const issuer = "https://auth.com/";
|
||||
const homeserverUrl = "https://matrix.org";
|
||||
const identityServerUrl = "https://is.org";
|
||||
const clientId = "xyz789";
|
||||
@@ -460,7 +465,7 @@ describe("<MatrixChat />", () => {
|
||||
const code = "test-oidc-auth-code";
|
||||
const state = "test-oidc-state";
|
||||
const urlParams = {
|
||||
oidc_fragment: {
|
||||
oauth2: {
|
||||
code,
|
||||
state: state,
|
||||
},
|
||||
@@ -472,15 +477,14 @@ describe("<MatrixChat />", () => {
|
||||
const tokenResponse: BearerTokenResponse = {
|
||||
access_token: accessToken,
|
||||
refresh_token: undefined,
|
||||
id_token: "ghi789",
|
||||
scope: "test",
|
||||
token_type: "Bearer",
|
||||
expires_at: 12345,
|
||||
expires_in: 12345,
|
||||
};
|
||||
|
||||
let loginClient!: ReturnType<typeof getMockClientWithEventEmitter>;
|
||||
|
||||
const expectOIDCError = async (
|
||||
const expectOAuthError = async (
|
||||
errorMessage = "Something went wrong during authentication. Go to the sign in page and try again.",
|
||||
): Promise<void> => {
|
||||
await flushPromises();
|
||||
@@ -490,24 +494,7 @@ describe("<MatrixChat />", () => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mocked(completeAuthorizationCodeGrant)
|
||||
.mockClear()
|
||||
.mockResolvedValue({
|
||||
oidcClientSettings: {
|
||||
clientId,
|
||||
issuer,
|
||||
},
|
||||
tokenResponse,
|
||||
homeserverUrl,
|
||||
identityServerUrl,
|
||||
idTokenClaims: {
|
||||
aud: "123",
|
||||
iss: issuer,
|
||||
sub: "123",
|
||||
exp: 123,
|
||||
iat: 456,
|
||||
},
|
||||
});
|
||||
mocked(OAuth2.prototype.completeAuthorizationCodeGrant).mockResolvedValue(tokenResponse);
|
||||
|
||||
loginClient = getMockClientWithEventEmitter(getMockClientMethods());
|
||||
// this is used to create a temporary client during login
|
||||
@@ -521,11 +508,23 @@ describe("<MatrixChat />", () => {
|
||||
device_id: deviceId,
|
||||
is_guest: false,
|
||||
});
|
||||
storeAuthContext({
|
||||
homeserverUrl,
|
||||
identityServerUrl,
|
||||
metadata: makeDelegatedAuthMetadata(),
|
||||
state,
|
||||
authContext: {
|
||||
clientId,
|
||||
codeVerifier: "123456",
|
||||
deviceId,
|
||||
redirectUri: "https://cb",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail when query params do not include valid code and state", async () => {
|
||||
it("should fail when fragment params do not include valid code and state", async () => {
|
||||
const urlParams = {
|
||||
oidc_query: {
|
||||
oauth2: {
|
||||
code: "",
|
||||
state: "abc",
|
||||
},
|
||||
@@ -535,11 +534,11 @@ describe("<MatrixChat />", () => {
|
||||
await flushPromises();
|
||||
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
"Failed to login via OIDC",
|
||||
new Error(OidcClientError.InvalidQueryParameters),
|
||||
"Failed to login via OAuth",
|
||||
new Error(OAuthClientError.InvalidFragmentParameters),
|
||||
);
|
||||
|
||||
await expectOIDCError();
|
||||
await expectOAuthError();
|
||||
});
|
||||
|
||||
it("should make correct request to complete authorization", async () => {
|
||||
@@ -547,7 +546,7 @@ describe("<MatrixChat />", () => {
|
||||
|
||||
await flushPromises();
|
||||
|
||||
expect(completeAuthorizationCodeGrant).toHaveBeenCalledWith(code, state, "fragment");
|
||||
expect(OAuth2.prototype.completeAuthorizationCodeGrant).toHaveBeenCalledWith(code);
|
||||
});
|
||||
|
||||
it("should look up userId using access token", async () => {
|
||||
@@ -571,10 +570,10 @@ describe("<MatrixChat />", () => {
|
||||
await flushPromises();
|
||||
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
"Failed to login via OIDC",
|
||||
"Failed to login via OAuth",
|
||||
new Error("Failed to retrieve userId using accessToken"),
|
||||
);
|
||||
await expectOIDCError();
|
||||
await expectOAuthError();
|
||||
});
|
||||
|
||||
it("should call onTokenLoginCompleted", async () => {
|
||||
@@ -586,23 +585,26 @@ describe("<MatrixChat />", () => {
|
||||
|
||||
describe("when login fails", () => {
|
||||
beforeEach(() => {
|
||||
mocked(completeAuthorizationCodeGrant).mockRejectedValue(new Error(OidcError.CodeExchangeFailed));
|
||||
mocked(OAuth2.prototype.completeAuthorizationCodeGrant).mockRejectedValue(
|
||||
new Error(OAuth2Error.CodeExchangeFailed),
|
||||
);
|
||||
});
|
||||
|
||||
it("should log and return to welcome page with correct error when login state is not found", async () => {
|
||||
mocked(completeAuthorizationCodeGrant).mockRejectedValue(
|
||||
new Error(OidcError.MissingOrInvalidStoredState),
|
||||
sessionStorage.clear();
|
||||
mocked(OAuth2.prototype.completeAuthorizationCodeGrant).mockRejectedValue(
|
||||
new Error(OAuth2Error.MissingOrInvalidStoredState),
|
||||
);
|
||||
getComponent({ urlParams });
|
||||
|
||||
await flushPromises();
|
||||
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
"Failed to login via OIDC",
|
||||
new Error(OidcError.MissingOrInvalidStoredState),
|
||||
"Failed to login via OAuth",
|
||||
new Error(OAuth2Error.MissingOrInvalidStoredState),
|
||||
);
|
||||
|
||||
await expectOIDCError(
|
||||
await expectOAuthError(
|
||||
"We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.",
|
||||
);
|
||||
});
|
||||
@@ -613,12 +615,12 @@ describe("<MatrixChat />", () => {
|
||||
await flushPromises();
|
||||
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
"Failed to login via OIDC",
|
||||
new Error(OidcError.CodeExchangeFailed),
|
||||
"Failed to login via OAuth",
|
||||
new Error(OAuth2Error.CodeExchangeFailed),
|
||||
);
|
||||
|
||||
// warning dialog
|
||||
await expectOIDCError();
|
||||
await expectOAuthError();
|
||||
});
|
||||
|
||||
it("should not clear storage", async () => {
|
||||
@@ -636,7 +638,6 @@ describe("<MatrixChat />", () => {
|
||||
await flushPromises();
|
||||
|
||||
expect(sessionStorageSetSpy).not.toHaveBeenCalledWith("mx_oidc_client_id", clientId);
|
||||
expect(sessionStorageSetSpy).not.toHaveBeenCalledWith("mx_oidc_token_issuer", issuer);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -658,7 +659,6 @@ describe("<MatrixChat />", () => {
|
||||
getComponent({ urlParams });
|
||||
|
||||
await waitFor(() => expect(localStorage.getItem("mx_oidc_client_id")).toEqual(clientId));
|
||||
await waitFor(() => expect(localStorage.getItem("mx_oidc_token_issuer")).toEqual(issuer));
|
||||
});
|
||||
|
||||
it("should set logged in and start MatrixClient", async () => {
|
||||
|
||||
@@ -9,22 +9,26 @@ import React from "react";
|
||||
import { fireEvent, render, screen, waitForElementToBeRemoved } from "jest-matrix-react";
|
||||
import { mocked, type MockedObject } from "jest-mock-vitest-adapter";
|
||||
import fetchMock from "@fetch-mock/jest";
|
||||
import { DELEGATED_OIDC_COMPATIBILITY, IdentityProviderBrand, type OidcClientConfig } from "matrix-js-sdk/src/matrix";
|
||||
import {
|
||||
OAUTH_AWARE_PREFERRED_FLOW_FIELD,
|
||||
IdentityProviderBrand,
|
||||
type ValidatedAuthMetadata,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import * as Matrix from "matrix-js-sdk/src/matrix";
|
||||
import { OidcError } from "matrix-js-sdk/src/oidc/error";
|
||||
import { OAuth2Error } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import SdkConfig from "../../../../../src/SdkConfig";
|
||||
import { mkServerConfig, mockPlatformPeg, unmockPlatformPeg } from "../../../../test-utils";
|
||||
import Login from "../../../../../src/components/structures/auth/Login";
|
||||
import type BasePlatform from "../../../../../src/BasePlatform";
|
||||
import * as registerClientUtils from "../../../../../src/utils/oidc/registerClient";
|
||||
import { makeDelegatedAuthConfig } from "../../../../test-utils/oidc";
|
||||
import * as registerClientUtils from "../../../../../src/utils/oauth/registerClient";
|
||||
import { makeDelegatedAuthMetadata } from "../../../../test-utils/auth";
|
||||
import { ModuleApi } from "../../../../../src/modules/Api.ts";
|
||||
|
||||
jest.useRealTimers();
|
||||
|
||||
const oidcStaticClientsConfig = {
|
||||
const oauthStaticClientsConfig = {
|
||||
"https://staticallyregisteredissuer.org/": {
|
||||
client_id: "static-clientId-123",
|
||||
},
|
||||
@@ -42,7 +46,7 @@ describe("Login", function () {
|
||||
SdkConfig.put({
|
||||
brand: "test-brand",
|
||||
disable_custom_urls: true,
|
||||
oidc_static_clients: oidcStaticClientsConfig,
|
||||
oidc_static_clients: oauthStaticClientsConfig,
|
||||
});
|
||||
mockClient.login.mockClear().mockResolvedValue({
|
||||
access_token: "TOKEN",
|
||||
@@ -72,7 +76,7 @@ describe("Login", function () {
|
||||
function getRawComponent(
|
||||
hsUrl = "https://matrix.org",
|
||||
isUrl = "https://vector.im",
|
||||
delegatedAuthentication?: OidcClientConfig,
|
||||
delegatedAuthentication?: ValidatedAuthMetadata,
|
||||
) {
|
||||
return (
|
||||
<Login
|
||||
@@ -84,7 +88,7 @@ describe("Login", function () {
|
||||
);
|
||||
}
|
||||
|
||||
function getComponent(hsUrl?: string, isUrl?: string, delegatedAuthentication?: OidcClientConfig) {
|
||||
function getComponent(hsUrl?: string, isUrl?: string, delegatedAuthentication?: ValidatedAuthMetadata) {
|
||||
return render(getRawComponent(hsUrl, isUrl, delegatedAuthentication));
|
||||
}
|
||||
|
||||
@@ -269,7 +273,7 @@ describe("Login", function () {
|
||||
flows: [
|
||||
{
|
||||
type: "m.login.sso",
|
||||
[DELEGATED_OIDC_COMPATIBILITY.name]: true,
|
||||
[OAUTH_AWARE_PREFERRED_FLOW_FIELD.name]: true,
|
||||
},
|
||||
{
|
||||
type: "m.login.password",
|
||||
@@ -393,7 +397,7 @@ describe("Login", function () {
|
||||
const hsUrl = "https://matrix.org";
|
||||
const isUrl = "https://vector.im";
|
||||
const issuer = "https://test.com/";
|
||||
const delegatedAuth = makeDelegatedAuthConfig(issuer);
|
||||
const delegatedAuth = makeDelegatedAuthMetadata(issuer);
|
||||
beforeEach(() => {
|
||||
jest.spyOn(logger, "error");
|
||||
});
|
||||
@@ -402,9 +406,9 @@ describe("Login", function () {
|
||||
jest.spyOn(logger, "error").mockRestore();
|
||||
});
|
||||
|
||||
it("should attempt to register oidc client", async () => {
|
||||
it("should attempt to register oauth client", async () => {
|
||||
// dont mock, spy so we can check config values were correctly passed
|
||||
jest.spyOn(registerClientUtils, "getOidcClientId");
|
||||
jest.spyOn(registerClientUtils, "getOAuthClientId");
|
||||
fetchMock.post(delegatedAuth.registration_endpoint!, { status: 500 });
|
||||
getComponent(hsUrl, isUrl, delegatedAuth);
|
||||
|
||||
@@ -413,7 +417,7 @@ describe("Login", function () {
|
||||
// tried to register
|
||||
expect(fetchMock).toHaveFetched(delegatedAuth.registration_endpoint);
|
||||
// called with values from config
|
||||
expect(registerClientUtils.getOidcClientId).toHaveBeenCalledWith(delegatedAuth, oidcStaticClientsConfig);
|
||||
expect(registerClientUtils.getOAuthClientId).toHaveBeenCalledWith(delegatedAuth, oauthStaticClientsConfig);
|
||||
});
|
||||
|
||||
it("should fallback to normal login when client registration fails", async () => {
|
||||
@@ -425,8 +429,8 @@ describe("Login", function () {
|
||||
// tried to register
|
||||
expect(fetchMock).toHaveFetched(delegatedAuth.registration_endpoint);
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
"Failed to get oidc native flow",
|
||||
new Error(OidcError.DynamicRegistrationFailed),
|
||||
"Failed to get OAuth2 native flow",
|
||||
new Error(OAuth2Error.DynamicRegistrationFailed),
|
||||
);
|
||||
|
||||
// continued with normal setup
|
||||
@@ -436,7 +440,7 @@ describe("Login", function () {
|
||||
});
|
||||
|
||||
// short term during active development, UI will be added in next PRs
|
||||
it("should show continue button when oidc native flow is correctly configured", async () => {
|
||||
it("should show continue button when oauth native flow is correctly configured", async () => {
|
||||
fetchMock.post(delegatedAuth.registration_endpoint!, { client_id: "abc123" });
|
||||
getComponent(hsUrl, isUrl, delegatedAuth);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen, waitFor, waitForElementToBeRemoved } from "jest-matrix-react";
|
||||
import { createClient, type MatrixClient, MatrixError, type OidcClientConfig } from "matrix-js-sdk/src/matrix";
|
||||
import { createClient, type MatrixClient, MatrixError, type ValidatedAuthMetadata } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked, type MockedObject } from "jest-mock-vitest-adapter";
|
||||
import fetchMock from "@fetch-mock/jest";
|
||||
|
||||
@@ -21,11 +21,11 @@ import {
|
||||
unmockPlatformPeg,
|
||||
} from "../../../../test-utils";
|
||||
import Registration from "../../../../../src/components/structures/auth/Registration";
|
||||
import { makeDelegatedAuthConfig } from "../../../../test-utils/oidc";
|
||||
import { startOidcLogin } from "../../../../../src/utils/oidc/authorize";
|
||||
import { makeDelegatedAuthMetadata } from "../../../../test-utils/auth";
|
||||
import { startOAuthLogin } from "../../../../../src/utils/oauth/authorize";
|
||||
|
||||
jest.mock("../../../../../src/utils/oidc/authorize", () => ({
|
||||
startOidcLogin: jest.fn(),
|
||||
jest.mock("../../../../../src/utils/oauth/authorize", () => ({
|
||||
startOAuthLogin: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("matrix-js-sdk/src/matrix", () => ({
|
||||
@@ -92,7 +92,7 @@ describe("Registration", function () {
|
||||
function getRawComponent(
|
||||
hsUrl = defaultHsUrl,
|
||||
isUrl = defaultIsUrl,
|
||||
authConfig?: OidcClientConfig,
|
||||
authConfig?: ValidatedAuthMetadata,
|
||||
mobileRegister?: boolean,
|
||||
) {
|
||||
return (
|
||||
@@ -104,7 +104,12 @@ describe("Registration", function () {
|
||||
);
|
||||
}
|
||||
|
||||
function getComponent(hsUrl?: string, isUrl?: string, authConfig?: OidcClientConfig, mobileRegister?: boolean) {
|
||||
function getComponent(
|
||||
hsUrl?: string,
|
||||
isUrl?: string,
|
||||
authConfig?: ValidatedAuthMetadata,
|
||||
mobileRegister?: boolean,
|
||||
) {
|
||||
return render(getRawComponent(hsUrl, isUrl, authConfig, mobileRegister));
|
||||
}
|
||||
|
||||
@@ -155,7 +160,7 @@ describe("Registration", function () {
|
||||
});
|
||||
|
||||
describe("when delegated authentication is configured and enabled", () => {
|
||||
const authConfig = makeDelegatedAuthConfig();
|
||||
const authConfig = makeDelegatedAuthMetadata();
|
||||
const clientId = "test-client-id";
|
||||
authConfig.prompt_values_supported = ["create"];
|
||||
|
||||
@@ -168,15 +173,6 @@ describe("Registration", function () {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
fetchMock.get(`${defaultHsUrl}/_matrix/client/unstable/org.matrix.msc2965/auth_issuer`, {
|
||||
issuer: authConfig.issuer,
|
||||
});
|
||||
fetchMock.get("https://auth.org/.well-known/openid-configuration", {
|
||||
...authConfig,
|
||||
signingKeys: undefined,
|
||||
});
|
||||
fetchMock.get(authConfig.jwks_uri!, { keys: [] });
|
||||
});
|
||||
|
||||
it("should display oidc-native continue button", async () => {
|
||||
@@ -194,7 +190,7 @@ describe("Registration", function () {
|
||||
|
||||
fireEvent.click(await screen.findByText("Continue"));
|
||||
|
||||
expect(startOidcLogin).toHaveBeenCalledWith(
|
||||
expect(startOAuthLogin).toHaveBeenCalledWith(
|
||||
authConfig,
|
||||
clientId,
|
||||
defaultHsUrl,
|
||||
|
||||
@@ -28,7 +28,7 @@ import { UIFeature } from "../../../../../src/settings/UIFeature";
|
||||
import { SettingLevel } from "../../../../../src/settings/SettingLevel";
|
||||
import { TestSDKContext } from "../../../TestSDKContext.ts";
|
||||
import { type FeatureSettingKey } from "../../../../../src/settings/Settings.tsx";
|
||||
import { mockOpenIdConfiguration } from "../../../../test-utils/oidc.ts";
|
||||
import { makeDelegatedAuthMetadata } from "../../../../test-utils/auth.ts";
|
||||
|
||||
mockPlatformPeg({
|
||||
supportsSpellCheckSettings: jest.fn().mockReturnValue(false),
|
||||
@@ -74,7 +74,7 @@ describe("<UserSettingsDialog />", () => {
|
||||
getPushers: jest.fn().mockResolvedValue([]),
|
||||
getProfileInfo: jest.fn().mockResolvedValue({}),
|
||||
getMediaConfig: jest.fn(),
|
||||
getAuthMetadata: jest.fn().mockResolvedValue(mockOpenIdConfiguration()),
|
||||
getAuthMetadata: jest.fn().mockResolvedValue(makeDelegatedAuthMetadata()),
|
||||
});
|
||||
sdkContext = new TestSDKContext();
|
||||
sdkContext._client = mockClient;
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
RendezvousError,
|
||||
RendezvousIntent,
|
||||
} from "matrix-js-sdk/src/rendezvous";
|
||||
import { mockOpenIdConfiguration } from "matrix-js-sdk/src/testing";
|
||||
import { makeDelegatedAuthMetadata } from "matrix-js-sdk/src/testing";
|
||||
import {
|
||||
AutoDiscovery,
|
||||
AutoDiscoveryAction,
|
||||
@@ -61,7 +61,7 @@ function makeClient() {
|
||||
getClientWellKnown: jest.fn().mockReturnValue({}),
|
||||
getCrypto: jest.fn().mockReturnValue({}),
|
||||
getDomain: jest.fn(),
|
||||
getAuthMetadata: jest.fn().mockReturnValue(mockOpenIdConfiguration()),
|
||||
getAuthMetadata: jest.fn().mockReturnValue(makeDelegatedAuthMetadata()),
|
||||
} as unknown as MatrixClient);
|
||||
|
||||
cli.http = new MatrixHttpApi(cli, {
|
||||
@@ -330,24 +330,23 @@ describe("<LoginWithQR />", () => {
|
||||
test("should handle qr login", async () => {
|
||||
fetchMock.get("https://hs/_matrix/client/versions", {
|
||||
unstable_features: {},
|
||||
versions: ["v1.1", "v1.5", "v1.6", "v1.8", "v1.9"],
|
||||
versions: ["v1.1", "v1.5", "v1.6", "v1.8", "v1.9", "v1.15"],
|
||||
});
|
||||
const authMetadata = {
|
||||
...mockOpenIdConfiguration("https://auth.org/", [OAuthGrantType.DeviceAuthorization]),
|
||||
jwks_uri: undefined,
|
||||
};
|
||||
fetchMock.get("https://hs/_matrix/client/unstable/org.matrix.msc2965/auth_metadata", authMetadata);
|
||||
const authMetadata = makeDelegatedAuthMetadata("https://auth.org/", [
|
||||
OAuthGrantType.DeviceAuthorization,
|
||||
]);
|
||||
fetchMock.get("https://hs/_matrix/client/v1/auth_metadata", authMetadata);
|
||||
fetchMock.post(authMetadata.registration_endpoint!, {
|
||||
client_id: "!client_id!",
|
||||
});
|
||||
|
||||
mockPlatformPeg({
|
||||
getOidcClientMetadata: jest.fn().mockReturnValue({
|
||||
clientName: "App name",
|
||||
clientUri: "https://company",
|
||||
redirectUris: ["https://app"],
|
||||
logoUri: "https://company/logo.png",
|
||||
applicationType: "web",
|
||||
getOAuthClientMetadata: jest.fn().mockReturnValue({
|
||||
client_name: "App name",
|
||||
client_uri: "https://company",
|
||||
redirect_uris: ["https://app"],
|
||||
logo_uri: "https://company/logo.png",
|
||||
application_type: "web",
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
+8
-17
@@ -26,7 +26,6 @@ import {
|
||||
flushPromises,
|
||||
} from "../../../../../../test-utils";
|
||||
import { UIFeature } from "../../../../../../../src/settings/UIFeature";
|
||||
import { type OidcClientStore } from "../../../../../../../src/stores/oidc/OidcClientStore";
|
||||
import MatrixClientContext from "../../../../../../../src/contexts/MatrixClientContext";
|
||||
import Modal from "../../../../../../../src/Modal";
|
||||
|
||||
@@ -77,6 +76,8 @@ describe("<AccountUserSettingsTab />", () => {
|
||||
getThreePids: jest.fn(),
|
||||
getIdentityServerUrl: jest.fn(),
|
||||
deleteThreePid: jest.fn(),
|
||||
getMediaConfig: jest.fn(),
|
||||
getAuthMetadata: jest.fn().mockRejectedValue(new Error("not implemented")),
|
||||
});
|
||||
|
||||
mockClient.getCapabilities.mockResolvedValue({});
|
||||
@@ -89,9 +90,6 @@ describe("<AccountUserSettingsTab />", () => {
|
||||
|
||||
stores = new TestSDKContext();
|
||||
stores._client = mockClient;
|
||||
// stub out this store completely to avoid mocking initialisation
|
||||
const mockOidcClientStore = {} as unknown as OidcClientStore;
|
||||
jest.spyOn(stores, "oidcClientStore", "get").mockReturnValue(mockOidcClientStore);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -107,10 +105,9 @@ describe("<AccountUserSettingsTab />", () => {
|
||||
|
||||
it("show account management link in expected format", async () => {
|
||||
const accountManagementLink = "https://id.server.org/my-account";
|
||||
const mockOidcClientStore = {
|
||||
accountManagementEndpoint: accountManagementLink,
|
||||
} as unknown as OidcClientStore;
|
||||
jest.spyOn(stores, "oidcClientStore", "get").mockReturnValue(mockOidcClientStore);
|
||||
mockClient.getAuthMetadata.mockResolvedValue({
|
||||
account_management_uri: accountManagementLink,
|
||||
} as any);
|
||||
|
||||
render(getComponent());
|
||||
|
||||
@@ -134,10 +131,9 @@ describe("<AccountUserSettingsTab />", () => {
|
||||
);
|
||||
// account is managed externally when we have delegated auth configured
|
||||
const accountManagementLink = "https://id.server.org/my-account";
|
||||
const mockOidcClientStore = {
|
||||
accountManagementEndpoint: accountManagementLink,
|
||||
} as unknown as OidcClientStore;
|
||||
jest.spyOn(stores, "oidcClientStore", "get").mockReturnValue(mockOidcClientStore);
|
||||
mockClient.getAuthMetadata.mockResolvedValue({
|
||||
account_management_uri: accountManagementLink,
|
||||
} as any);
|
||||
render(getComponent());
|
||||
|
||||
await flushPromises();
|
||||
@@ -207,11 +203,6 @@ describe("<AccountUserSettingsTab />", () => {
|
||||
|
||||
describe("3pids", () => {
|
||||
beforeEach(() => {
|
||||
const mockOidcClientStore = {
|
||||
accountManagementEndpoint: undefined,
|
||||
} as unknown as OidcClientStore;
|
||||
jest.spyOn(stores, "oidcClientStore", "get").mockReturnValue(mockOidcClientStore);
|
||||
|
||||
mockClient.getCapabilities.mockResolvedValue({
|
||||
"m.3pid_changes": {
|
||||
enabled: true,
|
||||
|
||||
+6
-15
@@ -34,7 +34,6 @@ import {
|
||||
type MatrixClient,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import { mocked, type MockedObject } from "jest-mock-vitest-adapter";
|
||||
import fetchMock from "@fetch-mock/jest";
|
||||
|
||||
import {
|
||||
clearAllModals,
|
||||
@@ -58,8 +57,7 @@ import SettingsStore from "../../../../../../../src/settings/SettingsStore";
|
||||
import { getClientInformationEventType } from "../../../../../../../src/utils/device/clientInformation";
|
||||
import { SDKContext } from "../../../../../../../src/contexts/SDKContext";
|
||||
import { TestSDKContext } from "../../../../../TestSDKContext.ts";
|
||||
import { type OidcClientStore } from "../../../../../../../src/stores/oidc/OidcClientStore";
|
||||
import { makeDelegatedAuthConfig } from "../../../../../../test-utils/oidc";
|
||||
import { makeDelegatedAuthMetadata } from "../../../../../../test-utils/auth";
|
||||
import MatrixClientContext from "../../../../../../../src/contexts/MatrixClientContext";
|
||||
|
||||
mockPlatformPeg();
|
||||
@@ -1180,10 +1178,10 @@ describe("<SessionManagerTab />", () => {
|
||||
describe("for an OIDC-aware server", () => {
|
||||
beforeEach(() => {
|
||||
// just do an ugly mock here to avoid mocking initialisation
|
||||
const mockOidcClientStore = {
|
||||
accountManagementEndpoint: "https://issuer.org/account",
|
||||
} as unknown as OidcClientStore;
|
||||
jest.spyOn(sdkContext, "oidcClientStore", "get").mockReturnValue(mockOidcClientStore);
|
||||
mockClient.getAuthMetadata.mockResolvedValue({
|
||||
...makeDelegatedAuthMetadata(),
|
||||
account_management_uri: "https://issuer.org/account",
|
||||
} as any);
|
||||
});
|
||||
|
||||
// signing out the current device works as usual
|
||||
@@ -1637,7 +1635,7 @@ describe("<SessionManagerTab />", () => {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
const delegatedAuthConfig = makeDelegatedAuthConfig(issuer);
|
||||
const delegatedAuthConfig = makeDelegatedAuthMetadata(issuer);
|
||||
mockClient.getAuthMetadata.mockResolvedValue({
|
||||
...delegatedAuthConfig,
|
||||
grant_types_supported: [
|
||||
@@ -1646,13 +1644,6 @@ describe("<SessionManagerTab />", () => {
|
||||
],
|
||||
});
|
||||
mockCrypto.exportSecretsBundle = jest.fn();
|
||||
fetchMock.route(delegatedAuthConfig.jwks_uri!, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
keys: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("renders qr code login section", async () => {
|
||||
|
||||
@@ -123,6 +123,7 @@ describe("<SpacePanel />", () => {
|
||||
removeListener: jest.fn(),
|
||||
isVersionSupported: jest.fn().mockResolvedValue(true),
|
||||
doesServerSupportUnstableFeature: jest.fn().mockResolvedValue(false),
|
||||
getAuthMetadata: jest.fn().mockRejectedValue(new Error("Legacy auth")),
|
||||
} as unknown as MatrixClient;
|
||||
const sdkContext = new TestSDKContext();
|
||||
const SpacePanel = wrapInSdkContext(wrapInMatrixClientContext(UnwrappedSpacePanel), sdkContext);
|
||||
|
||||
Reference in New Issue
Block a user