From 0adc4b3d66241b337c27099928c466f1f9a7789f Mon Sep 17 00:00:00 2001 From: Robert Long Date: Wed, 5 Jan 2022 16:47:53 -0800 Subject: [PATCH] Clean up old auth logic --- src/ConferenceCallManagerHooks.jsx | 333 +------------------------ src/auth/LoginPage.jsx | 2 +- src/auth/RegisterPage.jsx | 2 +- src/auth/useInteractiveLogin.js | 48 ++++ src/auth/useInteractiveRegistration.js | 86 +++++++ src/home/UnauthenticatedView.jsx | 2 +- src/room/RoomAuthView.jsx | 2 +- 7 files changed, 151 insertions(+), 324 deletions(-) create mode 100644 src/auth/useInteractiveLogin.js create mode 100644 src/auth/useInteractiveRegistration.js diff --git a/src/ConferenceCallManagerHooks.jsx b/src/ConferenceCallManagerHooks.jsx index a433d149..cacdff4c 100644 --- a/src/ConferenceCallManagerHooks.jsx +++ b/src/ConferenceCallManagerHooks.jsx @@ -21,9 +21,8 @@ import React, { createContext, useMemo, useContext, - useRef, } from "react"; -import matrix, { InteractiveAuth } from "matrix-js-sdk/src/browser-index"; +import matrix from "matrix-js-sdk/src/browser-index"; import { GroupCallIntent, GroupCallType, @@ -53,13 +52,9 @@ function waitForSync(client) { }); } -async function initClient(clientOptions, guest) { +export async function initClient(clientOptions) { const client = matrix.createClient(clientOptions); - if (guest) { - client.setGuest(true); - } - await client.startClient({ // dirty hack to reduce chance of gappy syncs // should be fixed by spotting gaps and backpaginating @@ -110,13 +105,12 @@ export async function fetchGroupCall( export function ClientProvider({ children }) { const history = useHistory(); const [ - { loading, isAuthenticated, isPasswordlessUser, isGuest, client, userName }, + { loading, isAuthenticated, isPasswordlessUser, client, userName }, setState, ] = useState({ loading: true, isAuthenticated: false, isPasswordlessUser: false, - isGuest: false, client: undefined, userName: null, }); @@ -131,20 +125,16 @@ export function ClientProvider({ children }) { user_id, device_id, access_token, - guest, passwordlessUser, tempPassword, } = JSON.parse(authStore); - const client = await initClient( - { - baseUrl: defaultHomeserver, - accessToken: access_token, - userId: user_id, - deviceId: device_id, - }, - guest - ); + const client = await initClient({ + baseUrl: defaultHomeserver, + accessToken: access_token, + userId: user_id, + deviceId: device_id, + }); localStorage.setItem( "matrix-auth-store", @@ -152,16 +142,16 @@ export function ClientProvider({ children }) { user_id, device_id, access_token, - guest, + passwordlessUser, tempPassword, }) ); - return { client, guest, passwordlessUser }; + return { client, passwordlessUser }; } - return { client: undefined, guest: false }; + return { client: undefined }; } catch (err) { localStorage.removeItem("matrix-auth-store"); throw err; @@ -169,13 +159,12 @@ export function ClientProvider({ children }) { } restore() - .then(({ client, guest, passwordlessUser }) => { + .then(({ client, passwordlessUser }) => { setState({ client, loading: false, isAuthenticated: !!client, isPasswordlessUser: !!passwordlessUser, - isGuest: guest, userName: client?.getUserIdLocalpart(), }); }) @@ -185,168 +174,11 @@ export function ClientProvider({ children }) { loading: false, isAuthenticated: false, isPasswordlessUser: false, - isGuest: false, userName: null, }); }); }, []); - const login = useCallback(async (homeserver, username, password) => { - try { - let loginHomeserverUrl = homeserver.trim(); - - if (!loginHomeserverUrl.includes("://")) { - loginHomeserverUrl = "https://" + loginHomeserverUrl; - } - - try { - const wellKnownUrl = new URL( - "/.well-known/matrix/client", - window.location - ); - const response = await fetch(wellKnownUrl); - const config = await response.json(); - - if (config["m.homeserver"]) { - loginHomeserverUrl = config["m.homeserver"]; - } - } catch (error) {} - - const registrationClient = matrix.createClient(loginHomeserverUrl); - - const { user_id, device_id, access_token } = - await registrationClient.loginWithPassword(username, password); - - const client = await initClient({ - baseUrl: loginHomeserverUrl, - accessToken: access_token, - userId: user_id, - deviceId: device_id, - }); - - localStorage.setItem( - "matrix-auth-store", - JSON.stringify({ user_id, device_id, access_token }) - ); - - setState({ - client, - loading: false, - isAuthenticated: true, - isPasswordlessUser: false, - isGuest: false, - userName: client.getUserIdLocalpart(), - }); - } catch (err) { - localStorage.removeItem("matrix-auth-store"); - setState({ - client: undefined, - loading: false, - isAuthenticated: false, - isPasswordlessUser: false, - isGuest: false, - userName: null, - }); - throw err; - } - }, []); - - const registerGuest = useCallback(async () => { - try { - const registrationClient = matrix.createClient(defaultHomeserver); - - const { user_id, device_id, access_token } = - await registrationClient.registerGuest({}); - - const client = await initClient( - { - baseUrl: defaultHomeserver, - accessToken: access_token, - userId: user_id, - deviceId: device_id, - }, - true - ); - - await client.setProfileInfo("displayname", { - displayname: `Guest ${client.getUserIdLocalpart()}`, - }); - - localStorage.setItem( - "matrix-auth-store", - JSON.stringify({ user_id, device_id, access_token, guest: true }) - ); - - setState({ - client, - loading: false, - isAuthenticated: true, - isGuest: true, - isPasswordlessUser: false, - userName: client.getUserIdLocalpart(), - }); - } catch (err) { - localStorage.removeItem("matrix-auth-store"); - setState({ - client: undefined, - loading: false, - isAuthenticated: false, - isPasswordlessUser: false, - isGuest: false, - userName: null, - }); - throw err; - } - }, []); - - const register = useCallback(async (username, password, passwordlessUser) => { - try { - const registrationClient = matrix.createClient(defaultHomeserver); - - const { user_id, device_id, access_token } = - await registrationClient.register(username, password, null, { - type: "m.login.dummy", - }); - - const client = await initClient({ - baseUrl: defaultHomeserver, - accessToken: access_token, - userId: user_id, - deviceId: device_id, - }); - - const session = { user_id, device_id, access_token, passwordlessUser }; - - if (passwordlessUser) { - session.tempPassword = password; - } - - localStorage.setItem("matrix-auth-store", JSON.stringify(session)); - - setState({ - client, - loading: false, - isGuest: false, - isAuthenticated: true, - isPasswordlessUser: passwordlessUser, - userName: client.getUserIdLocalpart(), - }); - - return client; - } catch (err) { - localStorage.removeItem("matrix-auth-store"); - setState({ - client: undefined, - loading: false, - isGuest: false, - isAuthenticated: false, - isPasswordlessUser: false, - userName: null, - }); - throw err; - } - }, []); - const changePassword = useCallback( async (password) => { const { tempPassword, passwordlessUser, ...existingSession } = JSON.parse( @@ -377,7 +209,6 @@ export function ClientProvider({ children }) { setState({ client, loading: false, - isGuest: false, isAuthenticated: true, isPasswordlessUser: false, userName: client.getUserIdLocalpart(), @@ -395,7 +226,6 @@ export function ClientProvider({ children }) { loading: false, isAuthenticated: true, isPasswordlessUser: !!session.passwordlessUser, - isGuest: false, userName: client.getUserIdLocalpart(), }); } else { @@ -406,7 +236,6 @@ export function ClientProvider({ children }) { loading: false, isAuthenticated: false, isPasswordlessUser: false, - isGuest: false, userName: null, }); } @@ -422,11 +251,7 @@ export function ClientProvider({ children }) { loading, isAuthenticated, isPasswordlessUser, - isGuest, client, - login, - registerGuest, - register, changePassword, logout, userName, @@ -436,11 +261,7 @@ export function ClientProvider({ children }) { loading, isAuthenticated, isPasswordlessUser, - isGuest, client, - login, - registerGuest, - register, changePassword, logout, userName, @@ -496,11 +317,6 @@ export async function createRoom(client, name) { }, }); - await client.setGuestAccess(room_id, { - allowJoin: true, - allowRead: true, - }); - await client.createGroupCall( room_id, GroupCallType.Video, @@ -747,126 +563,3 @@ export function useProfile(client) { return { loading, error, displayName, avatarUrl, saveProfile, success }; } - -export function useInteractiveLogin() { - const { setClient } = useClient(); - const [state, setState] = useState({ loading: false }); - - const auth = useCallback(async (homeserver, username, password) => { - const authClient = matrix.createClient(homeserver); - - const interactiveAuth = new InteractiveAuth({ - matrixClient: authClient, - busyChanged(loading) { - setState((prev) => ({ ...prev, loading })); - }, - async doRequest(auth, _background) { - return authClient.login("m.login.password", { - identifier: { - type: "m.id.user", - user: username, - }, - password, - }); - }, - stateUpdated(nextStage, status) { - console.log({ nextStage, status }); - }, - }); - - const { user_id, access_token, device_id } = - await interactiveAuth.attemptAuth(); - - const client = await initClient({ - baseUrl: defaultHomeserver, - accessToken: access_token, - userId: user_id, - deviceId: device_id, - }); - - setClient(client, { user_id, access_token, device_id }); - - return client; - }, []); - - return [state, auth]; -} - -export function useInteractiveRegistration() { - const { setClient } = useClient(); - const [state, setState] = useState({ privacyPolicyUrl: "#", loading: false }); - - const authClientRef = useRef(); - - useEffect(() => { - authClientRef.current = matrix.createClient(defaultHomeserver); - - authClientRef.current.registerRequest({}).catch((error) => { - const privacyPolicyUrl = - error.data?.params["m.login.terms"]?.policies?.privacy_policy?.en?.url; - - const recaptchaKey = error.data?.params["m.login.recaptcha"]?.public_key; - - if (privacyPolicyUrl || recaptchaKey) { - setState((prev) => ({ ...prev, privacyPolicyUrl, recaptchaKey })); - } - }); - }, []); - - const register = useCallback( - async (username, password, recaptchaResponse, passwordlessUser) => { - const interactiveAuth = new InteractiveAuth({ - matrixClient: authClientRef.current, - busyChanged(loading) { - setState((prev) => ({ ...prev, loading })); - }, - async doRequest(auth, _background) { - return authClientRef.current.registerRequest({ - username, - password, - auth: auth || undefined, - }); - }, - stateUpdated(nextStage, status) { - if (status.error) { - throw new Error(error); - } - - if (nextStage === "m.login.terms") { - interactiveAuth.submitAuthDict({ - type: "m.login.terms", - }); - } else if (nextStage === "m.login.recaptcha") { - interactiveAuth.submitAuthDict({ - type: "m.login.recaptcha", - response: recaptchaResponse, - }); - } - }, - }); - - const { user_id, access_token, device_id } = - await interactiveAuth.attemptAuth(); - - const client = await initClient({ - baseUrl: defaultHomeserver, - accessToken: access_token, - userId: user_id, - deviceId: device_id, - }); - - const session = { user_id, device_id, access_token, passwordlessUser }; - - if (passwordlessUser) { - session.tempPassword = password; - } - - setClient(client, session); - - return client; - }, - [] - ); - - return [state, register]; -} diff --git a/src/auth/LoginPage.jsx b/src/auth/LoginPage.jsx index c5a6841a..e1df8dc6 100644 --- a/src/auth/LoginPage.jsx +++ b/src/auth/LoginPage.jsx @@ -22,9 +22,9 @@ import { Button } from "../button"; import { defaultHomeserver, defaultHomeserverHost, - useInteractiveLogin, } from "../ConferenceCallManagerHooks"; import styles from "./LoginPage.module.css"; +import { useInteractiveLogin } from "./useInteractiveLogin"; export function LoginPage() { const [_, login] = useInteractiveLogin(); diff --git a/src/auth/RegisterPage.jsx b/src/auth/RegisterPage.jsx index c9ed9b82..f584bc24 100644 --- a/src/auth/RegisterPage.jsx +++ b/src/auth/RegisterPage.jsx @@ -21,8 +21,8 @@ import { Button } from "../button"; import { useClient, defaultHomeserverHost, - useInteractiveRegistration, } from "../ConferenceCallManagerHooks"; +import { useInteractiveRegistration } from "./useInteractiveRegistration"; import styles from "./LoginPage.module.css"; import { ReactComponent as Logo } from "../icons/LogoLarge.svg"; import { LoadingView } from "../FullScreenView"; diff --git a/src/auth/useInteractiveLogin.js b/src/auth/useInteractiveLogin.js new file mode 100644 index 00000000..64e42298 --- /dev/null +++ b/src/auth/useInteractiveLogin.js @@ -0,0 +1,48 @@ +import matrix, { InteractiveAuth } from "matrix-js-sdk/src/browser-index"; +import { useState, useCallback } from "react"; +import { + useClient, + initClient, + defaultHomeserver, +} from "../ConferenceCallManagerHooks"; + +export function useInteractiveLogin() { + const { setClient } = useClient(); + const [state, setState] = useState({ loading: false }); + + const auth = useCallback(async (homeserver, username, password) => { + const authClient = matrix.createClient(homeserver); + + const interactiveAuth = new InteractiveAuth({ + matrixClient: authClient, + busyChanged(loading) { + setState((prev) => ({ ...prev, loading })); + }, + async doRequest(_auth, _background) { + return authClient.login("m.login.password", { + identifier: { + type: "m.id.user", + user: username, + }, + password, + }); + }, + }); + + const { user_id, access_token, device_id } = + await interactiveAuth.attemptAuth(); + + const client = await initClient({ + baseUrl: defaultHomeserver, + accessToken: access_token, + userId: user_id, + deviceId: device_id, + }); + + setClient(client, { user_id, access_token, device_id }); + + return client; + }, []); + + return [state, auth]; +} diff --git a/src/auth/useInteractiveRegistration.js b/src/auth/useInteractiveRegistration.js new file mode 100644 index 00000000..350f2fa1 --- /dev/null +++ b/src/auth/useInteractiveRegistration.js @@ -0,0 +1,86 @@ +import matrix, { InteractiveAuth } from "matrix-js-sdk/src/browser-index"; +import { useState, useEffect, useCallback, useRef } from "react"; +import { + useClient, + initClient, + defaultHomeserver, +} from "../ConferenceCallManagerHooks"; + +export function useInteractiveRegistration() { + const { setClient } = useClient(); + const [state, setState] = useState({ privacyPolicyUrl: "#", loading: false }); + + const authClientRef = useRef(); + + useEffect(() => { + authClientRef.current = matrix.createClient(defaultHomeserver); + + authClientRef.current.registerRequest({}).catch((error) => { + const privacyPolicyUrl = + error.data?.params["m.login.terms"]?.policies?.privacy_policy?.en?.url; + + const recaptchaKey = error.data?.params["m.login.recaptcha"]?.public_key; + + if (privacyPolicyUrl || recaptchaKey) { + setState((prev) => ({ ...prev, privacyPolicyUrl, recaptchaKey })); + } + }); + }, []); + + const register = useCallback( + async (username, password, recaptchaResponse, passwordlessUser) => { + const interactiveAuth = new InteractiveAuth({ + matrixClient: authClientRef.current, + busyChanged(loading) { + setState((prev) => ({ ...prev, loading })); + }, + async doRequest(auth, _background) { + return authClientRef.current.registerRequest({ + username, + password, + auth: auth || undefined, + }); + }, + stateUpdated(nextStage, status) { + if (status.error) { + throw new Error(error); + } + + if (nextStage === "m.login.terms") { + interactiveAuth.submitAuthDict({ + type: "m.login.terms", + }); + } else if (nextStage === "m.login.recaptcha") { + interactiveAuth.submitAuthDict({ + type: "m.login.recaptcha", + response: recaptchaResponse, + }); + } + }, + }); + + const { user_id, access_token, device_id } = + await interactiveAuth.attemptAuth(); + + const client = await initClient({ + baseUrl: defaultHomeserver, + accessToken: access_token, + userId: user_id, + deviceId: device_id, + }); + + const session = { user_id, device_id, access_token, passwordlessUser }; + + if (passwordlessUser) { + session.tempPassword = password; + } + + setClient(client, session); + + return client; + }, + [] + ); + + return [state, register]; +} diff --git a/src/home/UnauthenticatedView.jsx b/src/home/UnauthenticatedView.jsx index bb5306ca..f564d049 100644 --- a/src/home/UnauthenticatedView.jsx +++ b/src/home/UnauthenticatedView.jsx @@ -7,9 +7,9 @@ import { Button } from "../button"; import { randomString } from "matrix-js-sdk/src/randomstring"; import { createRoom, - useInteractiveRegistration, roomAliasFromRoomName, } from "../ConferenceCallManagerHooks"; +import { useInteractiveRegistration } from "../auth/useInteractiveRegistration"; import { useModalTriggerState } from "../Modal"; import { JoinExistingCallModal } from "../JoinExistingCallModal"; import { useRecaptcha } from "../auth/useRecaptcha"; diff --git a/src/room/RoomAuthView.jsx b/src/room/RoomAuthView.jsx index 72634e59..d57ac787 100644 --- a/src/room/RoomAuthView.jsx +++ b/src/room/RoomAuthView.jsx @@ -7,7 +7,7 @@ import { useLocation } from "react-router-dom"; import { useRecaptcha } from "../auth/useRecaptcha"; import { FieldRow, InputField, ErrorMessage } from "../Input"; import { randomString } from "matrix-js-sdk/src/randomstring"; -import { useInteractiveRegistration } from "../ConferenceCallManagerHooks"; +import { useInteractiveRegistration } from "../auth/useInteractiveRegistration"; import { Form } from "../form/Form"; export function RoomAuthView() {