clean up authentication code

This commit is contained in:
Michael Fatemi 2021-07-03 00:55:13 -04:00
parent 557d554db3
commit dde3edee04
4 changed files with 53 additions and 63 deletions

View File

@ -14,10 +14,10 @@ const ION_AUTHORIZATION_ENDPOINT = dev
: 'https://ion.tjhsst.edu/oauth/authorize?response_type=code&client_id=rNa6n9YSg8ftINdyVPpUsaMuxNbHLo9dh1OsOktR&scope=read&redirect_uri=https%3A%2F%2Fwheelshare.space%2Fauth%2Fion%2Fcallback'; : 'https://ion.tjhsst.edu/oauth/authorize?response_type=code&client_id=rNa6n9YSg8ftINdyVPpUsaMuxNbHLo9dh1OsOktR&scope=read&redirect_uri=https%3A%2F%2Fwheelshare.space%2Fauth%2Fion%2Fcallback';
export default function App() { export default function App() {
const { isLoggedIn, user } = useContext(AuthenticationContext); const { user } = useContext(AuthenticationContext);
return ( return (
<div style={{ padding: '1rem' }}> <div style={{ padding: '1rem' }}>
{isLoggedIn ? ( {user ? (
<div <div
style={{ style={{
display: 'flex', display: 'flex',
@ -28,7 +28,7 @@ export default function App() {
alignItems: 'center', alignItems: 'center',
}} }}
> >
{user!.name}{' '} {user.name}{' '}
<UIButton style={{ marginTop: 0 }} onClick={logout}> <UIButton style={{ marginTop: 0 }} onClick={logout}>
Log out Log out
</UIButton> </UIButton>

View File

@ -6,20 +6,19 @@ export type User = {
email?: string; email?: string;
}; };
export type AuthState = { export type AuthenticationContextProps = {
isLoggedIn: boolean | null;
user: User | null; user: User | null;
/** /**
* Function that can be used to trigger an auth state refresh. * Function that can be used to trigger an auth state refresh.
*/ */
refreshAuthState: (() => void) | null; refresh: () => void;
}; };
const AuthenticationContext = createContext<AuthState>({ const AuthenticationContext = createContext<AuthenticationContextProps>({
isLoggedIn: false,
user: null, user: null,
refreshAuthState: null, refresh: () =>
console.warn('calling refresh on default AuthenticationContext'),
}); });
export default AuthenticationContext; export default AuthenticationContext;

View File

@ -1,45 +1,30 @@
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { getMe } from '../api'; import { getMe } from '../api';
import AuthenticationContext, { AuthState } from './AuthenticationContext'; import AuthenticationContext, { User } from './AuthenticationContext';
export default function AuthenticationWrapper({ export default function AuthenticationWrapper({
children, children,
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const sessionToken = localStorage.getItem('session_token');
// Prevent race conditions // Prevent race conditions
const [authState, setAuthState] = useState<AuthState>({ const [user, setUser] = useState<User | null>(null);
isLoggedIn: null,
user: null,
refreshAuthState: null,
});
const refreshAuthState = useCallback(() => { const refresh = useCallback(() => {
const loggedOut = () => const none = () => setUser(null);
setAuthState({ isLoggedIn: false, user: null, refreshAuthState }); const sessionToken = localStorage.getItem('session_token');
if (sessionToken) { if (sessionToken) {
getMe() getMe().then(setUser).catch(none);
.then((user) => {
if (user) {
setAuthState({ isLoggedIn: true, user, refreshAuthState });
} else {
loggedOut();
}
})
.catch(loggedOut);
} else { } else {
loggedOut(); none();
} }
}, [sessionToken]); }, []);
useEffect(() => { useEffect(refresh, [refresh]);
refreshAuthState();
}, [refreshAuthState]);
return ( return (
<AuthenticationContext.Provider value={authState}> <AuthenticationContext.Provider value={{ user, refresh }}>
{children} {children}
</AuthenticationContext.Provider> </AuthenticationContext.Provider>
); );

View File

@ -3,45 +3,51 @@ import { Redirect, useLocation, useParams } from 'react-router-dom';
import AuthenticationContext from './AuthenticationContext'; import AuthenticationContext from './AuthenticationContext';
import { createSession } from './createSession'; import { createSession } from './createSession';
function useCode() {
const location = useLocation();
const query = new URLSearchParams(location.search);
const code = query.get('code');
return code;
}
export default function Authenticator() { export default function Authenticator() {
const { provider } = useParams<{ provider: string }>(); const { provider } = useParams<{ provider: string }>();
const query = new URLSearchParams(useLocation().search); const code = useCode();
const code = query.get('code'); const { refresh } = useContext(AuthenticationContext);
const { refreshAuthState } = useContext(AuthenticationContext);
const [status, setStatus] = const [pending, setPending] = useState(true);
useState<'pending' | 'errored' | 'authenticated'>('pending');
const [token, setToken] = useState<string | null>(null); const [token, setToken] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
if (token) {
localStorage.setItem('session_token', token);
} else {
localStorage.removeItem('session_token');
}
}, [token]);
useEffect(() => {
setPending(true);
createSession(code!) createSession(code!)
.then((data) => { .then(({ token }) => {
if (data.status === 'success') { setToken(token ?? null);
console.log('Success! Token:', data.token);
setToken(data.token);
localStorage.setItem('session_token', data.token);
setStatus('authenticated');
} else {
console.log('Authentication failure.');
setToken(null);
localStorage.removeItem('session_token');
setStatus('errored');
}
}) })
.catch(() => { .finally(() => setPending(false));
setStatus('errored');
});
}, [code, provider]); }, [code, provider]);
useEffect(() => { useEffect(() => {
refreshAuthState && refreshAuthState(); refresh();
}, [token, refreshAuthState]); }, [token, refresh]);
switch (status) { if (pending) {
case 'authenticated': return <h1>Signing In</h1>;
return <Redirect to="/" />;
case 'errored':
return <h1>Sign In Error</h1>;
case 'pending':
return <h1>Signing In</h1>;
} }
if (token) {
return <Redirect to="/" />;
}
// If we aren't pending anymore, but don't have a token, we must have errored
return <h1>Sign In Error</h1>;
} }