update login flow to prevent session leakage

This commit is contained in:
41666 2022-01-31 23:32:41 -05:00
parent be826b613e
commit 1cb04c8b5a
4 changed files with 41 additions and 30 deletions

View file

@ -9,10 +9,7 @@ import { seeOther } from '@roleypoly/api/src/utils/response';
import { AuthTokenResponse, StateSession } from '@roleypoly/types';
const authFailure = (uiPublicURI: string, extra?: string) =>
seeOther(
uiPublicURI +
`/machinery/error?error_code=authFailure${extra ? `&extra=${extra}` : ''}`
);
seeOther(uiPublicURI + `/error/authFailure${extra ? `?extra=${extra}` : ''}`);
export const authCallback: RoleypolyHandler = async (
request: Request,
@ -72,5 +69,5 @@ export const authCallback: RoleypolyHandler = async (
return authFailure(config.uiPublicURI, 'session setup failure');
}
return seeOther(bounceBaseUrl + 'machinery/new-session/' + session.sessionID);
return seeOther(bounceBaseUrl + 'machinery/new-session/#/' + session.sessionID);
};

View file

@ -44,10 +44,7 @@ export const AppRouter = () => {
<RouteWrapper component={ErrorPage} path="/error" />
<RouteWrapper component={ErrorPage} path="/error/:identity" />
<RouteWrapper
component={MachineryNewSession}
path="/machinery/new-session/:sessionID"
/>
<RouteWrapper component={MachineryNewSession} path="/machinery/new-session" />
<RouteWrapper component={MachineryLogout} path="/machinery/logout" />
<RouteWrapper component={MachineryBotJoin} path="/machinery/bot-join" />
<RouteWrapper component={MachineryBotJoin} path="/machinery/bot-join/:serverID" />

View file

@ -1,4 +1,6 @@
import { redirectTo } from '@reach/router';
import { useLocation, useNavigate } from '@reach/router';
import { palette } from '@roleypoly/design-system/atoms/colors';
import { Link } from '@roleypoly/design-system/atoms/typography';
import { AuthLogin } from '@roleypoly/design-system/templates/auth-login';
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
import { GuildSlug } from '@roleypoly/types';
@ -12,21 +14,23 @@ const Login = (props: { path: string }) => {
const { apiUrl } = useApiContext();
const { isAuthenticated } = useSessionContext();
const { getGuildSlug } = useGuildContext();
const navigate = useNavigate();
const location = useLocation();
// If ?r is in query, then let's render the slug page
// If not, redirect.
const [guildSlug, setGuildSlug] = React.useState<GuildSlug | null>(null);
const [oauthLink, setOauthLink] = React.useState(`${apiUrl}/auth/bounce`);
React.useEffect(() => {
const url = new URL(window.location.href);
const url = new URL(location.href);
const callbackHost = new URL('/', url);
const redirectServerID = url.searchParams.get('r');
const redirectUrl = `${apiUrl}/auth/bounce?cbh=${callbackHost.href}`;
if (!redirectServerID) {
if (isAuthenticated) {
redirectTo('/servers');
navigate('/servers');
}
window.location.href = redirectUrl;
navigate(redirectUrl);
return;
}
@ -43,12 +47,21 @@ const Login = (props: { path: string }) => {
fetchGuildSlug(redirectServerID);
if (isAuthenticated) {
redirectTo(`/s/${redirectServerID}`);
navigate(`/s/${redirectServerID}`);
}
}, [apiUrl, getGuildSlug, isAuthenticated]);
}, [apiUrl, getGuildSlug, isAuthenticated, location, navigate]);
if (guildSlug === null) {
return <GenericLoadingTemplate>Sending you to Discord...</GenericLoadingTemplate>;
return (
<GenericLoadingTemplate>
<div style={{ textAlign: 'center' }}>
<div>Sending you to Discord...</div>
<Link style={{ color: palette.taupe400 }} href={oauthLink}>
If you aren't redirected soon, click here.
</Link>
</div>
</GenericLoadingTemplate>
);
}
return (

View file

@ -1,3 +1,4 @@
import { useLocation, useNavigate } from '@reach/router';
import { palette } from '@roleypoly/design-system/atoms/colors';
import { Link } from '@roleypoly/design-system/atoms/typography';
import { GenericLoadingTemplate } from '@roleypoly/design-system/templates/generic-loading';
@ -6,8 +7,10 @@ import { useSessionContext } from '../../contexts/session/SessionContext';
import { Title } from '../../utils/metaTitle';
const NewSession = (props: { sessionID: string }) => {
const { setupSession, isAuthenticated } = useSessionContext();
const { setupSession, sessionID } = useSessionContext();
const [postauthUrl, setPostauthUrl] = React.useState('/servers');
const navigate = useNavigate();
const location = useLocation();
React.useEffect(() => {
const storedPostauthUrl = localStorage.getItem('rp_postauth_redirect');
@ -18,20 +21,21 @@ const NewSession = (props: { sessionID: string }) => {
}, [setPostauthUrl]);
React.useEffect(() => {
if (isAuthenticated) {
setTimeout(() => {
window.history.replaceState(null, '', '/');
window.location.href = postauthUrl;
}, 0);
}
}, [postauthUrl, isAuthenticated]);
if (!sessionID) {
const sessionToken = location.hash.substring(2);
if (!sessionToken) {
console.error({ sessionToken });
navigate('/error/400?extra=missing-hash');
return;
}
React.useCallback(
(sessionID) => {
setupSession(sessionID);
},
[setupSession]
)(props.sessionID);
setupSession(sessionToken);
}
if (sessionID) {
navigate(postauthUrl);
}
}, [sessionID, location, postauthUrl, setupSession, navigate]);
return (
<GenericLoadingTemplate>