diff --git a/src/components/App.tsx b/src/components/App.tsx
index e926e39..4345575 100644
--- a/src/components/App.tsx
+++ b/src/components/App.tsx
@@ -1,11 +1,11 @@
-import { lazy, Suspense, useContext, useState, useEffect } from 'react';
+import { CSSProperties, lazy, Suspense, useEffect, useState } from 'react';
 import { BrowserRouter, Route, Switch } from 'react-router-dom';
-import AuthenticationContext from './Authentication/AuthenticationContext';
+import { getReceivedInvitationsAndRequests } from './api';
+import { useMe } from './hooks';
+import Notifications from './Notifications';
+import { IInvitation } from './types';
 import WheelShare from './WheelShare';
 import WheelShareLoggedOut from './WheelShareLoggedOut';
-import Notifications from './Notifications';
-import { getReceivedInvitationsAndRequests } from './api';
-import { IInvitation } from './types';
 
 const Authenticator = lazy(() => import('./Authentication/Authenticator'));
 const Group = lazy(() => import('./Group'));
@@ -37,33 +37,54 @@ const dummyNotificationData: IInvitation[] = [
 	},
 ];
 
-export default function App() {
-	const { user } = useContext(AuthenticationContext);
-	// eslint-disable-next-line
+const style: CSSProperties = {
+	display: 'flex',
+	flexDirection: 'column',
+	alignItems: 'center',
+	width: '30rem',
+	maxWidth: '30rem',
+	marginLeft: 'auto',
+	marginRight: 'auto',
+};
+
+function useNotifications() {
 	const [notifications, setNotifications] = useState(dummyNotificationData);
+
 	useEffect(() => {
 		getReceivedInvitationsAndRequests().then(setNotifications);
 	}, []);
+
+	return notifications;
+}
+
+export default function App() {
+	const user = useMe();
+	const notifications = useNotifications();
 	return (
 		<div style={{ padding: '1rem' }}>
-			{notifications.length > 0 ? (
-				<Notifications notifications={notifications} />
-			) : (
-				<span>No notifications</span>
-			)}
-			<BrowserRouter>
-				<Switch>
-					<Route
-						path="/"
-						exact
-						component={user ? WheelShare : WheelShareLoggedOut}
-					/>
-					<Suspense fallback={null}>
-						<Route path="/groups/:id" component={Group} />
-						<Route component={Authenticator} path="/auth/:provider/callback" />
-					</Suspense>
-				</Switch>
-			</BrowserRouter>
+			<div style={style}>
+				{notifications.length > 0 ? (
+					<Notifications notifications={notifications} />
+				) : (
+					<span>No notifications</span>
+				)}
+				<BrowserRouter>
+					<Switch>
+						<Route
+							path="/"
+							exact
+							component={user ? WheelShare : WheelShareLoggedOut}
+						/>
+						<Suspense fallback={null}>
+							<Route path="/groups/:id" component={Group} />
+							<Route
+								component={Authenticator}
+								path="/auth/:provider/callback"
+							/>
+						</Suspense>
+					</Switch>
+				</BrowserRouter>
+			</div>
 		</div>
 	);
 }
diff --git a/src/components/Authentication/Authenticator.tsx b/src/components/Authentication/Authenticator.tsx
index f5a1cfa..ba95556 100644
--- a/src/components/Authentication/Authenticator.tsx
+++ b/src/components/Authentication/Authenticator.tsx
@@ -19,22 +19,13 @@ function inferRedirectUrl() {
 	return redirectUrl;
 }
 
-export default function Authenticator() {
-	const { provider } = useParams<{ provider: string }>();
-	const [code, error] = useCodeAndError();
-	const { refresh } = useContext(AuthenticationContext);
-
-	const [pending, setPending] = useState(true);
+function useToken(
+	code: string | null,
+	provider: string
+): [string | null, boolean] {
+	const [pending, setPending] = useState(false);
 	const [token, setToken] = useState<string | null>(null);
 
-	useEffect(() => {
-		if (token) {
-			localStorage.setItem('session_token', token);
-		} else {
-			localStorage.removeItem('session_token');
-		}
-	}, [token]);
-
 	useEffect(() => {
 		if (code) {
 			setPending(true);
@@ -46,10 +37,34 @@ export default function Authenticator() {
 		}
 	}, [code, provider]);
 
+	return [token, pending];
+}
+
+function useLocalStorageSync(key: string, value: string | null) {
+	useEffect(() => {
+		if (value) {
+			localStorage.setItem(key, value);
+		} else {
+			localStorage.removeItem(key);
+		}
+	}, [key, value]);
+}
+
+function useRefresh(refresh: Function, watch: string | null) {
 	useEffect(() => {
-		// Refresh when the token changes
 		refresh();
-	}, [token, refresh]);
+	}, [watch, refresh]);
+}
+
+export default function Authenticator() {
+	const { provider } = useParams<{ provider: string }>();
+	const [code, error] = useCodeAndError();
+	const { refresh } = useContext(AuthenticationContext);
+
+	const [token, pending] = useToken(code, provider);
+
+	useLocalStorageSync('session_token', token);
+	useRefresh(refresh, token);
 
 	let children: JSX.Element;
 
@@ -86,15 +101,5 @@ export default function Authenticator() {
 		);
 	}
 
-	return (
-		<div
-			style={{
-				display: 'flex',
-				flexDirection: 'column',
-				alignItems: 'center',
-			}}
-		>
-			{children}
-		</div>
-	);
+	return children;
 }
