Add change password flow for anonymous users
This commit is contained in:
parent
d7fd06a250
commit
a7539eb34e
5 changed files with 181 additions and 45 deletions
|
@ -130,8 +130,14 @@ export function ClientProvider({ homeserverUrl, children }) {
|
||||||
const authStore = localStorage.getItem("matrix-auth-store");
|
const authStore = localStorage.getItem("matrix-auth-store");
|
||||||
|
|
||||||
if (authStore) {
|
if (authStore) {
|
||||||
const { user_id, device_id, access_token, guest, passwordlessUser } =
|
const {
|
||||||
JSON.parse(authStore);
|
user_id,
|
||||||
|
device_id,
|
||||||
|
access_token,
|
||||||
|
guest,
|
||||||
|
passwordlessUser,
|
||||||
|
tempPassword,
|
||||||
|
} = JSON.parse(authStore);
|
||||||
|
|
||||||
const client = await initClient(
|
const client = await initClient(
|
||||||
{
|
{
|
||||||
|
@ -151,6 +157,7 @@ export function ClientProvider({ homeserverUrl, children }) {
|
||||||
access_token,
|
access_token,
|
||||||
guest,
|
guest,
|
||||||
passwordlessUser,
|
passwordlessUser,
|
||||||
|
tempPassword,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -311,10 +318,13 @@ export function ClientProvider({ homeserverUrl, children }) {
|
||||||
deviceId: device_id,
|
deviceId: device_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
localStorage.setItem(
|
const session = { user_id, device_id, access_token, passwordlessUser };
|
||||||
"matrix-auth-store",
|
|
||||||
JSON.stringify({ user_id, device_id, access_token, passwordlessUser })
|
if (passwordlessUser) {
|
||||||
);
|
session.tempPassword = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem("matrix-auth-store", JSON.stringify(session));
|
||||||
|
|
||||||
setState({
|
setState({
|
||||||
client,
|
client,
|
||||||
|
@ -340,6 +350,45 @@ export function ClientProvider({ homeserverUrl, children }) {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const changePassword = useCallback(
|
||||||
|
async (password) => {
|
||||||
|
const { tempPassword, passwordlessUser, ...existingSession } = JSON.parse(
|
||||||
|
localStorage.getItem("matrix-auth-store")
|
||||||
|
);
|
||||||
|
|
||||||
|
await client.setPassword(
|
||||||
|
{
|
||||||
|
type: "m.login.password",
|
||||||
|
identifier: {
|
||||||
|
type: "m.id.user",
|
||||||
|
user: existingSession.user_id,
|
||||||
|
},
|
||||||
|
user: existingSession.user_id,
|
||||||
|
password: tempPassword,
|
||||||
|
},
|
||||||
|
password
|
||||||
|
);
|
||||||
|
|
||||||
|
localStorage.setItem(
|
||||||
|
"matrix-auth-store",
|
||||||
|
JSON.stringify({
|
||||||
|
...existingSession,
|
||||||
|
passwordlessUser: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
setState({
|
||||||
|
client,
|
||||||
|
loading: false,
|
||||||
|
isGuest: false,
|
||||||
|
isAuthenticated: true,
|
||||||
|
isPasswordlessUser: false,
|
||||||
|
userName: client.getUserIdLocalpart(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[client]
|
||||||
|
);
|
||||||
|
|
||||||
const logout = useCallback(() => {
|
const logout = useCallback(() => {
|
||||||
localStorage.removeItem("matrix-auth-store");
|
localStorage.removeItem("matrix-auth-store");
|
||||||
window.location = "/";
|
window.location = "/";
|
||||||
|
@ -355,6 +404,7 @@ export function ClientProvider({ homeserverUrl, children }) {
|
||||||
login,
|
login,
|
||||||
registerGuest,
|
registerGuest,
|
||||||
register,
|
register,
|
||||||
|
changePassword,
|
||||||
logout,
|
logout,
|
||||||
userName,
|
userName,
|
||||||
}),
|
}),
|
||||||
|
@ -367,6 +417,7 @@ export function ClientProvider({ homeserverUrl, children }) {
|
||||||
login,
|
login,
|
||||||
registerGuest,
|
registerGuest,
|
||||||
register,
|
register,
|
||||||
|
changePassword,
|
||||||
logout,
|
logout,
|
||||||
userName,
|
userName,
|
||||||
]
|
]
|
||||||
|
|
|
@ -22,17 +22,30 @@ export function Field({ children, className, ...rest }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InputField = forwardRef(
|
export const InputField = forwardRef(
|
||||||
({ id, label, className, type, checked, prefix, suffix, ...rest }, ref) => {
|
(
|
||||||
|
{ id, label, className, type, checked, prefix, suffix, disabled, ...rest },
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
return (
|
return (
|
||||||
<Field
|
<Field
|
||||||
className={classNames(
|
className={classNames(
|
||||||
type === "checkbox" ? styles.checkboxField : styles.inputField,
|
type === "checkbox" ? styles.checkboxField : styles.inputField,
|
||||||
{ [styles.prefix]: !!prefix },
|
{
|
||||||
|
[styles.prefix]: !!prefix,
|
||||||
|
[styles.disabled]: disabled,
|
||||||
|
},
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{prefix && <span>{prefix}</span>}
|
{prefix && <span>{prefix}</span>}
|
||||||
<input id={id} {...rest} ref={ref} type={type} checked={checked} />
|
<input
|
||||||
|
id={id}
|
||||||
|
{...rest}
|
||||||
|
ref={ref}
|
||||||
|
type={type}
|
||||||
|
checked={checked}
|
||||||
|
disabled={disabled}
|
||||||
|
/>
|
||||||
<label htmlFor={id}>
|
<label htmlFor={id}>
|
||||||
{type === "checkbox" && (
|
{type === "checkbox" && (
|
||||||
<div className={styles.checkbox}>
|
<div className={styles.checkbox}>
|
||||||
|
|
|
@ -33,17 +33,26 @@
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 11px 9px;
|
padding: 12px 9px 10px 9px;
|
||||||
color: var(--textColor1);
|
color: var(--textColor1);
|
||||||
background-color: var(--bgColor1);
|
background-color: var(--bgColor1);
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inputField.disabled input,
|
||||||
|
.inputField.disabled span {
|
||||||
|
color: var(--textColor2);
|
||||||
|
}
|
||||||
|
|
||||||
.inputField span {
|
.inputField span {
|
||||||
padding: 11px 9px;
|
padding: 11px 9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inputField span:first-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.inputField input::placeholder {
|
.inputField input::placeholder {
|
||||||
transition: color 0.25s ease-in 0s;
|
transition: color 0.25s ease-in 0s;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
|
|
|
@ -21,14 +21,21 @@ import { Button } from "./button";
|
||||||
import { useClient } from "./ConferenceCallManagerHooks";
|
import { useClient } from "./ConferenceCallManagerHooks";
|
||||||
import styles from "./LoginPage.module.css";
|
import styles from "./LoginPage.module.css";
|
||||||
import { ReactComponent as Logo } from "./icons/LogoLarge.svg";
|
import { ReactComponent as Logo } from "./icons/LogoLarge.svg";
|
||||||
|
import { LoadingView } from "./FullScreenView";
|
||||||
|
|
||||||
export function RegisterPage() {
|
export function RegisterPage() {
|
||||||
const { register } = useClient();
|
const {
|
||||||
const registerUsernameRef = useRef();
|
loading,
|
||||||
|
client,
|
||||||
|
register,
|
||||||
|
changePassword,
|
||||||
|
isAuthenticated,
|
||||||
|
isPasswordlessUser,
|
||||||
|
} = useClient();
|
||||||
const confirmPasswordRef = useRef();
|
const confirmPasswordRef = useRef();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [loading, setLoading] = useState(false);
|
const [registering, setRegistering] = useState(false);
|
||||||
const [error, setError] = useState();
|
const [error, setError] = useState();
|
||||||
const [password, setPassword] = useState("");
|
const [password, setPassword] = useState("");
|
||||||
const [passwordConfirmation, setPasswordConfirmation] = useState("");
|
const [passwordConfirmation, setPasswordConfirmation] = useState("");
|
||||||
|
@ -36,27 +43,55 @@ export function RegisterPage() {
|
||||||
const onSubmitRegisterForm = useCallback(
|
const onSubmitRegisterForm = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
const data = new FormData(e.target);
|
||||||
register(
|
const userName = data.get("userName");
|
||||||
registerUsernameRef.current.value,
|
const password = data.get("password");
|
||||||
registerPasswordRef.current.value
|
const passwordConfirmation = data.get("passwordConfirmation");
|
||||||
)
|
|
||||||
.then(() => {
|
if (password !== passwordConfirmation) {
|
||||||
if (location.state && location.state.from) {
|
return;
|
||||||
history.push(location.state.from);
|
}
|
||||||
} else {
|
|
||||||
history.push("/");
|
setRegistering(true);
|
||||||
}
|
|
||||||
})
|
console.log(isPasswordlessUser);
|
||||||
.catch((error) => {
|
|
||||||
setError(error);
|
if (isPasswordlessUser) {
|
||||||
setLoading(false);
|
changePassword(password)
|
||||||
});
|
.then(() => {
|
||||||
|
if (location.state && location.state.from) {
|
||||||
|
history.push(location.state.from);
|
||||||
|
} else {
|
||||||
|
history.push("/");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setError(error);
|
||||||
|
setRegistering(false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
register(userName, password)
|
||||||
|
.then(() => {
|
||||||
|
if (location.state && location.state.from) {
|
||||||
|
history.push(location.state.from);
|
||||||
|
} else {
|
||||||
|
history.push("/");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
setError(error);
|
||||||
|
setRegistering(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[register, location, history]
|
[register, changePassword, location, history, isPasswordlessUser]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!confirmPasswordRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (password && passwordConfirmation && password !== passwordConfirmation) {
|
if (password && passwordConfirmation && password !== passwordConfirmation) {
|
||||||
confirmPasswordRef.current.setCustomValidity("Passwords must match");
|
confirmPasswordRef.current.setCustomValidity("Passwords must match");
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,6 +99,16 @@ export function RegisterPage() {
|
||||||
}
|
}
|
||||||
}, [password, passwordConfirmation]);
|
}, [password, passwordConfirmation]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!loading && isAuthenticated && !isPasswordlessUser) {
|
||||||
|
history.push("/");
|
||||||
|
}
|
||||||
|
}, [history, isAuthenticated, isPasswordlessUser]);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <LoadingView />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
|
@ -75,18 +120,26 @@ export function RegisterPage() {
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
<InputField
|
<InputField
|
||||||
type="text"
|
type="text"
|
||||||
ref={registerUsernameRef}
|
name="userName"
|
||||||
placeholder="Username"
|
placeholder="Username"
|
||||||
label="Username"
|
label="Username"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
prefix="@"
|
prefix="@"
|
||||||
suffix={`:${window.location.host}`}
|
suffix={`:${window.location.host}`}
|
||||||
|
value={
|
||||||
|
isAuthenticated && isPasswordlessUser
|
||||||
|
? client.getUserIdLocalpart()
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
onChange={(e) => setUserName(e.target.value)}
|
||||||
|
disabled={isAuthenticated && isPasswordlessUser}
|
||||||
/>
|
/>
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
<InputField
|
<InputField
|
||||||
required
|
required
|
||||||
|
name="password"
|
||||||
type="password"
|
type="password"
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
value={password}
|
value={password}
|
||||||
|
@ -98,6 +151,7 @@ export function RegisterPage() {
|
||||||
<InputField
|
<InputField
|
||||||
required
|
required
|
||||||
type="password"
|
type="password"
|
||||||
|
name="passwordConfirmation"
|
||||||
onChange={(e) => setPasswordConfirmation(e.target.value)}
|
onChange={(e) => setPasswordConfirmation(e.target.value)}
|
||||||
value={passwordConfirmation}
|
value={passwordConfirmation}
|
||||||
placeholder="Confirm Password"
|
placeholder="Confirm Password"
|
||||||
|
@ -111,8 +165,8 @@ export function RegisterPage() {
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
)}
|
)}
|
||||||
<FieldRow>
|
<FieldRow>
|
||||||
<Button type="submit" disabled={loading}>
|
<Button type="submit" disabled={registering}>
|
||||||
{loading ? "Registering..." : "Register"}
|
{registering ? "Registering..." : "Register"}
|
||||||
</Button>
|
</Button>
|
||||||
</FieldRow>
|
</FieldRow>
|
||||||
</form>
|
</form>
|
||||||
|
|
33
src/Room.jsx
33
src/Room.jsx
|
@ -60,8 +60,15 @@ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
||||||
export function Room() {
|
export function Room() {
|
||||||
const [registeringGuest, setRegisteringGuest] = useState(false);
|
const [registeringGuest, setRegisteringGuest] = useState(false);
|
||||||
const [registrationError, setRegistrationError] = useState();
|
const [registrationError, setRegistrationError] = useState();
|
||||||
const { loading, isAuthenticated, error, client, registerGuest, isGuest } =
|
const {
|
||||||
useClient();
|
loading,
|
||||||
|
isAuthenticated,
|
||||||
|
error,
|
||||||
|
client,
|
||||||
|
registerGuest,
|
||||||
|
isGuest,
|
||||||
|
isPasswordlessUser,
|
||||||
|
} = useClient();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && !isAuthenticated) {
|
if (!loading && !isAuthenticated) {
|
||||||
|
@ -86,10 +93,16 @@ export function Room() {
|
||||||
return <ErrorView error={registrationError || error} />;
|
return <ErrorView error={registrationError || error} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <GroupCall client={client} isGuest={isGuest} />;
|
return (
|
||||||
|
<GroupCall
|
||||||
|
client={client}
|
||||||
|
isGuest={isGuest}
|
||||||
|
isPasswordlessUser={isPasswordlessUser}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GroupCall({ client, isGuest }) {
|
export function GroupCall({ client, isGuest, isPasswordlessUser }) {
|
||||||
const { roomId: maybeRoomId } = useParams();
|
const { roomId: maybeRoomId } = useParams();
|
||||||
const { hash, search } = useLocation();
|
const { hash, search } = useLocation();
|
||||||
const [simpleGrid, viaServers] = useMemo(() => {
|
const [simpleGrid, viaServers] = useMemo(() => {
|
||||||
|
@ -118,6 +131,7 @@ export function GroupCall({ client, isGuest }) {
|
||||||
return (
|
return (
|
||||||
<GroupCallView
|
<GroupCallView
|
||||||
isGuest={isGuest}
|
isGuest={isGuest}
|
||||||
|
isPasswordlessUser={isPasswordlessUser}
|
||||||
client={client}
|
client={client}
|
||||||
roomId={roomId}
|
roomId={roomId}
|
||||||
groupCall={groupCall}
|
groupCall={groupCall}
|
||||||
|
@ -129,6 +143,7 @@ export function GroupCall({ client, isGuest }) {
|
||||||
export function GroupCallView({
|
export function GroupCallView({
|
||||||
client,
|
client,
|
||||||
isGuest,
|
isGuest,
|
||||||
|
isPasswordlessUser,
|
||||||
roomId,
|
roomId,
|
||||||
groupCall,
|
groupCall,
|
||||||
simpleGrid,
|
simpleGrid,
|
||||||
|
@ -184,7 +199,7 @@ export function GroupCallView({
|
||||||
const onLeave = useCallback(() => {
|
const onLeave = useCallback(() => {
|
||||||
leave();
|
leave();
|
||||||
|
|
||||||
if (!isGuest) {
|
if (!isGuest && !isPasswordlessUser) {
|
||||||
history.push("/");
|
history.push("/");
|
||||||
} else {
|
} else {
|
||||||
setLeft(true);
|
setLeft(true);
|
||||||
|
@ -494,18 +509,12 @@ export function CallEndedScreen() {
|
||||||
<ul>
|
<ul>
|
||||||
<li>Easily access all your previous call links</li>
|
<li>Easily access all your previous call links</li>
|
||||||
<li>Set a username and avatar</li>
|
<li>Set a username and avatar</li>
|
||||||
<li>
|
|
||||||
Get your own Matrix ID so you can log in to{" "}
|
|
||||||
<a href="https://element.io" target="_blank">
|
|
||||||
Element
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<LinkButton
|
<LinkButton
|
||||||
className={styles.callEndedButton}
|
className={styles.callEndedButton}
|
||||||
size="lg"
|
size="lg"
|
||||||
variant="default"
|
variant="default"
|
||||||
to="/login"
|
to="/register"
|
||||||
>
|
>
|
||||||
Create account
|
Create account
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
|
|
Loading…
Add table
Reference in a new issue