diff --git a/web/source/settings-panel/index.js b/web/source/settings-panel/index.js
index 906163eb9..d59dc7237 100644
--- a/web/source/settings-panel/index.js
+++ b/web/source/settings-panel/index.js
@@ -79,7 +79,7 @@ function App() {
 		}).then(() => {
 			// Check currently stored auth token for validity if available
 			if (loginState == "callback" || loginState == "login") {
-				return dispatch(api.oauth.verify());
+				return dispatch(api.user.fetchAccount());
 			}
 		}).then(() => {
 			setTokenChecked(true);
diff --git a/web/source/settings-panel/lib/api/index.js b/web/source/settings-panel/lib/api/index.js
index f6e826e49..840cbca10 100644
--- a/web/source/settings-panel/lib/api/index.js
+++ b/web/source/settings-panel/lib/api/index.js
@@ -22,51 +22,58 @@ const Promise = require("bluebird");
 
 const { APIError } = require("../errors");
 const { setInstanceInfo } = require("../../redux/reducers/instances").actions;
+const oauth = require("../../redux/reducers/oauth").actions;
 
-function apiCall(state, method, route, payload) {
-	let base = state.oauth.instance;
-	let auth = state.oauth.token;
-	console.log(method, base, route, auth);
-
-	return Promise.try(() => {
-		let url = new URL(base);
-		url.pathname = route;
-		let body;
-
-		if (payload != undefined) {
-			body = JSON.stringify(payload);
-		}
-
-		let headers = {
-			"Accept": "application/json",
-			"Content-Type": "application/json"
-		};
-
-		if (auth != undefined) {
-			headers["Authorization"] = auth;
-		}
-
-		return fetch(url.toString(), {
-			method,
-			headers,
-			body
+function apiCall(method, route, payload) {
+	return function (dispatch, getState) {
+		const state = getState();
+		let base = state.oauth.instance;
+		let auth = state.oauth.token;
+		console.log(method, base, route, "auth:", auth != undefined);
+	
+		return Promise.try(() => {
+			let url = new URL(base);
+			url.pathname = route;
+			let body;
+	
+			if (payload != undefined) {
+				body = JSON.stringify(payload);
+			}
+	
+			let headers = {
+				"Accept": "application/json",
+				"Content-Type": "application/json"
+			};
+	
+			if (auth != undefined) {
+				headers["Authorization"] = auth;
+			}
+	
+			return fetch(url.toString(), {
+				method,
+				headers,
+				body
+			});
+		}).then((res) => {
+			// try parse json even with error
+			let json = res.json().catch((e) => {
+				throw new APIError(`JSON parsing error: ${e.message}`);
+			});
+	
+			return Promise.all([res, json]);
+		}).then(([res, json]) => {
+			if (!res.ok) {
+				if (auth != undefined && res.status == 401) {
+					// stored access token is invalid
+					dispatch(oauth.remove());
+					throw new APIError("Stored OAUTH login was no longer valid, please log in again.");
+				}
+				throw new APIError(json.error, {json});
+			} else {
+				return json;
+			}
 		});
-	}).then((res) => {
-		let ok = res.ok;
-
-		// try parse json even with error
-		let json = res.json().catch((e) => {
-			throw new APIError(`JSON parsing error: ${e.message}`);
-		});
-
-		return Promise.all([ok, json]);
-	}).then(([ok, json]) => {
-		if (!ok) {
-			throw new APIError(json.error, {json});
-		} else {
-			return json;
-		}
-	});
+	};
 }
 
 function getCurrentUrl() {
@@ -88,7 +95,7 @@ function fetchInstance(domain) {
 				oauth: {instance: domain}
 			};
 
-			return apiCall(fakeState, "GET", "/api/v1/instance");
+			return apiCall("GET", "/api/v1/instance")(dispatch, () => fakeState);
 		}).then((json) => {
 			if (json && json.uri) { // TODO: validate instance json more?
 				dispatch(setInstanceInfo([json.uri, json]));
@@ -102,5 +109,6 @@ module.exports = {
 	instance: {
 		fetch: fetchInstance
 	},
-	oauth: require("./oauth")({apiCall, getCurrentUrl})
+	oauth: require("./oauth")({apiCall, getCurrentUrl}),
+	user: require("./user")({apiCall})
 };
\ No newline at end of file
diff --git a/web/source/settings-panel/lib/api/oauth.js b/web/source/settings-panel/lib/api/oauth.js
index 0fbf236d7..1b985e7bd 100644
--- a/web/source/settings-panel/lib/api/oauth.js
+++ b/web/source/settings-panel/lib/api/oauth.js
@@ -24,19 +24,20 @@ const { OAUTHError } = require("../errors");
 
 const oauth = require("../../redux/reducers/oauth").actions;
 const temporary = require("../../redux/reducers/temporary").actions;
+const user = require("../../redux/reducers/user").actions;
 
 module.exports = function oauthAPI({apiCall, getCurrentUrl}) {
 	return {
 
 		register: function register(scopes = []) {
-			return function (dispatch, getState) {
+			return function (dispatch, _getState) {
 				return Promise.try(() => {
-					return apiCall(getState(), "POST", "/api/v1/apps", {
+					return dispatch(apiCall("POST", "/api/v1/apps", {
 						client_name: "GoToSocial Settings",
 						scopes: scopes.join(" "),
 						redirect_uris: getCurrentUrl(),
 						website: getCurrentUrl()
-					});
+					}));
 				}).then((json) => {
 					json.scopes = scopes;
 					dispatch(oauth.setRegistration(json));
@@ -73,13 +74,13 @@ module.exports = function oauthAPI({apiCall, getCurrentUrl}) {
 						throw new OAUTHError("Callback code present, but no client registration is available from localStorage. \nNote: localStorage is unavailable in Private Browsing.");
 					}
 	
-					return apiCall(getState(), "POST", "/oauth/token", {
+					return dispatch(apiCall("POST", "/oauth/token", {
 						client_id: reg.client_id,
 						client_secret: reg.client_secret,
 						redirect_uri: getCurrentUrl(),
 						grant_type: "authorization_code",
 						code: code
-					});
+					}));
 				}).then((json) => {
 					console.log(json);
 					window.history.replaceState({}, document.title, window.location.pathname);
@@ -88,20 +89,6 @@ module.exports = function oauthAPI({apiCall, getCurrentUrl}) {
 			};
 		},
 	
-		verify: function verify() {
-			return function (dispatch, getState) {
-				console.log(getState());
-				return Promise.try(() => {
-					return apiCall(getState(), "GET", "/api/v1/accounts/verify_credentials");
-				}).then((account) => {
-					console.log(account);
-				}).catch((e) => {
-					dispatch(oauth.remove());
-					throw e;
-				});
-			};
-		},
-	
 		logout: function logout() {
 			return function (dispatch, _getState) {
 				// TODO: GoToSocial does not have a logout API route yet
diff --git a/web/source/settings-panel/lib/api/user.js b/web/source/settings-panel/lib/api/user.js
new file mode 100644
index 000000000..e0a9f7dda
--- /dev/null
+++ b/web/source/settings-panel/lib/api/user.js
@@ -0,0 +1,37 @@
+/*
+	GoToSocial
+	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU Affero General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU Affero General Public License for more details.
+
+	You should have received a copy of the GNU Affero General Public License
+	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+"use strict";
+
+const Promise = require("bluebird");
+
+const user = require("../../redux/reducers/user").actions;
+
+module.exports = function({apiCall}) {
+	return {
+		fetchAccount: function fetchAccount() {
+			return function (dispatch, _getState) {
+				return Promise.try(() => {
+					return dispatch(apiCall("GET", "/api/v1/accounts/verify_credentials"));
+				}).then((account) => {
+					return dispatch(user.setAccount(account));
+				});
+			};
+		}	
+	};
+};
\ No newline at end of file
diff --git a/web/source/settings-panel/lib/generate-views.js b/web/source/settings-panel/lib/generate-views.js
index 24822a735..2e825c63a 100644
--- a/web/source/settings-panel/lib/generate-views.js
+++ b/web/source/settings-panel/lib/generate-views.js
@@ -48,7 +48,6 @@ module.exports = function generateViews(struct) {
 				firstRoute = `${base}/${urlSafe(name)}`;
 			}
 
-			console.log(name, ViewComponent);
 			routes.push((
 				<Route path={url} key={url}>
 					<ErrorBoundary FallbackComponent={ErrorFallback} onReset={() => {}}>
diff --git a/web/source/settings-panel/redux/index.js b/web/source/settings-panel/redux/index.js
index 33e9e5044..ab2694223 100644
--- a/web/source/settings-panel/redux/index.js
+++ b/web/source/settings-panel/redux/index.js
@@ -35,6 +35,7 @@ const combinedReducers = combineReducers({
 	oauth: require("./reducers/oauth").reducer,
 	instances: require("./reducers/instances").reducer,
 	temporary: require("./reducers/temporary").reducer,
+	user: require("./reducers/user").reducer,
 });
 
 const persistedReducer = persistReducer(persistConfig, combinedReducers);
diff --git a/web/source/settings-panel/redux/reducers/user.js b/web/source/settings-panel/redux/reducers/user.js
new file mode 100644
index 000000000..480751290
--- /dev/null
+++ b/web/source/settings-panel/redux/reducers/user.js
@@ -0,0 +1,32 @@
+/*
+	GoToSocial
+	Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU Affero General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU Affero General Public License for more details.
+
+	You should have received a copy of the GNU Affero General Public License
+	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+"use strict";
+
+const {createSlice} = require("@reduxjs/toolkit");
+
+module.exports = createSlice({
+	name: "user",
+	initialState: {
+	},
+	reducers: {
+		setAccount: (state, {payload}) => {
+			state.account = payload;
+		}
+	}
+});
\ No newline at end of file
diff --git a/web/source/settings-panel/user/index.js b/web/source/settings-panel/user/index.js
index 7e95beb6b..9c207365b 100644
--- a/web/source/settings-panel/user/index.js
+++ b/web/source/settings-panel/user/index.js
@@ -20,26 +20,9 @@
 
 const Promise = require("bluebird");
 const React = require("react");
-const { Route, Switch } = require("wouter");
-
-module.exports = function UserPanel({oauth, routes}) {
-	// const [account, setAccount] = React.useState({});
-	// const [errorMsg, setError] = React.useState("");
-	// const [statusMsg, setStatus] = React.useState("Fetching user info");
-
-	// React.useEffect(() => {
-	// 	Promise.try(() => {
-	// 		return oauth.apiRequest("/api/v1/accounts/verify_credentials", "GET");
-	// 	}).then((json) => {
-	// 		setAccount(json);
-	// 	}).catch((e) => {
-	// 		setError(e.message);
-	// 		setStatus("");
-	// 	});
-	// }, [oauth, setAccount, setError, setStatus]);
-
-	// throw new Error("test");
+const { Switch } = require("wouter");
 
+module.exports = function UserPanel({routes}) {
 	return (
 		<Switch>
 			{routes}
diff --git a/web/source/settings-panel/user/profile.js b/web/source/settings-panel/user/profile.js
index 8ca8e3176..3d0a16b4e 100644
--- a/web/source/settings-panel/user/profile.js
+++ b/web/source/settings-panel/user/profile.js
@@ -20,11 +20,14 @@
 
 const Promise = require("bluebird");
 const React = require("react");
+const Redux = require("react-redux");
 const { useErrorHandler } = require("react-error-boundary");
 
 const Submit = require("../components/submit");
 
-module.exports = function UserProfile({account, oauth}) {
+module.exports = function UserProfile() {
+	const account = Redux.useSelector(state => state.user.account);
+
 	const [errorMsg, setError] = React.useState("");
 	const [statusMsg, setStatus] = React.useState("");