diff --git a/src/components/Notifications.tsx b/src/components/Notifications.tsx
index 09d3ac1..5338312 100644
--- a/src/components/Notifications.tsx
+++ b/src/components/Notifications.tsx
@@ -10,9 +10,12 @@ export default function Notifications({
 		<div className="notifications">
 			<div style={{ display: 'flex', flexDirection: 'column' }}>
 				<h3 style={{ marginBlockEnd: '0' }}>Notifications</h3>
-				{notifications.map((notification) => {
-					return <Notification notification={notification} />;
-				})}
+				{notifications.map((notification) => (
+					<Notification
+						notification={notification}
+						key={`${notification.user.id}:${notification.carpool.id}`}
+					/>
+				))}
 			</div>
 		</div>
 	);
diff --git a/src/components/WheelShare.tsx b/src/components/WheelShare.tsx
index 80bd3f8..fe8a1b2 100644
--- a/src/components/WheelShare.tsx
+++ b/src/components/WheelShare.tsx
@@ -1,4 +1,3 @@
-import { CSSProperties } from 'react';
 import logout from './Authentication/logout';
 import Carpool from './Carpool';
 import Events from './Events';
@@ -7,21 +6,11 @@ import { useMe } from './hooks';
 import UIPressable from './UIPressable';
 import UIPrimaryTitle from './UIPrimaryTitle';
 
-const style: CSSProperties = {
-	display: 'flex',
-	flexDirection: 'column',
-	alignItems: 'center',
-	width: '30rem',
-	maxWidth: '30rem',
-	marginLeft: 'auto',
-	marginRight: 'auto',
-};
-
 export default function WheelShare() {
 	const user = useMe()!;
 
 	return (
-		<div style={style}>
+		<>
 			<UIPrimaryTitle>WheelShare</UIPrimaryTitle>
 
 			<Carpool
@@ -40,6 +29,6 @@ export default function WheelShare() {
 
 			<Groups />
 			<Events />
-		</div>
+		</>
 	);
 }
diff --git a/src/components/WheelShareLoggedOut.tsx b/src/components/WheelShareLoggedOut.tsx
index b910e15..b1655d4 100644
--- a/src/components/WheelShareLoggedOut.tsx
+++ b/src/components/WheelShareLoggedOut.tsx
@@ -1,28 +1,30 @@
-import { CSSProperties } from 'react';
 import UILink from './UILink';
 import UIPrimaryTitle from './UIPrimaryTitle';
 
-const dev = true;
-const ION_AUTHORIZATION_ENDPOINT = dev
-	? 'https://ion.tjhsst.edu/oauth/authorize?response_type=code&client_id=rNa6n9YSg8ftINdyVPpUsaMuxNbHLo9dh1OsOktR&scope=read&redirect_uri=http%3A%2F%2Flocalhost%3A3000%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';
+function createAuthorizationEndpoint(redirectUrl: string) {
+	const url = new URL('https://ion.tjhsst.edu/oauth/authorize');
 
-const style: CSSProperties = {
-	display: 'flex',
-	flexDirection: 'column',
-	alignItems: 'center',
-	width: '30rem',
-	maxWidth: '30rem',
-	marginLeft: 'auto',
-	marginRight: 'auto',
-};
+	url.searchParams.set('response_type', 'code');
+	url.searchParams.set('client_id', 'rNa6n9YSg8ftINdyVPpUsaMuxNbHLo9dh1OsOktR');
+	url.searchParams.set('redirect_uri', redirectUrl);
+	url.searchParams.set('scope', 'read');
+
+	return url.toString();
+}
+
+const dev = true;
+const endpoint = createAuthorizationEndpoint(
+	dev
+		? 'http://localhost:3000/auth/ion/callback'
+		: 'https://wheelshare.space/auth/ion/callback'
+);
 
 export default function WheelShareLoggedOut() {
 	return (
-		<div style={style}>
+		<>
 			<UIPrimaryTitle>WheelShare</UIPrimaryTitle>
 			<p>To get started, log in with your Ion account.</p>
-			<UILink href={ION_AUTHORIZATION_ENDPOINT}>Log in</UILink>
-		</div>
+			<UILink href={endpoint}>Log in</UILink>
+		</>
 	);
 }