Force authentication to see public content
This commit is contained in:
parent
1d9cdb4645
commit
7571af38cb
8 changed files with 101 additions and 17 deletions
|
@ -22,6 +22,7 @@ import WaitlistPage from 'soapbox/features/verification/waitlist_page';
|
||||||
import { createGlobals } from 'soapbox/globals';
|
import { createGlobals } from 'soapbox/globals';
|
||||||
import { useAppSelector, useAppDispatch, useOwnAccount, useFeatures, useSoapboxConfig, useSettings, useSystemTheme } from 'soapbox/hooks';
|
import { useAppSelector, useAppDispatch, useOwnAccount, useFeatures, useSoapboxConfig, useSettings, useSystemTheme } from 'soapbox/hooks';
|
||||||
import MESSAGES from 'soapbox/locales/messages';
|
import MESSAGES from 'soapbox/locales/messages';
|
||||||
|
import { useCachedLocationHandler } from 'soapbox/utils/redirect';
|
||||||
import { generateThemeCss } from 'soapbox/utils/theme';
|
import { generateThemeCss } from 'soapbox/utils/theme';
|
||||||
|
|
||||||
import { checkOnboardingStatus } from '../actions/onboarding';
|
import { checkOnboardingStatus } from '../actions/onboarding';
|
||||||
|
@ -64,6 +65,7 @@ const loadInitial = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const SoapboxMount = () => {
|
const SoapboxMount = () => {
|
||||||
|
useCachedLocationHandler();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const me = useAppSelector(state => state.me);
|
const me = useAppSelector(state => state.me);
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Link, Redirect, Route, Switch } from 'react-router-dom';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
import { Link, Redirect, Route, Switch, useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
import LandingGradient from 'soapbox/components/landing-gradient';
|
import LandingGradient from 'soapbox/components/landing-gradient';
|
||||||
import SiteLogo from 'soapbox/components/site-logo';
|
import SiteLogo from 'soapbox/components/site-logo';
|
||||||
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
|
||||||
import { NotificationsContainer } from 'soapbox/features/ui/util/async-components';
|
import { NotificationsContainer } from 'soapbox/features/ui/util/async-components';
|
||||||
import { useAppSelector } from 'soapbox/hooks';
|
import { useAppSelector, useFeatures, useSoapboxConfig } from 'soapbox/hooks';
|
||||||
|
|
||||||
import { Card, CardBody } from '../../components/ui';
|
import { Button, Card, CardBody } from '../../components/ui';
|
||||||
import LoginPage from '../auth_login/components/login_page';
|
import LoginPage from '../auth_login/components/login_page';
|
||||||
import PasswordReset from '../auth_login/components/password_reset';
|
import PasswordReset from '../auth_login/components/password_reset';
|
||||||
import PasswordResetConfirm from '../auth_login/components/password_reset_confirm';
|
import PasswordResetConfirm from '../auth_login/components/password_reset_confirm';
|
||||||
|
@ -17,22 +18,52 @@ import RegisterInvite from '../register_invite';
|
||||||
import Verification from '../verification';
|
import Verification from '../verification';
|
||||||
import EmailPassthru from '../verification/email_passthru';
|
import EmailPassthru from '../verification/email_passthru';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
register: { id: 'auth_layout.register', defaultMessage: 'Create an account' },
|
||||||
|
});
|
||||||
|
|
||||||
const AuthLayout = () => {
|
const AuthLayout = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
const siteTitle = useAppSelector(state => state.instance.title);
|
const siteTitle = useAppSelector(state => state.instance.title);
|
||||||
|
const soapboxConfig = useSoapboxConfig();
|
||||||
|
const pepeEnabled = soapboxConfig.getIn(['extensions', 'pepe', 'enabled']) === true;
|
||||||
|
|
||||||
|
const features = useFeatures();
|
||||||
|
const instance = useAppSelector((state) => state.instance);
|
||||||
|
const isOpen = features.accountCreation && instance.registrations;
|
||||||
|
const pepeOpen = useAppSelector(state => state.verification.getIn(['instance', 'registrations'], false) === true);
|
||||||
|
const isLoginPage = history.location.pathname === '/login';
|
||||||
|
const shouldShowRegisterLink = (isLoginPage && (isOpen || (pepeEnabled && pepeOpen)));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='h-full'>
|
<div className='h-full'>
|
||||||
<LandingGradient />
|
<LandingGradient />
|
||||||
|
|
||||||
<main className='relative min-h-full sm:flex sm:items-center sm:justify-center py-12'>
|
<main className='relative min-h-full sm:flex sm:justify-center'>
|
||||||
<div className='w-full sm:max-w-lg md:max-w-2xl space-y-8'>
|
<div className='w-full sm:max-w-lg md:max-w-2xl lg:max-w-6xl'>
|
||||||
<header className='flex justify-center relative'>
|
<header className='flex justify-between relative py-12 px-2'>
|
||||||
<Link to='/' className='cursor-pointer'>
|
<div className='relative z-0 flex-1 px-2 lg:flex lg:items-center lg:justify-center lg:absolute lg:inset-0'>
|
||||||
<SiteLogo alt={siteTitle} className='h-7' />
|
<Link to='/' className='cursor-pointer'>
|
||||||
</Link>
|
<SiteLogo alt={siteTitle} className='h-7' />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{shouldShowRegisterLink && (
|
||||||
|
<div className='relative z-10 ml-auto flex items-center'>
|
||||||
|
<Button
|
||||||
|
theme='link'
|
||||||
|
icon={require('@tabler/icons/icons/user.svg')}
|
||||||
|
to='/signup'
|
||||||
|
>
|
||||||
|
{intl.formatMessage(messages.register)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className='flex flex-col justify-center items-center'>
|
<div className='flex flex-col h-full justify-center items-center'>
|
||||||
<div className='pb-10 sm:mx-auto w-full sm:max-w-lg md:max-w-2xl'>
|
<div className='pb-10 sm:mx-auto w-full sm:max-w-lg md:max-w-2xl'>
|
||||||
<Card variant='rounded' size='xl'>
|
<Card variant='rounded' size='xl'>
|
||||||
<CardBody>
|
<CardBody>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Redirect } from 'react-router-dom';
|
||||||
import { logIn, verifyCredentials, switchAccount } from 'soapbox/actions/auth';
|
import { logIn, verifyCredentials, switchAccount } from 'soapbox/actions/auth';
|
||||||
import { fetchInstance } from 'soapbox/actions/instance';
|
import { fetchInstance } from 'soapbox/actions/instance';
|
||||||
import { closeModal } from 'soapbox/actions/modals';
|
import { closeModal } from 'soapbox/actions/modals';
|
||||||
|
import { getRedirectUrl } from 'soapbox/utils/redirect';
|
||||||
import { isStandalone } from 'soapbox/utils/state';
|
import { isStandalone } from 'soapbox/utils/state';
|
||||||
|
|
||||||
import LoginForm from './login_form';
|
import LoginForm from './login_form';
|
||||||
|
@ -78,7 +79,10 @@ class LoginPage extends ImmutablePureComponent {
|
||||||
|
|
||||||
if (standalone) return <Redirect to='/login/external' />;
|
if (standalone) return <Redirect to='/login/external' />;
|
||||||
|
|
||||||
if (shouldRedirect) return <Redirect to='/' />;
|
if (shouldRedirect) {
|
||||||
|
const redirectUri = getRedirectUrl();
|
||||||
|
return <Redirect to={redirectUri} />;
|
||||||
|
}
|
||||||
|
|
||||||
if (mfa_auth_needed) return <OtpAuthForm mfa_token={mfa_token} />;
|
if (mfa_auth_needed) return <OtpAuthForm mfa_token={mfa_token} />;
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import RemoteInstancePage from 'soapbox/pages/remote_instance_page';
|
||||||
import StatusPage from 'soapbox/pages/status_page';
|
import StatusPage from 'soapbox/pages/status_page';
|
||||||
import { getAccessToken } from 'soapbox/utils/auth';
|
import { getAccessToken } from 'soapbox/utils/auth';
|
||||||
import { getVapidKey } from 'soapbox/utils/auth';
|
import { getVapidKey } from 'soapbox/utils/auth';
|
||||||
|
import { cacheCurrentUrl } from 'soapbox/utils/redirect';
|
||||||
import { isStandalone } from 'soapbox/utils/state';
|
import { isStandalone } from 'soapbox/utils/state';
|
||||||
// import GroupSidebarPanel from '../groups/sidebar_panel';
|
// import GroupSidebarPanel from '../groups/sidebar_panel';
|
||||||
|
|
||||||
|
@ -278,7 +279,7 @@ const SwitchingColumnsArea: React.FC = ({ children }) => {
|
||||||
<WrappedRoute path='/@:username/following' publicRoute={!authenticatedProfile} component={Following} page={ProfilePage} content={children} />
|
<WrappedRoute path='/@:username/following' publicRoute={!authenticatedProfile} component={Following} page={ProfilePage} content={children} />
|
||||||
<WrappedRoute path='/@:username/media' publicRoute={!authenticatedProfile} component={AccountGallery} page={ProfilePage} content={children} />
|
<WrappedRoute path='/@:username/media' publicRoute={!authenticatedProfile} component={AccountGallery} page={ProfilePage} content={children} />
|
||||||
<WrappedRoute path='/@:username/tagged/:tag' exact component={AccountTimeline} page={ProfilePage} content={children} />
|
<WrappedRoute path='/@:username/tagged/:tag' exact component={AccountTimeline} page={ProfilePage} content={children} />
|
||||||
<WrappedRoute path='/@:username/favorites' component={FavouritedStatuses} page={ProfilePage} content={children} />
|
<WrappedRoute path='/@:username/favorites' component={FavouritedStatuses} page={ProfilePage} content={children} />
|
||||||
<WrappedRoute path='/@:username/pins' component={PinnedStatuses} page={ProfilePage} content={children} />
|
<WrappedRoute path='/@:username/pins' component={PinnedStatuses} page={ProfilePage} content={children} />
|
||||||
<WrappedRoute path='/@:username/posts/:statusId' publicRoute exact page={StatusPage} component={Status} content={children} />
|
<WrappedRoute path='/@:username/posts/:statusId' publicRoute exact page={StatusPage} component={Status} content={children} />
|
||||||
<Redirect from='/@:username/:statusId' to='/@:username/posts/:statusId' />
|
<Redirect from='/@:username/:statusId' to='/@:username/posts/:statusId' />
|
||||||
|
@ -330,6 +331,7 @@ const UI: React.FC = ({ children }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const { guestExperience } = useSoapboxConfig();
|
||||||
|
|
||||||
const [draggingOver, setDraggingOver] = useState<boolean>(false);
|
const [draggingOver, setDraggingOver] = useState<boolean>(false);
|
||||||
const [mobile, setMobile] = useState<boolean>(isMobile(window.innerWidth));
|
const [mobile, setMobile] = useState<boolean>(isMobile(window.innerWidth));
|
||||||
|
@ -479,7 +481,7 @@ const UI: React.FC = ({ children }) => {
|
||||||
document.addEventListener('drop', handleDrop, false);
|
document.addEventListener('drop', handleDrop, false);
|
||||||
document.addEventListener('dragleave', handleDragLeave, false);
|
document.addEventListener('dragleave', handleDragLeave, false);
|
||||||
|
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
navigator.serviceWorker.addEventListener('message', handleServiceWorkerPostMessage);
|
navigator.serviceWorker.addEventListener('message', handleServiceWorkerPostMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,6 +610,11 @@ const UI: React.FC = ({ children }) => {
|
||||||
// Wait for login to succeed or fail
|
// Wait for login to succeed or fail
|
||||||
if (me === null) return null;
|
if (me === null) return null;
|
||||||
|
|
||||||
|
if (!me && !guestExperience) {
|
||||||
|
cacheCurrentUrl(history.location);
|
||||||
|
return <Redirect to='/login' />;
|
||||||
|
}
|
||||||
|
|
||||||
type HotkeyHandlers = { [key: string]: (keyEvent?: KeyboardEvent) => void };
|
type HotkeyHandlers = { [key: string]: (keyEvent?: KeyboardEvent) => void };
|
||||||
|
|
||||||
const handlers: HotkeyHandlers = {
|
const handlers: HotkeyHandlers = {
|
||||||
|
|
|
@ -86,13 +86,14 @@ const WrappedRoute: React.FC<IWrappedRoute> = ({
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderLoading = () => renderWithLayout(<ColumnLoading />);
|
const renderLoading = () => renderWithLayout(<ColumnLoading />);
|
||||||
const renderForbidden = () => renderWithLayout(<ColumnForbidden />);
|
const renderForbidden = () => renderWithLayout(<ColumnForbidden />);
|
||||||
const renderError = (props: any) => renderWithLayout(<BundleColumnError {...props} />);
|
const renderError = (props: any) => renderWithLayout(<BundleColumnError {...props} />);
|
||||||
|
|
||||||
const loginRedirect = () => {
|
const loginRedirect = () => {
|
||||||
const actualUrl = encodeURIComponent(`${history.location.pathname}${history.location.search}`);
|
const actualUrl = encodeURIComponent(`${history.location.pathname}${history.location.search}`);
|
||||||
return <Redirect to={`/login?redirect_uri=${actualUrl}`} />;
|
localStorage.setItem('soapbox:redirect_uri', actualUrl);
|
||||||
|
return <Redirect to='/login' />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const authorized = [
|
const authorized = [
|
||||||
|
|
|
@ -11,6 +11,7 @@ import snackbar from 'soapbox/actions/snackbar';
|
||||||
import { createAccount } from 'soapbox/actions/verification';
|
import { createAccount } from 'soapbox/actions/verification';
|
||||||
import { removeStoredVerification } from 'soapbox/actions/verification';
|
import { removeStoredVerification } from 'soapbox/actions/verification';
|
||||||
import { useAppSelector } from 'soapbox/hooks';
|
import { useAppSelector } from 'soapbox/hooks';
|
||||||
|
import { getRedirectUrl } from 'soapbox/utils/redirect';
|
||||||
|
|
||||||
import { Button, Form, FormGroup, Input } from '../../components/ui';
|
import { Button, Form, FormGroup, Input } from '../../components/ui';
|
||||||
|
|
||||||
|
@ -81,7 +82,8 @@ const Registration = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (shouldRedirect) {
|
if (shouldRedirect) {
|
||||||
return <Redirect to='/' />;
|
const redirectUri = getRedirectUrl();
|
||||||
|
return <Redirect to={redirectUri} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -115,6 +115,7 @@ export const SoapboxConfigRecord = ImmutableRecord({
|
||||||
singleUserMode: false,
|
singleUserMode: false,
|
||||||
singleUserModeProfile: '',
|
singleUserModeProfile: '',
|
||||||
linkFooterMessage: '',
|
linkFooterMessage: '',
|
||||||
|
guestExperience: true,
|
||||||
links: ImmutableMap<string, string>(),
|
links: ImmutableMap<string, string>(),
|
||||||
}, 'SoapboxConfig');
|
}, 'SoapboxConfig');
|
||||||
|
|
||||||
|
|
36
app/soapbox/utils/redirect.ts
Normal file
36
app/soapbox/utils/redirect.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import { Location } from 'history';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
const LOCAL_STORAGE_REDIRECT_KEY = 'soapbox:redirect-uri';
|
||||||
|
|
||||||
|
const cacheCurrentUrl = (location: Location<unknown>) => {
|
||||||
|
const actualUrl = encodeURIComponent(`${location.pathname}${location.search}`);
|
||||||
|
localStorage.setItem(LOCAL_STORAGE_REDIRECT_KEY, actualUrl);
|
||||||
|
return actualUrl;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRedirectUrl = () => {
|
||||||
|
let redirectUri = localStorage.getItem(LOCAL_STORAGE_REDIRECT_KEY);
|
||||||
|
if (redirectUri) {
|
||||||
|
redirectUri = decodeURIComponent(redirectUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.removeItem(LOCAL_STORAGE_REDIRECT_KEY);
|
||||||
|
return redirectUri || '/';
|
||||||
|
};
|
||||||
|
|
||||||
|
const useCachedLocationHandler = () => {
|
||||||
|
const removeCachedRedirectUri = () => localStorage.removeItem(LOCAL_STORAGE_REDIRECT_KEY);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('beforeunload', removeCachedRedirectUri);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('beforeunload', removeCachedRedirectUri);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { cacheCurrentUrl, getRedirectUrl, useCachedLocationHandler };
|
Loading…
Reference in a new issue