diff --git a/Cargo.lock b/Cargo.lock
index dee04c62b5f5155b99b40ed77252d88fcfd1e7de..6426d317df3fa64e5a1028e4bc7f6bdd6c6ba189 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2894,6 +2894,7 @@ dependencies = [
  "rustls 0.21.3",
  "serde",
  "serde_json",
+ "serial_test",
  "tokio",
  "tokio-postgres",
  "tokio-postgres-rustls",
diff --git a/Cargo.toml b/Cargo.toml
index 72ab87d4f395711b0f933203b26b116cc62b794d..723d7aabf81b3e76620aecaa5d34416b4f6a3d1c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -166,5 +166,6 @@ tokio-postgres-rustls = { workspace = true }
 chrono = { workspace = true }
 prometheus = { version = "0.13.3", features = ["process"], optional = true }
 actix-web-prom = { version = "0.6.0", optional = true }
+serial_test = { workspace = true }
 clap = { version = "4.3.19", features = ["derive"] }
 lemmy_federate = { version = "0.18.1", path = "crates/federate" }
diff --git a/api_tests/package.json b/api_tests/package.json
index e57d37d088df266593fff62f98164ca8bb79bc84..56624f9e226ac240b97e50deaeccaca9a4781dfb 100644
--- a/api_tests/package.json
+++ b/api_tests/package.json
@@ -19,7 +19,7 @@
     "eslint": "^8.40.0",
     "eslint-plugin-prettier": "^4.0.0",
     "jest": "^29.5.0",
-    "lemmy-js-client": "0.19.0-rc.5",
+    "lemmy-js-client": "0.19.0-rc.12",
     "prettier": "^3.0.0",
     "ts-jest": "^29.1.0",
     "typescript": "^5.0.4"
diff --git a/api_tests/src/comment.spec.ts b/api_tests/src/comment.spec.ts
index 000c0b0ab1a08c5e6361ec24bf5d579196ef2481..394f73dcd77d350f746b82aeefa33683ddbb0993 100644
--- a/api_tests/src/comment.spec.ts
+++ b/api_tests/src/comment.spec.ts
@@ -24,7 +24,6 @@ import {
   reportComment,
   listCommentReports,
   randomString,
-  API,
   unfollows,
   getComments,
   getCommentParentId,
@@ -34,8 +33,10 @@ import {
   getUnreadCount,
   waitUntil,
   delay,
+  alphaUrl,
 } from "./shared";
 import { CommentView } from "lemmy-js-client/dist/types/CommentView";
+import { LemmyHttp } from "lemmy-js-client";
 
 let postOnAlphaRes: PostResponse;
 
@@ -227,10 +228,9 @@ test.skip("Remove a comment from admin and community on the same instance", asyn
 
 test("Remove a comment from admin and community on different instance", async () => {
   let alpha_user = await registerUser(alpha);
-  let newAlphaApi: API = {
-    client: alpha.client,
-    auth: alpha_user.jwt ?? "",
-  };
+  let newAlphaApi = new LemmyHttp(alphaUrl, {
+    headers: { auth: alpha_user.jwt ?? "" },
+  });
 
   // New alpha user creates a community, post, and comment.
   let newCommunity = await createCommunity(newAlphaApi);
diff --git a/api_tests/src/community.spec.ts b/api_tests/src/community.spec.ts
index effd2169e3862aef63ba031fd36a3f794e4f0a37..d91ac8aa75085a34413c8e447ecc23c9b958d3e6 100644
--- a/api_tests/src/community.spec.ts
+++ b/api_tests/src/community.spec.ts
@@ -19,7 +19,6 @@ import {
   getPost,
   resolvePost,
   registerUser,
-  API,
   getPosts,
   getComments,
   createComment,
@@ -27,7 +26,9 @@ import {
   blockInstance,
   waitUntil,
   delay,
+  alphaUrl,
 } from "./shared";
+import { LemmyHttp } from "lemmy-js-client";
 
 beforeAll(async () => {
   await setupLogins();
@@ -258,11 +259,10 @@ test("Admin actions in remote community are not federated to origin", async () =
 
 test("moderator view", async () => {
   // register a new user with their own community on alpha and post to it
-  let otherUser: API = {
-    auth: (await registerUser(alpha)).jwt ?? "",
-    client: alpha.client,
-  };
-  expect(otherUser.auth).not.toBe("");
+  let registerUserRes = await registerUser(alpha);
+  let otherUser = new LemmyHttp(alphaUrl, {
+    headers: { auth: registerUserRes.jwt ?? "" },
+  });
 
   let otherCommunity = (await createCommunity(otherUser)).community_view;
   expect(otherCommunity.community.name).toBeDefined();
diff --git a/api_tests/src/post.spec.ts b/api_tests/src/post.spec.ts
index cd3eec71b6f970d3e77dd1e6b7eea316dd80c6cd..2ef28951ff476cb9a3f88b773509b3de45fb9952 100644
--- a/api_tests/src/post.spec.ts
+++ b/api_tests/src/post.spec.ts
@@ -30,15 +30,16 @@ import {
   listPostReports,
   randomString,
   registerUser,
-  API,
   getSite,
   unfollows,
   resolveCommunity,
   waitUntil,
   delay,
+  alphaUrl,
 } from "./shared";
 import { PostView } from "lemmy-js-client/dist/types/PostView";
 import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
+import { LemmyHttp } from "lemmy-js-client";
 
 let betaCommunity: CommunityView | undefined;
 
@@ -395,17 +396,16 @@ test("Enforce site ban for federated user", async () => {
   // create a test user
   let alphaUserJwt = await registerUser(alpha);
   expect(alphaUserJwt).toBeDefined();
-  let alpha_user: API = {
-    client: alpha.client,
-    auth: alphaUserJwt.jwt ?? "",
-  };
-  const alphaUserActorId = (await getSite(alpha_user)).my_user?.local_user_view
+  let alpha_user = new LemmyHttp(alphaUrl, {
+    headers: { auth: alphaUserJwt.jwt ?? "" },
+  });
+  let alphaUserActorId = (await getSite(alpha_user)).my_user?.local_user_view
     .person.actor_id;
   if (!alphaUserActorId) {
     throw "Missing alpha user actor id";
   }
   expect(alphaUserActorId).toBeDefined();
-  let alphaPerson = (await resolvePerson(alpha_user, alphaUserActorId)).person;
+  let alphaPerson = (await resolvePerson(alpha_user, alphaUserActorId!)).person;
   if (!alphaPerson) {
     throw "Missing alpha person";
   }
@@ -430,7 +430,7 @@ test("Enforce site ban for federated user", async () => {
 
   // alpha ban should be federated to beta
   let alphaUserOnBeta1 = await waitUntil(
-    () => resolvePerson(beta, alphaUserActorId),
+    () => resolvePerson(beta, alphaUserActorId!),
     res => res.person?.person.banned ?? false,
   );
   expect(alphaUserOnBeta1.person?.person.banned).toBe(true);
@@ -456,7 +456,7 @@ test("Enforce site ban for federated user", async () => {
   );
   expect(searchBeta3.posts[0]).toBeDefined();
 
-  let alphaUserOnBeta2 = await resolvePerson(beta, alphaUserActorId);
+  let alphaUserOnBeta2 = await resolvePerson(beta, alphaUserActorId!);
   expect(alphaUserOnBeta2.person?.person.banned).toBe(false);
 });
 
@@ -569,10 +569,9 @@ test("Sanitize HTML", async () => {
   let form: CreatePost = {
     name,
     body,
-    auth: beta.auth,
     community_id: betaCommunity.community.id,
   };
-  let post = await beta.client.createPost(form);
+  let post = await beta.createPost(form);
   // first escaping for the api
   expect(post.post_view.post.body).toBe(
     "<script>alert('xss');</script> hello &"'",
diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts
index f7039942d580701b0432bd74a2fad419120a0f8c..19636f386a96ae01a443da1e9be373843b39fbdf 100644
--- a/api_tests/src/shared.ts
+++ b/api_tests/src/shared.ts
@@ -3,7 +3,6 @@ import {
   BlockInstanceResponse,
   GetReplies,
   GetRepliesResponse,
-  GetUnreadCount,
   GetUnreadCountResponse,
   InstanceId,
   LemmyHttp,
@@ -56,7 +55,6 @@ import { SaveUserSettings } from "lemmy-js-client/dist/types/SaveUserSettings";
 import { DeleteAccount } from "lemmy-js-client/dist/types/DeleteAccount";
 import { GetSiteResponse } from "lemmy-js-client/dist/types/GetSiteResponse";
 import { DeleteAccountResponse } from "lemmy-js-client/dist/types/DeleteAccountResponse";
-import { GetSite } from "lemmy-js-client/dist/types/GetSite";
 import { PrivateMessagesResponse } from "lemmy-js-client/dist/types/PrivateMessagesResponse";
 import { GetPrivateMessages } from "lemmy-js-client/dist/types/GetPrivateMessages";
 import { PostReportResponse } from "lemmy-js-client/dist/types/PostReportResponse";
@@ -73,35 +71,17 @@ import { GetPersonDetailsResponse } from "lemmy-js-client/dist/types/GetPersonDe
 import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails";
 import { ListingType } from "lemmy-js-client/dist/types/ListingType";
 
-export interface API {
-  client: LemmyHttp;
-  auth: string;
-}
-
-export let alpha: API = {
-  client: new LemmyHttp("http://127.0.0.1:8541"),
-  auth: "",
-};
-
-export let beta: API = {
-  client: new LemmyHttp("http://127.0.0.1:8551"),
-  auth: "",
-};
-
-export let gamma: API = {
-  client: new LemmyHttp("http://127.0.0.1:8561"),
-  auth: "",
-};
+export let alphaUrl = "http://127.0.0.1:8541";
+export let betaUrl = "http://127.0.0.1:8551";
+export let gammaUrl = "http://127.0.0.1:8561";
+export let deltaUrl = "http://127.0.0.1:8571";
+export let epsilonUrl = "http://127.0.0.1:8581";
 
-export let delta: API = {
-  client: new LemmyHttp("http://127.0.0.1:8571"),
-  auth: "",
-};
-
-export let epsilon: API = {
-  client: new LemmyHttp("http://127.0.0.1:8581"),
-  auth: "",
-};
+export let alpha = new LemmyHttp(alphaUrl);
+export let beta = new LemmyHttp(betaUrl);
+export let gamma = new LemmyHttp(gammaUrl);
+export let delta = new LemmyHttp(deltaUrl);
+export let epsilon = new LemmyHttp(epsilonUrl);
 
 const password = "lemmylemmy";
 
@@ -110,31 +90,31 @@ export async function setupLogins() {
     username_or_email: "lemmy_alpha",
     password,
   };
-  let resAlpha = alpha.client.login(formAlpha);
+  let resAlpha = alpha.login(formAlpha);
 
   let formBeta: Login = {
     username_or_email: "lemmy_beta",
     password,
   };
-  let resBeta = beta.client.login(formBeta);
+  let resBeta = beta.login(formBeta);
 
   let formGamma: Login = {
     username_or_email: "lemmy_gamma",
     password,
   };
-  let resGamma = gamma.client.login(formGamma);
+  let resGamma = gamma.login(formGamma);
 
   let formDelta: Login = {
     username_or_email: "lemmy_delta",
     password,
   };
-  let resDelta = delta.client.login(formDelta);
+  let resDelta = delta.login(formDelta);
 
   let formEpsilon: Login = {
     username_or_email: "lemmy_epsilon",
     password,
   };
-  let resEpsilon = epsilon.client.login(formEpsilon);
+  let resEpsilon = epsilon.login(formEpsilon);
 
   let res = await Promise.all([
     resAlpha,
@@ -143,12 +123,11 @@ export async function setupLogins() {
     resDelta,
     resEpsilon,
   ]);
-
-  alpha.auth = res[0].jwt ?? "";
-  beta.auth = res[1].jwt ?? "";
-  gamma.auth = res[2].jwt ?? "";
-  delta.auth = res[3].jwt ?? "";
-  epsilon.auth = res[4].jwt ?? "";
+  alpha.setHeaders({ auth: res[0].jwt ?? "" });
+  beta.setHeaders({ auth: res[1].jwt ?? "" });
+  gamma.setHeaders({ auth: res[2].jwt ?? "" });
+  delta.setHeaders({ auth: res[3].jwt ?? "" });
+  epsilon.setHeaders({ auth: res[4].jwt ?? "" });
 
   // Registration applications are now enabled by default, need to disable them
   let editSiteForm: EditSite = {
@@ -159,45 +138,39 @@ export async function setupLogins() {
     rate_limit_image: 999,
     rate_limit_comment: 999,
     rate_limit_search: 999,
-    auth: "",
   };
 
   // Set the blocks and auths for each
-  editSiteForm.auth = alpha.auth;
   editSiteForm.allowed_instances = [
     "lemmy-beta",
     "lemmy-gamma",
     "lemmy-delta",
     "lemmy-epsilon",
   ];
-  await alpha.client.editSite(editSiteForm);
+  await alpha.editSite(editSiteForm);
 
-  editSiteForm.auth = beta.auth;
   editSiteForm.allowed_instances = [
     "lemmy-alpha",
     "lemmy-gamma",
     "lemmy-delta",
     "lemmy-epsilon",
   ];
-  await beta.client.editSite(editSiteForm);
+  await beta.editSite(editSiteForm);
 
-  editSiteForm.auth = gamma.auth;
   editSiteForm.allowed_instances = [
     "lemmy-alpha",
     "lemmy-beta",
     "lemmy-delta",
     "lemmy-epsilon",
   ];
-  await gamma.client.editSite(editSiteForm);
+  await gamma.editSite(editSiteForm);
 
   editSiteForm.allowed_instances = ["lemmy-beta"];
-  editSiteForm.auth = delta.auth;
-  await delta.client.editSite(editSiteForm);
+  await delta.editSite(editSiteForm);
 
-  editSiteForm.auth = epsilon.auth;
   editSiteForm.allowed_instances = [];
   editSiteForm.blocked_instances = ["lemmy-alpha"];
-  await epsilon.client.editSite(editSiteForm);
+  await epsilon.editSite(editSiteForm);
 
   // Create the main alpha/beta communities
   // Ignore thrown errors of duplicates
@@ -215,7 +188,7 @@ export async function setupLogins() {
 }
 
 export async function createPost(
-  api: API,
+  api: LemmyHttp,
   community_id: number,
 ): Promise<PostResponse> {
   let name = randomString(5);
@@ -227,50 +200,49 @@ export async function createPost(
     name,
     url,
     body,
-    auth: api.auth,
     community_id,
   };
-  return api.client.createPost(form);
+  return api.createPost(form);
 }
 
-export async function editPost(api: API, post: Post): Promise<PostResponse> {
+export async function editPost(
+  api: LemmyHttp,
+  post: Post,
+): Promise<PostResponse> {
   let name = "A jest test federated post, updated";
   let form: EditPost = {
     name,
     post_id: post.id,
-    auth: api.auth,
   };
-  return api.client.editPost(form);
+  return api.editPost(form);
 }
 
 export async function deletePost(
-  api: API,
+  api: LemmyHttp,
   deleted: boolean,
   post: Post,
 ): Promise<PostResponse> {
   let form: DeletePost = {
     post_id: post.id,
     deleted: deleted,
-    auth: api.auth,
   };
-  return api.client.deletePost(form);
+  return api.deletePost(form);
 }
 
 export async function removePost(
-  api: API,
+  api: LemmyHttp,
   removed: boolean,
   post: Post,
 ): Promise<PostResponse> {
   let form: RemovePost = {
     post_id: post.id,
     removed,
-    auth: api.auth,
   };
-  return api.client.removePost(form);
+  return api.removePost(form);
 }
 
 export async function featurePost(
-  api: API,
+  api: LemmyHttp,
   featured: boolean,
   post: Post,
 ): Promise<PostResponse> {
@@ -278,61 +250,56 @@ export async function featurePost(
     post_id: post.id,
     featured,
     feature_type: "Community",
-    auth: api.auth,
   };
-  return api.client.featurePost(form);
+  return api.featurePost(form);
 }
 
 export async function lockPost(
-  api: API,
+  api: LemmyHttp,
   locked: boolean,
   post: Post,
 ): Promise<PostResponse> {
   let form: LockPost = {
     post_id: post.id,
     locked,
-    auth: api.auth,
   };
-  return api.client.lockPost(form);
+  return api.lockPost(form);
 }
 
 export async function resolvePost(
-  api: API,
+  api: LemmyHttp,
   post: Post,
 ): Promise<ResolveObjectResponse> {
   let form: ResolveObject = {
     q: post.ap_id,
-    auth: api.auth,
   };
-  return api.client.resolveObject(form);
+  return api.resolveObject(form);
 }
 
 export async function searchPostLocal(
-  api: API,
+  api: LemmyHttp,
   post: Post,
 ): Promise<SearchResponse> {
   let form: Search = {
     q: post.name,
     type_: "Posts",
     sort: "TopAll",
-    auth: api.auth,
   };
-  return api.client.search(form);
+  return api.search(form);
 }
 
 export async function getPost(
-  api: API,
+  api: LemmyHttp,
   post_id: number,
 ): Promise<GetPostResponse> {
   let form: GetPost = {
     id: post_id,
-    auth: api.auth,
   };
-  return api.client.getPost(form);
+  return api.getPost(form);
 }
 
 export async function getComments(
-  api: API,
+  api: LemmyHttp,
   post_id?: number,
   listingType: ListingType = "All",
 ): Promise<GetCommentsResponse> {
@@ -340,75 +307,66 @@ export async function getComments(
     post_id: post_id,
     type_: listingType,
     sort: "New",
-    auth: api.auth,
   };
-  return api.client.getComments(form);
+  return api.getComments(form);
 }
 
 export async function getUnreadCount(
-  api: API,
+  api: LemmyHttp,
 ): Promise<GetUnreadCountResponse> {
-  let form: GetUnreadCount = {
-    auth: api.auth,
-  };
-  return api.client.getUnreadCount(form);
+  return api.getUnreadCount();
 }
 
-export async function getReplies(api: API): Promise<GetRepliesResponse> {
+export async function getReplies(api: LemmyHttp): Promise<GetRepliesResponse> {
   let form: GetReplies = {
     sort: "New",
     unread_only: false,
-    auth: api.auth,
   };
-  return api.client.getReplies(form);
+  return api.getReplies(form);
 }
 
 export async function resolveComment(
-  api: API,
+  api: LemmyHttp,
   comment: Comment,
 ): Promise<ResolveObjectResponse> {
   let form: ResolveObject = {
     q: comment.ap_id,
-    auth: api.auth,
   };
-  return api.client.resolveObject(form);
+  return api.resolveObject(form);
 }
 
 export async function resolveBetaCommunity(
-  api: API,
+  api: LemmyHttp,
 ): Promise<ResolveObjectResponse> {
   // Use short-hand search url
   let form: ResolveObject = {
     q: "!main@lemmy-beta:8551",
-    auth: api.auth,
   };
-  return api.client.resolveObject(form);
+  return api.resolveObject(form);
 }
 
 export async function resolveCommunity(
-  api: API,
+  api: LemmyHttp,
   q: string,
 ): Promise<ResolveObjectResponse> {
   let form: ResolveObject = {
     q,
-    auth: api.auth,
   };
-  return api.client.resolveObject(form);
+  return api.resolveObject(form);
 }
 
 export async function resolvePerson(
-  api: API,
+  api: LemmyHttp,
   apShortname: string,
 ): Promise<ResolveObjectResponse> {
   let form: ResolveObject = {
     q: apShortname,
-    auth: api.auth,
   };
-  return api.client.resolveObject(form);
+  return api.resolveObject(form);
 }
 
 export async function banPersonFromSite(
-  api: API,
+  api: LemmyHttp,
   person_id: number,
   ban: boolean,
   remove_data: boolean,
@@ -418,13 +376,12 @@ export async function banPersonFromSite(
     person_id,
     ban,
     remove_data: remove_data,
-    auth: api.auth,
   };
-  return api.client.banPerson(form);
+  return api.banPerson(form);
 }
 
 export async function banPersonFromCommunity(
-  api: API,
+  api: LemmyHttp,
   person_id: number,
   community_id: number,
   remove_data: boolean,
@@ -435,40 +392,37 @@ export async function banPersonFromCommunity(
     community_id,
     remove_data: remove_data,
     ban,
-    auth: api.auth,
   };
-  return api.client.banFromCommunity(form);
+  return api.banFromCommunity(form);
 }
 
 export async function followCommunity(
-  api: API,
+  api: LemmyHttp,
   follow: boolean,
   community_id: number,
 ): Promise<CommunityResponse> {
   let form: FollowCommunity = {
     community_id,
     follow,
-    auth: api.auth,
   };
-  return api.client.followCommunity(form);
+  return api.followCommunity(form);
 }
 
 export async function likePost(
-  api: API,
+  api: LemmyHttp,
   score: number,
   post: Post,
 ): Promise<PostResponse> {
   let form: CreatePostLike = {
     post_id: post.id,
     score: score,
-    auth: api.auth,
   };
 
-  return api.client.likePost(form);
+  return api.likePost(form);
 }
 
 export async function createComment(
-  api: API,
+  api: LemmyHttp,
   post_id: number,
   parent_id?: number,
   content = "a jest test comment",
@@ -477,76 +431,70 @@ export async function createComment(
     content,
     post_id,
     parent_id,
-    auth: api.auth,
   };
-  return api.client.createComment(form);
+  return api.createComment(form);
 }
 
 export async function editComment(
-  api: API,
+  api: LemmyHttp,
   comment_id: number,
   content = "A jest test federated comment update",
 ): Promise<CommentResponse> {
   let form: EditComment = {
     content,
     comment_id,
-    auth: api.auth,
   };
-  return api.client.editComment(form);
+  return api.editComment(form);
 }
 
 export async function deleteComment(
-  api: API,
+  api: LemmyHttp,
   deleted: boolean,
   comment_id: number,
 ): Promise<CommentResponse> {
   let form: DeleteComment = {
     comment_id,
     deleted,
-    auth: api.auth,
   };
-  return api.client.deleteComment(form);
+  return api.deleteComment(form);
 }
 
 export async function removeComment(
-  api: API,
+  api: LemmyHttp,
   removed: boolean,
   comment_id: number,
 ): Promise<CommentResponse> {
   let form: RemoveComment = {
     comment_id,
     removed,
-    auth: api.auth,
   };
-  return api.client.removeComment(form);
+  return api.removeComment(form);
 }
 
 export async function getMentions(
-  api: API,
+  api: LemmyHttp,
 ): Promise<GetPersonMentionsResponse> {
   let form: GetPersonMentions = {
     sort: "New",
     unread_only: false,
-    auth: api.auth,
   };
-  return api.client.getPersonMentions(form);
+  return api.getPersonMentions(form);
 }
 
 export async function likeComment(
-  api: API,
+  api: LemmyHttp,
   score: number,
   comment: Comment,
 ): Promise<CommentResponse> {
   let form: CreateCommentLike = {
     comment_id: comment.id,
     score,
-    auth: api.auth,
   };
-  return api.client.likeComment(form);
+  return api.likeComment(form);
 }
 
 export async function createCommunity(
-  api: API,
+  api: LemmyHttp,
   name_: string = randomString(5),
 ): Promise<CommunityResponse> {
   let description = "a sample description";
@@ -554,100 +502,92 @@ export async function createCommunity(
     name: name_,
     title: name_,
     description,
-    auth: api.auth,
   };
-  return api.client.createCommunity(form);
+  return api.createCommunity(form);
 }
 
 export async function getCommunity(
-  api: API,
+  api: LemmyHttp,
   id: number,
 ): Promise<CommunityResponse> {
   let form: GetCommunity = {
     id,
-    auth: api.auth,
   };
-  return api.client.getCommunity(form);
+  return api.getCommunity(form);
 }
 
 export async function getCommunityByName(
-  api: API,
+  api: LemmyHttp,
   name: string,
 ): Promise<CommunityResponse> {
   let form: GetCommunity = {
     name,
-    auth: api.auth,
   };
-  return api.client.getCommunity(form);
+  return api.getCommunity(form);
 }
 
 export async function deleteCommunity(
-  api: API,
+  api: LemmyHttp,
   deleted: boolean,
   community_id: number,
 ): Promise<CommunityResponse> {
   let form: DeleteCommunity = {
     community_id,
     deleted,
-    auth: api.auth,
   };
-  return api.client.deleteCommunity(form);
+  return api.deleteCommunity(form);
 }
 
 export async function removeCommunity(
-  api: API,
+  api: LemmyHttp,
   removed: boolean,
   community_id: number,
 ): Promise<CommunityResponse> {
   let form: RemoveCommunity = {
     community_id,
     removed,
-    auth: api.auth,
   };
-  return api.client.removeCommunity(form);
+  return api.removeCommunity(form);
 }
 
 export async function createPrivateMessage(
-  api: API,
+  api: LemmyHttp,
   recipient_id: number,
 ): Promise<PrivateMessageResponse> {
   let content = "A jest test federated private message";
   let form: CreatePrivateMessage = {
     content,
     recipient_id,
-    auth: api.auth,
   };
-  return api.client.createPrivateMessage(form);
+  return api.createPrivateMessage(form);
 }
 
 export async function editPrivateMessage(
-  api: API,
+  api: LemmyHttp,
   private_message_id: number,
 ): Promise<PrivateMessageResponse> {
   let updatedContent = "A jest test federated private message edited";
   let form: EditPrivateMessage = {
     content: updatedContent,
     private_message_id,
-    auth: api.auth,
   };
-  return api.client.editPrivateMessage(form);
+  return api.editPrivateMessage(form);
 }
 
 export async function deletePrivateMessage(
-  api: API,
+  api: LemmyHttp,
   deleted: boolean,
   private_message_id: number,
 ): Promise<PrivateMessageResponse> {
   let form: DeletePrivateMessage = {
     deleted,
     private_message_id,
-    auth: api.auth,
   };
-  return api.client.deletePrivateMessage(form);
+  return api.deletePrivateMessage(form);
 }
 
 export async function registerUser(
-  api: API,
+  api: LemmyHttp,
   username: string = randomString(5),
 ): Promise<LoginResponse> {
   let form: Register = {
@@ -656,10 +596,12 @@ export async function registerUser(
     password_verify: password,
     show_nsfw: true,
   };
-  return api.client.register(form);
+  return api.register(form);
 }
 
-export async function saveUserSettingsBio(api: API): Promise<LoginResponse> {
+export async function saveUserSettingsBio(
+  api: LemmyHttp,
+): Promise<LoginResponse> {
   let form: SaveUserSettings = {
     show_nsfw: true,
     blur_nsfw: false,
@@ -671,13 +613,12 @@ export async function saveUserSettingsBio(api: API): Promise<LoginResponse> {
     show_avatars: true,
     send_notifications_to_email: false,
     bio: "a changed bio",
-    auth: api.auth,
   };
   return saveUserSettings(api, form);
 }
 
 export async function saveUserSettingsFederated(
-  api: API,
+  api: LemmyHttp,
 ): Promise<LoginResponse> {
   let avatar = "https://image.flaticon.com/icons/png/512/35/35896.png";
   let banner = "https://image.flaticon.com/icons/png/512/36/35896.png";
@@ -695,55 +636,52 @@ export async function saveUserSettingsFederated(
     show_avatars: false,
     send_notifications_to_email: false,
     bio,
-    auth: api.auth,
   };
-  return await saveUserSettings(alpha, form);
+  return await saveUserSettings(api, form);
 }
 
 export async function saveUserSettings(
-  api: API,
+  api: LemmyHttp,
   form: SaveUserSettings,
 ): Promise<LoginResponse> {
-  return api.client.saveUserSettings(form);
+  return api.saveUserSettings(form);
 }
 export async function getPersonDetails(
-  api: API,
+  api: LemmyHttp,
   person_id: number,
 ): Promise<GetPersonDetailsResponse> {
   let form: GetPersonDetails = {
-    auth: api.auth,
     person_id: person_id,
   };
-  return api.client.getPersonDetails(form);
+  return api.getPersonDetails(form);
 }
 
-export async function deleteUser(api: API): Promise<DeleteAccountResponse> {
+export async function deleteUser(
+  api: LemmyHttp,
+): Promise<DeleteAccountResponse> {
   let form: DeleteAccount = {
-    auth: api.auth,
     delete_content: true,
     password,
   };
-  return api.client.deleteAccount(form);
+  return api.deleteAccount(form);
 }
 
-export async function getSite(api: API): Promise<GetSiteResponse> {
-  let form: GetSite = {
-    auth: api.auth,
-  };
-  return api.client.getSite(form);
+export async function getSite(api: LemmyHttp): Promise<GetSiteResponse> {
+  return api.getSite();
 }
 
 export async function listPrivateMessages(
-  api: API,
+  api: LemmyHttp,
 ): Promise<PrivateMessagesResponse> {
   let form: GetPrivateMessages = {
-    auth: api.auth,
     unread_only: false,
   };
-  return api.client.getPrivateMessages(form);
+  return api.getPrivateMessages(form);
 }
 
-export async function unfollowRemotes(api: API): Promise<GetSiteResponse> {
+export async function unfollowRemotes(
+  api: LemmyHttp,
+): Promise<GetSiteResponse> {
   // Unfollow all remote communities
   let site = await getSite(api);
   let remoteFollowed =
@@ -755,7 +693,7 @@ export async function unfollowRemotes(api: API): Promise<GetSiteResponse> {
   return siteRes;
 }
 
-export async function followBeta(api: API): Promise<CommunityResponse> {
+export async function followBeta(api: LemmyHttp): Promise<CommunityResponse> {
   let betaCommunity = (await resolveBetaCommunity(api)).community;
   if (betaCommunity) {
     let follow = await followCommunity(api, true, betaCommunity.community.id);
@@ -766,71 +704,63 @@ export async function followBeta(api: API): Promise<CommunityResponse> {
 }
 
 export async function reportPost(
-  api: API,
+  api: LemmyHttp,
   post_id: number,
   reason: string,
 ): Promise<PostReportResponse> {
   let form: CreatePostReport = {
     post_id,
     reason,
-    auth: api.auth,
   };
-  return api.client.createPostReport(form);
+  return api.createPostReport(form);
 }
 
 export async function listPostReports(
-  api: API,
+  api: LemmyHttp,
 ): Promise<ListPostReportsResponse> {
-  let form: ListPostReports = {
-    auth: api.auth,
-  };
-  return api.client.listPostReports(form);
+  let form: ListPostReports = {};
+  return api.listPostReports(form);
 }
 
 export async function reportComment(
-  api: API,
+  api: LemmyHttp,
   comment_id: number,
   reason: string,
 ): Promise<CommentReportResponse> {
   let form: CreateCommentReport = {
     comment_id,
     reason,
-    auth: api.auth,
   };
-  return api.client.createCommentReport(form);
+  return api.createCommentReport(form);
 }
 
 export async function listCommentReports(
-  api: API,
+  api: LemmyHttp,
 ): Promise<ListCommentReportsResponse> {
-  let form: ListCommentReports = {
-    auth: api.auth,
-  };
-  return api.client.listCommentReports(form);
+  let form: ListCommentReports = {};
+  return api.listCommentReports(form);
 }
 
 export function getPosts(
-  api: API,
+  api: LemmyHttp,
   listingType?: ListingType,
 ): Promise<GetPostsResponse> {
   let form: GetPosts = {
-    auth: api.auth,
     type_: listingType,
   };
-  return api.client.getPosts(form);
+  return api.getPosts(form);
 }
 
 export function blockInstance(
-  api: API,
+  api: LemmyHttp,
   instance_id: InstanceId,
   block: boolean,
 ): Promise<BlockInstanceResponse> {
   let form: BlockInstance = {
     instance_id,
     block,
-    auth: api.auth,
   };
-  return api.client.blockInstance(form);
+  return api.blockInstance(form);
 }
 
 export function delay(millis = 500) {
diff --git a/api_tests/src/user.spec.ts b/api_tests/src/user.spec.ts
index f488ebe1e6afd651c63895952af5a079a7576744..e679e80537b9a7778fd42ce37f885560eb45058e 100644
--- a/api_tests/src/user.spec.ts
+++ b/api_tests/src/user.spec.ts
@@ -13,10 +13,10 @@ import {
   resolveBetaCommunity,
   deleteUser,
   resolvePost,
-  API,
   resolveComment,
   saveUserSettingsFederated,
   setupLogins,
+  alphaUrl,
 } from "./shared";
 import { LemmyHttp } from "lemmy-js-client";
 import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
@@ -40,9 +40,11 @@ function assertUserFederation(userOne?: PersonView, userTwo?: PersonView) {
 test("Create user", async () => {
   let userRes = await registerUser(alpha);
   expect(userRes.jwt).toBeDefined();
-  alpha.auth = userRes.jwt ?? "";
+  let user = new LemmyHttp(alphaUrl, {
+    headers: { auth: userRes.jwt ?? "" },
+  });
 
-  let site = await getSite(alpha);
+  let site = await getSite(user);
   expect(site.my_user).toBeDefined();
   if (!site.my_user) {
     throw "Missing site user";
@@ -60,10 +62,9 @@ test("Set some user settings, check that they are federated", async () => {
 test("Delete user", async () => {
   let userRes = await registerUser(alpha);
   expect(userRes.jwt).toBeDefined();
-  let user: API = {
-    client: alpha.client,
-    auth: userRes.jwt ?? "",
-  };
+  let user = new LemmyHttp(alphaUrl, {
+    headers: { auth: userRes.jwt ?? "" },
+  });
 
   // make a local post and comment
   let alphaCommunity = (await resolveCommunity(user, "!main@lemmy-alpha:8541"))
@@ -107,17 +108,14 @@ test("Delete user", async () => {
 });
 
 test("Requests with invalid auth should be treated as unauthenticated", async () => {
-  let invalid_auth: API = {
-    client: new LemmyHttp("http://127.0.0.1:8541"),
-    auth: "invalid",
-  };
+  let invalid_auth = new LemmyHttp(alphaUrl, {
+    headers: { auth: "" },
+  });
   let site = await getSite(invalid_auth);
   expect(site.my_user).toBeUndefined();
   expect(site.site_view).toBeDefined();
 
-  let form: GetPosts = {
-    auth: "invalid",
-  };
-  let posts = invalid_auth.client.getPosts(form);
+  let form: GetPosts = {};
+  let posts = invalid_auth.getPosts(form);
   expect((await posts).posts).toBeDefined();
 });
diff --git a/api_tests/yarn.lock b/api_tests/yarn.lock
index 3b6a85146889a51c7f1c41bebd2a75df638a06f4..eb9859b8d3c559ba98bee12643f9908557af22b4 100644
--- a/api_tests/yarn.lock
+++ b/api_tests/yarn.lock
@@ -2174,10 +2174,10 @@ kleur@^3.0.3:
   resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
   integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
 
-lemmy-js-client@0.19.0-rc.5:
-  version "0.19.0-rc.5"
-  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.19.0-rc.5.tgz#a0d3e0ac829b1e46124edcf3a0343ae04317d51a"
-  integrity sha512-Z1T95Ht1VZNvWlLH9XpVnO2oC7LhMT81jTiU5BhYPIkEtGhOwN91jk5uI1oyI6/d4v9lwbrsyzFYPsiuMmXTFQ==
+lemmy-js-client@0.19.0-rc.12:
+  version "0.19.0-rc.12"
+  resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.19.0-rc.12.tgz#e3bd4e21b1966d583ab790ef70ece8394b012b48"
+  integrity sha512-1iu2fW9vlb3TrI+QR/ODP3+5pWZB0rUqL1wH09IzomDXohCqoQvfmXpwArmgF4Eq8GZgjkcfeMDC2gMrfw/i7Q==
   dependencies:
     cross-fetch "^3.1.5"
     form-data "^4.0.0"
diff --git a/crates/api/src/comment/distinguish.rs b/crates/api/src/comment/distinguish.rs
index a378e51b9e09e77ecade12c25ab1a82de8274624..5059e2db7eea25b7dc498097d4ab6ed15fdb1e28 100644
--- a/crates/api/src/comment/distinguish.rs
+++ b/crates/api/src/comment/distinguish.rs
@@ -2,22 +2,21 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   comment::{CommentResponse, DistinguishComment},
   context::LemmyContext,
-  utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
+  utils::{check_community_ban, is_mod_or_admin},
 };
 use lemmy_db_schema::{
   source::comment::{Comment, CommentUpdateForm},
   traits::Crud,
 };
-use lemmy_db_views::structs::CommentView;
+use lemmy_db_views::structs::{CommentView, LocalUserView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn distinguish_comment(
   data: Json<DistinguishComment>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommentResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let orig_comment = CommentView::read(&mut context.pool(), data.comment_id, None).await?;
 
   check_community_ban(
diff --git a/crates/api/src/comment/like.rs b/crates/api/src/comment/like.rs
index af013f2831a60c1e7c843b8c4cafe773057176c8..ee0a0bd7e135c05031a85e42d4e779ce1a44f53f 100644
--- a/crates/api/src/comment/like.rs
+++ b/crates/api/src/comment/like.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   comment::{CommentResponse, CreateCommentLike},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{check_community_ban, check_downvotes_enabled, local_user_view_from_jwt},
+  utils::{check_community_ban, check_downvotes_enabled},
 };
 use lemmy_db_schema::{
   newtypes::LocalUserId,
@@ -24,9 +24,9 @@ use std::ops::Deref;
 pub async fn like_comment(
   data: Json<CreateCommentLike>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommentResponse>, LemmyError> {
   let local_site = LocalSite::read(&mut context.pool()).await?;
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
 
   let mut recipient_ids = Vec::<LocalUserId>::new();
 
diff --git a/crates/api/src/comment/save.rs b/crates/api/src/comment/save.rs
index f2c926f17a60aa0dfaf01a4bb553cd3d3820265c..95c08e7010ef60563b10f7a22e11c92851ae07b0 100644
--- a/crates/api/src/comment/save.rs
+++ b/crates/api/src/comment/save.rs
@@ -2,22 +2,20 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   comment::{CommentResponse, SaveComment},
   context::LemmyContext,
-  utils::local_user_view_from_jwt,
 };
 use lemmy_db_schema::{
   source::comment::{CommentSaved, CommentSavedForm},
   traits::Saveable,
 };
-use lemmy_db_views::structs::CommentView;
+use lemmy_db_views::structs::{CommentView, LocalUserView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn save_comment(
   data: Json<SaveComment>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommentResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let comment_saved_form = CommentSavedForm {
     comment_id: data.comment_id,
     person_id: local_user_view.person.id,
diff --git a/crates/api/src/comment_report/create.rs b/crates/api/src/comment_report/create.rs
index e493d60a3c3b86c9845d4932394c3ac082eb829b..3c1304674b2e6185a3cad604d04b6bac2700fc13 100644
--- a/crates/api/src/comment_report/create.rs
+++ b/crates/api/src/comment_report/create.rs
@@ -5,12 +5,7 @@ use lemmy_api_common::{
   comment::{CommentReportResponse, CreateCommentReport},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{
-    check_community_ban,
-    local_user_view_from_jwt,
-    sanitize_html_api,
-    send_new_report_email_to_admins,
-  },
+  utils::{check_community_ban, sanitize_html_api, send_new_report_email_to_admins},
 };
 use lemmy_db_schema::{
   source::{
@@ -19,7 +14,7 @@ use lemmy_db_schema::{
   },
   traits::Reportable,
 };
-use lemmy_db_views::structs::{CommentReportView, CommentView};
+use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 /// Creates a comment report and notifies the moderators of the community
@@ -27,8 +22,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn create_comment_report(
   data: Json<CreateCommentReport>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommentReportResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   let reason = sanitize_html_api(data.reason.trim());
diff --git a/crates/api/src/comment_report/list.rs b/crates/api/src/comment_report/list.rs
index 839288521b4619335c55d35d926c5d756808fd5a..bc8dd26774da2abac70404984c282decd51c3e42 100644
--- a/crates/api/src/comment_report/list.rs
+++ b/crates/api/src/comment_report/list.rs
@@ -2,9 +2,8 @@ use actix_web::web::{Data, Json, Query};
 use lemmy_api_common::{
   comment::{ListCommentReports, ListCommentReportsResponse},
   context::LemmyContext,
-  utils::local_user_view_from_jwt,
 };
-use lemmy_db_views::comment_report_view::CommentReportQuery;
+use lemmy_db_views::{comment_report_view::CommentReportQuery, structs::LocalUserView};
 use lemmy_utils::error::LemmyError;
 
 /// Lists comment reports for a community if an id is supplied
@@ -13,9 +12,8 @@ use lemmy_utils::error::LemmyError;
 pub async fn list_comment_reports(
   data: Query<ListCommentReports>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<ListCommentReportsResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let community_id = data.community_id;
   let unresolved_only = data.unresolved_only.unwrap_or_default();
 
diff --git a/crates/api/src/comment_report/resolve.rs b/crates/api/src/comment_report/resolve.rs
index 8e03484e8d2224d65545f1a47ebc70cafde0d5d2..8296f068bac6ae0fb358ce7ae871c939bd21c987 100644
--- a/crates/api/src/comment_report/resolve.rs
+++ b/crates/api/src/comment_report/resolve.rs
@@ -2,10 +2,10 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   comment::{CommentReportResponse, ResolveCommentReport},
   context::LemmyContext,
-  utils::{is_mod_or_admin, local_user_view_from_jwt},
+  utils::is_mod_or_admin,
 };
 use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
-use lemmy_db_views::structs::CommentReportView;
+use lemmy_db_views::structs::{CommentReportView, LocalUserView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 /// Resolves or unresolves a comment report and notifies the moderators of the community
@@ -13,9 +13,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn resolve_comment_report(
   data: Json<ResolveCommentReport>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommentReportResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let report_id = data.report_id;
   let person_id = local_user_view.person.id;
   let report = CommentReportView::read(&mut context.pool(), report_id, person_id).await?;
diff --git a/crates/api/src/community/add_mod.rs b/crates/api/src/community/add_mod.rs
index f5fac15035266c0f1b62c7cac729380eeec75543..e08686361f3f8763ace9dfa093f08278f27e766e 100644
--- a/crates/api/src/community/add_mod.rs
+++ b/crates/api/src/community/add_mod.rs
@@ -4,7 +4,7 @@ use lemmy_api_common::{
   community::{AddModToCommunity, AddModToCommunityResponse},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{is_mod_or_admin, local_user_view_from_jwt},
+  utils::is_mod_or_admin,
 };
 use lemmy_db_schema::{
   source::{
@@ -13,6 +13,7 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Joinable},
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::CommunityModeratorView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
@@ -20,9 +21,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn add_mod_to_community(
   data: Json<AddModToCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<AddModToCommunityResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let community_id = data.community_id;
 
   // Verify that only mods or admins can add mod
diff --git a/crates/api/src/community/ban.rs b/crates/api/src/community/ban.rs
index e969e9b78c74b93a3534e93c9ae83130cfcfb31b..c2f70b7c07eda216e303b024e7527e136a467f58 100644
--- a/crates/api/src/community/ban.rs
+++ b/crates/api/src/community/ban.rs
@@ -4,12 +4,7 @@ use lemmy_api_common::{
   community::{BanFromCommunity, BanFromCommunityResponse},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{
-    is_mod_or_admin,
-    local_user_view_from_jwt,
-    remove_user_data_in_community,
-    sanitize_html_api_opt,
-  },
+  utils::{is_mod_or_admin, remove_user_data_in_community, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   source::{
@@ -23,6 +18,7 @@ use lemmy_db_schema::{
   },
   traits::{Bannable, Crud, Followable},
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::PersonView;
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
@@ -33,9 +29,8 @@ use lemmy_utils::{
 pub async fn ban_from_community(
   data: Json<BanFromCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<BanFromCommunityResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let banned_person_id = data.person_id;
   let remove_data = data.remove_data.unwrap_or(false);
   let expires = data.expires.map(naive_from_unix);
diff --git a/crates/api/src/community/block.rs b/crates/api/src/community/block.rs
index f27b7c01bd80a5b195dffe10ac2e2b088479c046..fd4a5a01bf3b268252871203f1a2b1a738331c22 100644
--- a/crates/api/src/community/block.rs
+++ b/crates/api/src/community/block.rs
@@ -4,7 +4,6 @@ use lemmy_api_common::{
   community::{BlockCommunity, BlockCommunityResponse},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::local_user_view_from_jwt,
 };
 use lemmy_db_schema::{
   source::{
@@ -13,6 +12,7 @@ use lemmy_db_schema::{
   },
   traits::{Blockable, Followable},
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
@@ -20,9 +20,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn block_community(
   data: Json<BlockCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<BlockCommunityResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let community_id = data.community_id;
   let person_id = local_user_view.person.id;
   let community_block_form = CommunityBlockForm {
diff --git a/crates/api/src/community/follow.rs b/crates/api/src/community/follow.rs
index 2a50a94edcc982192692ddfed681909b6d23dc34..91ffce714032e3f917176070a1120324eb793280 100644
--- a/crates/api/src/community/follow.rs
+++ b/crates/api/src/community/follow.rs
@@ -4,7 +4,7 @@ use lemmy_api_common::{
   community::{CommunityResponse, FollowCommunity},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{check_community_ban, check_community_deleted_or_removed, local_user_view_from_jwt},
+  utils::{check_community_ban, check_community_deleted_or_removed},
 };
 use lemmy_db_schema::{
   source::{
@@ -13,6 +13,7 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Followable},
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
@@ -20,9 +21,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn follow_community(
   data: Json<FollowCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommunityResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let community = Community::read(&mut context.pool(), data.community_id).await?;
   let mut community_follower_form = CommunityFollowerForm {
     community_id: community.id,
diff --git a/crates/api/src/community/hide.rs b/crates/api/src/community/hide.rs
index 8fd27018128b27567a75cff04e749735039e69e7..5f2b9d6fbd2a22cee90338f16767cd77038a7ab1 100644
--- a/crates/api/src/community/hide.rs
+++ b/crates/api/src/community/hide.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   community::{CommunityResponse, HideCommunity},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{is_admin, local_user_view_from_jwt, sanitize_html_api_opt},
+  utils::{is_admin, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   source::{
@@ -14,15 +14,16 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn hide_community(
   data: Json<HideCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommunityResponse>, LemmyError> {
   // Verify its a admin (only admin can hide or unhide it)
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   is_admin(&local_user_view)?;
 
   let community_form = CommunityUpdateForm {
diff --git a/crates/api/src/community/transfer.rs b/crates/api/src/community/transfer.rs
index 1dc5a96e0d8ee77cd1df5921aeb29327a4da5ab0..fd2e293f82471f048fc7f17c81f16f6fa297ac81 100644
--- a/crates/api/src/community/transfer.rs
+++ b/crates/api/src/community/transfer.rs
@@ -3,7 +3,7 @@ use anyhow::Context;
 use lemmy_api_common::{
   community::{GetCommunityResponse, TransferCommunity},
   context::LemmyContext,
-  utils::{is_admin, is_top_mod, local_user_view_from_jwt},
+  utils::{is_admin, is_top_mod},
 };
 use lemmy_db_schema::{
   source::{
@@ -12,6 +12,7 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Joinable},
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
@@ -24,9 +25,8 @@ use lemmy_utils::{
 pub async fn transfer_community(
   data: Json<TransferCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<GetCommunityResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Fetch the community mods
   let community_id = data.community_id;
   let mut community_mods =
diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs
index 22a02197041ae320272cb03a2af19c0054beca5b..04df09308a4e256666ce0300d50f55837ca338c4 100644
--- a/crates/api/src/lib.rs
+++ b/crates/api/src/lib.rs
@@ -126,68 +126,6 @@ mod tests {
   #![allow(clippy::indexing_slicing)]
 
   use super::*;
-  use lemmy_api_common::utils::check_validator_time;
-  use lemmy_db_schema::{
-    source::{
-      instance::Instance,
-      local_user::{LocalUser, LocalUserInsertForm},
-      person::{Person, PersonInsertForm},
-      secret::Secret,
-    },
-    traits::Crud,
-    utils::build_db_pool_for_tests,
-  };
-  use lemmy_utils::{claims::Claims, settings::SETTINGS};
-  use serial_test::serial;
-
-  #[tokio::test]
-  #[serial]
-  async fn test_should_not_validate_user_token_after_password_change() {
-    let pool = &build_db_pool_for_tests().await;
-    let pool = &mut pool.into();
-    let secret = Secret::init(pool).await.unwrap();
-    let settings = &SETTINGS.to_owned();
-
-    let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
-      .await
-      .unwrap();
-
-    let new_person = PersonInsertForm::builder()
-      .name("Gerry9812".into())
-      .public_key("pubkey".to_string())
-      .instance_id(inserted_instance.id)
-      .build();
-
-    let inserted_person = Person::create(pool, &new_person).await.unwrap();
-
-    let local_user_form = LocalUserInsertForm::builder()
-      .person_id(inserted_person.id)
-      .password_encrypted("123456".to_string())
-      .build();
-
-    let inserted_local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
-
-    let jwt = Claims::jwt(
-      inserted_local_user.id.0,
-      &secret.jwt_secret,
-      &settings.hostname,
-    )
-    .unwrap();
-    let claims = Claims::decode(&jwt, &secret.jwt_secret).unwrap().claims;
-    let check = check_validator_time(&inserted_local_user.validator_time, &claims);
-    assert!(check.is_ok());
-
-    // The check should fail, since the validator time is now newer than the jwt issue time
-    let updated_local_user =
-      LocalUser::update_password(pool, inserted_local_user.id, "password111")
-        .await
-        .unwrap();
-    let check_after = check_validator_time(&updated_local_user.validator_time, &claims);
-    assert!(check_after.is_err());
-
-    let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
-    assert_eq!(1, num_deleted);
-  }
 
   #[test]
   fn test_build_totp() {
diff --git a/crates/api/src/local_user/add_admin.rs b/crates/api/src/local_user/add_admin.rs
index 8e50147a472f5fb06e5142d774dae054c9515969..50233587606d26a468ff5a15d459305d2e7c8f8a 100644
--- a/crates/api/src/local_user/add_admin.rs
+++ b/crates/api/src/local_user/add_admin.rs
@@ -2,7 +2,7 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   person::{AddAdmin, AddAdminResponse},
-  utils::{is_admin, local_user_view_from_jwt},
+  utils::is_admin,
 };
 use lemmy_db_schema::{
   source::{
@@ -19,9 +19,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn add_admin(
   data: Json<AddAdmin>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<AddAdminResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Make sure user is an admin
   is_admin(&local_user_view)?;
 
diff --git a/crates/api/src/local_user/ban_person.rs b/crates/api/src/local_user/ban_person.rs
index 3a83d6c6e386e5b34b4f3992cc5bb69f74791913..ecc09220db61c404cf335a9ef69e20381f89ad64 100644
--- a/crates/api/src/local_user/ban_person.rs
+++ b/crates/api/src/local_user/ban_person.rs
@@ -4,7 +4,7 @@ use lemmy_api_common::{
   context::LemmyContext,
   person::{BanPerson, BanPersonResponse},
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{is_admin, local_user_view_from_jwt, remove_user_data, sanitize_html_api_opt},
+  utils::{is_admin, remove_user_data, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   source::{
@@ -13,6 +13,7 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::PersonView;
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
@@ -22,9 +23,8 @@ use lemmy_utils::{
 pub async fn ban_from_site(
   data: Json<BanPerson>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<BanPersonResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Make sure user is an admin
   is_admin(&local_user_view)?;
 
diff --git a/crates/api/src/local_user/block.rs b/crates/api/src/local_user/block.rs
index 77a23aef3fabfa499bd33229731784bfa6aa4f10..cb345616bb59b2c15af0ccb4705b1cbf6c9f5d65 100644
--- a/crates/api/src/local_user/block.rs
+++ b/crates/api/src/local_user/block.rs
@@ -2,7 +2,6 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   person::{BlockPerson, BlockPersonResponse},
-  utils::local_user_view_from_jwt,
 };
 use lemmy_db_schema::{
   source::person_block::{PersonBlock, PersonBlockForm},
@@ -16,9 +15,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn block_person(
   data: Json<BlockPerson>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<BlockPersonResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let target_id = data.person_id;
   let person_id = local_user_view.person.id;
 
diff --git a/crates/api/src/local_user/change_password.rs b/crates/api/src/local_user/change_password.rs
index 532c8ed7cb0fad617f8ca7903a72423fb4e46be6..9b491d82162af4a6c87f5a181856f60f67133dfb 100644
--- a/crates/api/src/local_user/change_password.rs
+++ b/crates/api/src/local_user/change_password.rs
@@ -3,9 +3,10 @@ use bcrypt::verify;
 use lemmy_api_common::{
   context::LemmyContext,
   person::{ChangePassword, LoginResponse},
-  utils::{local_user_view_from_jwt, password_length_check},
+  utils::password_length_check,
 };
 use lemmy_db_schema::source::local_user::LocalUser;
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::{
   claims::Claims,
   error::{LemmyError, LemmyErrorType},
@@ -15,9 +16,8 @@ use lemmy_utils::{
 pub async fn change_password(
   data: Json<ChangePassword>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<LoginResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), &context).await?;
-
   password_length_check(&data.new_password)?;
 
   // Make sure passwords match
diff --git a/crates/api/src/local_user/list_banned.rs b/crates/api/src/local_user/list_banned.rs
index c4c0a98ae5bab9c383163641216119d6ab53c6bb..5c76d89a86e1ee8355c1d0460c95676cbb5435e7 100644
--- a/crates/api/src/local_user/list_banned.rs
+++ b/crates/api/src/local_user/list_banned.rs
@@ -1,18 +1,13 @@
-use actix_web::web::{Data, Json, Query};
-use lemmy_api_common::{
-  context::LemmyContext,
-  person::{BannedPersonsResponse, GetBannedPersons},
-  utils::{is_admin, local_user_view_from_jwt},
-};
+use actix_web::web::{Data, Json};
+use lemmy_api_common::{context::LemmyContext, person::BannedPersonsResponse, utils::is_admin};
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::PersonView;
 use lemmy_utils::error::LemmyError;
 
 pub async fn list_banned_users(
-  data: Query<GetBannedPersons>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<BannedPersonsResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Make sure user is an admin
   is_admin(&local_user_view)?;
 
diff --git a/crates/api/src/local_user/login.rs b/crates/api/src/local_user/login.rs
index 5dd3e29d8f8c729ab4aa4fb580e73b98b45f28d3..11a5c6b0197ffbcad4ec91519d6825eb2a81eb29 100644
--- a/crates/api/src/local_user/login.rs
+++ b/crates/api/src/local_user/login.rs
@@ -4,7 +4,13 @@ use bcrypt::verify;
 use lemmy_api_common::{
   context::LemmyContext,
   person::{Login, LoginResponse},
-  utils::{check_registration_application, check_user_valid},
+  utils,
+  utils::check_user_valid,
+};
+use lemmy_db_schema::{
+  source::{local_site::LocalSite, registration_application::RegistrationApplication},
+  utils::DbPool,
+  RegistrationMode,
 };
 use lemmy_db_views::structs::{LocalUserView, SiteView};
 use lemmy_utils::{
@@ -72,3 +78,29 @@ pub async fn login(
     registration_created: false,
   }))
 }
+
+async fn check_registration_application(
+  local_user_view: &LocalUserView,
+  local_site: &LocalSite,
+  pool: &mut DbPool<'_>,
+) -> Result<(), LemmyError> {
+  if (local_site.registration_mode == RegistrationMode::RequireApplication
+    || local_site.registration_mode == RegistrationMode::Closed)
+    && !local_user_view.local_user.accepted_application
+    && !local_user_view.local_user.admin
+  {
+    // Fetch the registration, see if its denied
+    let local_user_id = local_user_view.local_user.id;
+    let registration = RegistrationApplication::find_by_local_user_id(pool, local_user_id).await?;
+    if let Some(deny_reason) = registration.deny_reason {
+      let lang = utils::get_interface_language(local_user_view);
+      let registration_denied_message = format!("{}: {}", lang.registration_denied(), deny_reason);
+      Err(LemmyErrorType::RegistrationDenied(
+        registration_denied_message,
+      ))?
+    } else {
+      Err(LemmyErrorType::RegistrationApplicationIsPending)?
+    }
+  }
+  Ok(())
+}
diff --git a/crates/api/src/local_user/notifications/list_mentions.rs b/crates/api/src/local_user/notifications/list_mentions.rs
index af32e31a829ed5a2a687849c8c253e198b266837..9f9ee3ae8f77de5d453ee21bab92628d52717701 100644
--- a/crates/api/src/local_user/notifications/list_mentions.rs
+++ b/crates/api/src/local_user/notifications/list_mentions.rs
@@ -2,8 +2,8 @@ use actix_web::web::{Data, Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   person::{GetPersonMentions, GetPersonMentionsResponse},
-  utils::local_user_view_from_jwt,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::person_mention_view::PersonMentionQuery;
 use lemmy_utils::error::LemmyError;
 
@@ -11,9 +11,8 @@ use lemmy_utils::error::LemmyError;
 pub async fn list_mentions(
   data: Query<GetPersonMentions>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<GetPersonMentionsResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let sort = data.sort;
   let page = data.page;
   let limit = data.limit;
diff --git a/crates/api/src/local_user/notifications/list_replies.rs b/crates/api/src/local_user/notifications/list_replies.rs
index 8a7658d724d24032ecaebe5c0d3e34bb9366f395..555989721cc526d568ae81b5044feb2a89883cf4 100644
--- a/crates/api/src/local_user/notifications/list_replies.rs
+++ b/crates/api/src/local_user/notifications/list_replies.rs
@@ -2,8 +2,8 @@ use actix_web::web::{Data, Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   person::{GetReplies, GetRepliesResponse},
-  utils::local_user_view_from_jwt,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::comment_reply_view::CommentReplyQuery;
 use lemmy_utils::error::LemmyError;
 
@@ -11,9 +11,8 @@ use lemmy_utils::error::LemmyError;
 pub async fn list_replies(
   data: Query<GetReplies>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<GetRepliesResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let sort = data.sort;
   let page = data.page;
   let limit = data.limit;
diff --git a/crates/api/src/local_user/notifications/mark_all_read.rs b/crates/api/src/local_user/notifications/mark_all_read.rs
index a38b23c53f9973d975175ccaefa36a5d614fbcfe..d3667460b8259dee1bfd08fc364c6713456fa5dd 100644
--- a/crates/api/src/local_user/notifications/mark_all_read.rs
+++ b/crates/api/src/local_user/notifications/mark_all_read.rs
@@ -1,22 +1,18 @@
 use actix_web::web::{Data, Json};
-use lemmy_api_common::{
-  context::LemmyContext,
-  person::{GetRepliesResponse, MarkAllAsRead},
-  utils::local_user_view_from_jwt,
-};
+use lemmy_api_common::{context::LemmyContext, person::GetRepliesResponse};
 use lemmy_db_schema::source::{
   comment_reply::CommentReply,
   person_mention::PersonMention,
   private_message::PrivateMessage,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn mark_all_notifications_read(
-  data: Json<MarkAllAsRead>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<GetRepliesResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let person_id = local_user_view.person.id;
 
   // Mark all comment_replies as read
diff --git a/crates/api/src/local_user/notifications/mark_mention_read.rs b/crates/api/src/local_user/notifications/mark_mention_read.rs
index f4529015d6776a9f0a04653afce8ce22db410c32..4cce598acffc43211051a42543988bb778300ac1 100644
--- a/crates/api/src/local_user/notifications/mark_mention_read.rs
+++ b/crates/api/src/local_user/notifications/mark_mention_read.rs
@@ -2,12 +2,12 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   person::{MarkPersonMentionAsRead, PersonMentionResponse},
-  utils::local_user_view_from_jwt,
 };
 use lemmy_db_schema::{
   source::person_mention::{PersonMention, PersonMentionUpdateForm},
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::PersonMentionView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
@@ -15,9 +15,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn mark_person_mention_as_read(
   data: Json<MarkPersonMentionAsRead>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PersonMentionResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let person_mention_id = data.person_mention_id;
   let read_person_mention = PersonMention::read(&mut context.pool(), person_mention_id).await?;
 
diff --git a/crates/api/src/local_user/notifications/mark_reply_read.rs b/crates/api/src/local_user/notifications/mark_reply_read.rs
index 4439a6cfbb887822d16468c5b56e4e93d9bcdee5..f7b259c94b1a914b9dc7c31e0e6bb2aed38f3abd 100644
--- a/crates/api/src/local_user/notifications/mark_reply_read.rs
+++ b/crates/api/src/local_user/notifications/mark_reply_read.rs
@@ -2,12 +2,12 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   person::{CommentReplyResponse, MarkCommentReplyAsRead},
-  utils::local_user_view_from_jwt,
 };
 use lemmy_db_schema::{
   source::comment_reply::{CommentReply, CommentReplyUpdateForm},
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::CommentReplyView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
@@ -15,9 +15,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn mark_reply_as_read(
   data: Json<MarkCommentReplyAsRead>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommentReplyResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let comment_reply_id = data.comment_reply_id;
   let read_comment_reply = CommentReply::read(&mut context.pool(), comment_reply_id).await?;
 
diff --git a/crates/api/src/local_user/notifications/unread_count.rs b/crates/api/src/local_user/notifications/unread_count.rs
index 0315609ddd1df31138cdb01e7d924e886c3796a5..c0b1f0f2ef9e8cf6fe19755e32947a8faca5e89b 100644
--- a/crates/api/src/local_user/notifications/unread_count.rs
+++ b/crates/api/src/local_user/notifications/unread_count.rs
@@ -1,20 +1,14 @@
-use actix_web::web::{Data, Json, Query};
-use lemmy_api_common::{
-  context::LemmyContext,
-  person::{GetUnreadCount, GetUnreadCountResponse},
-  utils::local_user_view_from_jwt,
-};
-use lemmy_db_views::structs::PrivateMessageView;
+use actix_web::web::{Data, Json};
+use lemmy_api_common::{context::LemmyContext, person::GetUnreadCountResponse};
+use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
 use lemmy_db_views_actor::structs::{CommentReplyView, PersonMentionView};
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn unread_count(
-  data: Query<GetUnreadCount>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<GetUnreadCountResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let person_id = local_user_view.person.id;
 
   let replies = CommentReplyView::get_unread_replies(&mut context.pool(), person_id).await?;
diff --git a/crates/api/src/local_user/report_count.rs b/crates/api/src/local_user/report_count.rs
index 08be9c9482da5b441edfc2427d1bff6846b71ef2..3bfa1559b3dcaacb233aa184a92a2a1453989eef 100644
--- a/crates/api/src/local_user/report_count.rs
+++ b/crates/api/src/local_user/report_count.rs
@@ -2,18 +2,21 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   person::{GetReportCount, GetReportCountResponse},
-  utils::local_user_view_from_jwt,
 };
-use lemmy_db_views::structs::{CommentReportView, PostReportView, PrivateMessageReportView};
+use lemmy_db_views::structs::{
+  CommentReportView,
+  LocalUserView,
+  PostReportView,
+  PrivateMessageReportView,
+};
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn report_count(
   data: Json<GetReportCount>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<GetReportCountResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let person_id = local_user_view.person.id;
   let admin = local_user_view.local_user.admin;
   let community_id = data.community_id;
diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs
index 045a3c2f7bbd1a5b07262f50755dc11e79d580e0..7d3b675f6c7bfd92277ae548dd8efe4443533be5 100644
--- a/crates/api/src/local_user/save_settings.rs
+++ b/crates/api/src/local_user/save_settings.rs
@@ -2,7 +2,7 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   person::{LoginResponse, SaveUserSettings},
-  utils::{local_user_view_from_jwt, sanitize_html_api_opt, send_verification_email},
+  utils::{sanitize_html_api_opt, send_verification_email},
 };
 use lemmy_db_schema::{
   source::{
@@ -13,7 +13,7 @@ use lemmy_db_schema::{
   traits::Crud,
   utils::{diesel_option_overwrite, diesel_option_overwrite_to_url},
 };
-use lemmy_db_views::structs::SiteView;
+use lemmy_db_views::structs::{LocalUserView, SiteView};
 use lemmy_utils::{
   claims::Claims,
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
@@ -24,8 +24,8 @@ use lemmy_utils::{
 pub async fn save_user_settings(
   data: Json<SaveUserSettings>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<LoginResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let site_view = SiteView::read_local(&mut context.pool()).await?;
 
   let bio = sanitize_html_api_opt(&data.bio);
diff --git a/crates/api/src/post/feature.rs b/crates/api/src/post/feature.rs
index 02b9551a9493a25bcc9c0ffb23fa07eb8a040be5..8c2d2fcedfc49a3dccb8b0f15a6f475211554b81 100644
--- a/crates/api/src/post/feature.rs
+++ b/crates/api/src/post/feature.rs
@@ -5,13 +5,7 @@ use lemmy_api_common::{
   context::LemmyContext,
   post::{FeaturePost, PostResponse},
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{
-    check_community_ban,
-    check_community_deleted_or_removed,
-    is_admin,
-    is_mod_or_admin,
-    local_user_view_from_jwt,
-  },
+  utils::{check_community_ban, check_community_deleted_or_removed, is_admin, is_mod_or_admin},
 };
 use lemmy_db_schema::{
   source::{
@@ -21,15 +15,15 @@ use lemmy_db_schema::{
   traits::Crud,
   PostFeatureType,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn feature_post(
   data: Json<FeaturePost>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let post_id = data.post_id;
   let orig_post = Post::read(&mut context.pool(), post_id).await?;
 
diff --git a/crates/api/src/post/like.rs b/crates/api/src/post/like.rs
index b9b2bede08408351d9f7ae16d3c25e49168e5304..d4f9d644d5b49006a5e4d3b67db090d16429c446 100644
--- a/crates/api/src/post/like.rs
+++ b/crates/api/src/post/like.rs
@@ -9,7 +9,6 @@ use lemmy_api_common::{
     check_community_ban,
     check_community_deleted_or_removed,
     check_downvotes_enabled,
-    local_user_view_from_jwt,
     mark_post_as_read,
   },
 };
@@ -21,6 +20,7 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Likeable},
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 use std::ops::Deref;
 
@@ -28,8 +28,8 @@ use std::ops::Deref;
 pub async fn like_post(
   data: Json<CreatePostLike>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   // Don't do a downvote if site has downvotes disabled
diff --git a/crates/api/src/post/lock.rs b/crates/api/src/post/lock.rs
index b2ba9b3d12b2be5ba7ebc8b1876bb772676440a3..ecd2061566a9b6ede035dc7d5f353bfb28fdd5da 100644
--- a/crates/api/src/post/lock.rs
+++ b/crates/api/src/post/lock.rs
@@ -5,12 +5,7 @@ use lemmy_api_common::{
   context::LemmyContext,
   post::{LockPost, PostResponse},
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{
-    check_community_ban,
-    check_community_deleted_or_removed,
-    is_mod_or_admin,
-    local_user_view_from_jwt,
-  },
+  utils::{check_community_ban, check_community_deleted_or_removed, is_mod_or_admin},
 };
 use lemmy_db_schema::{
   source::{
@@ -19,15 +14,15 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn lock_post(
   data: Json<LockPost>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let post_id = data.post_id;
   let orig_post = Post::read(&mut context.pool(), post_id).await?;
 
diff --git a/crates/api/src/post/mark_read.rs b/crates/api/src/post/mark_read.rs
index ecb1b3448902c6668e89c0cef87bf505109b0bbe..a248b0196073f57c588c8a19a44defdee6fe7ad5 100644
--- a/crates/api/src/post/mark_read.rs
+++ b/crates/api/src/post/mark_read.rs
@@ -3,18 +3,16 @@ use lemmy_api_common::{
   context::LemmyContext,
   post::{MarkPostAsRead, PostResponse},
   utils,
-  utils::local_user_view_from_jwt,
 };
-use lemmy_db_views::structs::PostView;
+use lemmy_db_views::structs::{LocalUserView, PostView};
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn mark_post_as_read(
   data: Json<MarkPostAsRead>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let post_id = data.post_id;
   let person_id = local_user_view.person.id;
 
diff --git a/crates/api/src/post/save.rs b/crates/api/src/post/save.rs
index ddad6ede4aa0aaf4cead5debfeee1bc3e770f9b5..1648407704d20f276fee929f0b5095fd56900315 100644
--- a/crates/api/src/post/save.rs
+++ b/crates/api/src/post/save.rs
@@ -2,22 +2,21 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   post::{PostResponse, SavePost},
-  utils::{local_user_view_from_jwt, mark_post_as_read},
+  utils::mark_post_as_read,
 };
 use lemmy_db_schema::{
   source::post::{PostSaved, PostSavedForm},
   traits::Saveable,
 };
-use lemmy_db_views::structs::PostView;
+use lemmy_db_views::structs::{LocalUserView, PostView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn save_post(
   data: Json<SavePost>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let post_saved_form = PostSavedForm {
     post_id: data.post_id,
     person_id: local_user_view.person.id,
diff --git a/crates/api/src/post_report/create.rs b/crates/api/src/post_report/create.rs
index 8a5162a609a75b975f1c3ed4d9aa774cfbf61855..6a32d505b3862e0bcdf72603f6fa626561ce7f19 100644
--- a/crates/api/src/post_report/create.rs
+++ b/crates/api/src/post_report/create.rs
@@ -5,12 +5,7 @@ use lemmy_api_common::{
   context::LemmyContext,
   post::{CreatePostReport, PostReportResponse},
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{
-    check_community_ban,
-    local_user_view_from_jwt,
-    sanitize_html_api,
-    send_new_report_email_to_admins,
-  },
+  utils::{check_community_ban, sanitize_html_api, send_new_report_email_to_admins},
 };
 use lemmy_db_schema::{
   source::{
@@ -19,7 +14,7 @@ use lemmy_db_schema::{
   },
   traits::Reportable,
 };
-use lemmy_db_views::structs::{PostReportView, PostView};
+use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 /// Creates a post report and notifies the moderators of the community
@@ -27,8 +22,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn create_post_report(
   data: Json<CreatePostReport>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostReportResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   let reason = sanitize_html_api(data.reason.trim());
diff --git a/crates/api/src/post_report/list.rs b/crates/api/src/post_report/list.rs
index e6509fe00602fa92a65fc116d1361d287a1d2966..5cf2889ea1985f63082019e74b5c601bc71484c4 100644
--- a/crates/api/src/post_report/list.rs
+++ b/crates/api/src/post_report/list.rs
@@ -2,9 +2,8 @@ use actix_web::web::{Data, Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   post::{ListPostReports, ListPostReportsResponse},
-  utils::local_user_view_from_jwt,
 };
-use lemmy_db_views::post_report_view::PostReportQuery;
+use lemmy_db_views::{post_report_view::PostReportQuery, structs::LocalUserView};
 use lemmy_utils::error::LemmyError;
 
 /// Lists post reports for a community if an id is supplied
@@ -13,9 +12,8 @@ use lemmy_utils::error::LemmyError;
 pub async fn list_post_reports(
   data: Query<ListPostReports>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<ListPostReportsResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let community_id = data.community_id;
   let unresolved_only = data.unresolved_only.unwrap_or_default();
 
diff --git a/crates/api/src/post_report/resolve.rs b/crates/api/src/post_report/resolve.rs
index 94e841e086dfe1cdd1b788162753d16e8b1c5c74..fe01c748cdd015186edd01d9359d35aa25ad316f 100644
--- a/crates/api/src/post_report/resolve.rs
+++ b/crates/api/src/post_report/resolve.rs
@@ -2,10 +2,10 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   post::{PostReportResponse, ResolvePostReport},
-  utils::{is_mod_or_admin, local_user_view_from_jwt},
+  utils::is_mod_or_admin,
 };
 use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
-use lemmy_db_views::structs::PostReportView;
+use lemmy_db_views::structs::{LocalUserView, PostReportView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 /// Resolves or unresolves a post report and notifies the moderators of the community
@@ -13,9 +13,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn resolve_post_report(
   data: Json<ResolvePostReport>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostReportResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let report_id = data.report_id;
   let person_id = local_user_view.person.id;
   let report = PostReportView::read(&mut context.pool(), report_id, person_id).await?;
diff --git a/crates/api/src/private_message/mark_read.rs b/crates/api/src/private_message/mark_read.rs
index 6b6f7a51d91c958f421f2f7012a4cae1a93f5335..6b089c0abfa0b3fede4c1550569a46620ab1f590 100644
--- a/crates/api/src/private_message/mark_read.rs
+++ b/crates/api/src/private_message/mark_read.rs
@@ -2,22 +2,20 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   private_message::{MarkPrivateMessageAsRead, PrivateMessageResponse},
-  utils::local_user_view_from_jwt,
 };
 use lemmy_db_schema::{
   source::private_message::{PrivateMessage, PrivateMessageUpdateForm},
   traits::Crud,
 };
-use lemmy_db_views::structs::PrivateMessageView;
+use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn mark_pm_as_read(
   data: Json<MarkPrivateMessageAsRead>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PrivateMessageResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Checking permissions
   let private_message_id = data.private_message_id;
   let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?;
diff --git a/crates/api/src/private_message_report/create.rs b/crates/api/src/private_message_report/create.rs
index ed19fdc35bbe8835905e1ad88be0c1de5e64627b..2c42915816c5e08e16c06c401544566eacabed5e 100644
--- a/crates/api/src/private_message_report/create.rs
+++ b/crates/api/src/private_message_report/create.rs
@@ -3,7 +3,7 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   private_message::{CreatePrivateMessageReport, PrivateMessageReportResponse},
-  utils::{local_user_view_from_jwt, sanitize_html_api, send_new_report_email_to_admins},
+  utils::{sanitize_html_api, send_new_report_email_to_admins},
 };
 use lemmy_db_schema::{
   source::{
@@ -13,15 +13,15 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Reportable},
 };
-use lemmy_db_views::structs::PrivateMessageReportView;
+use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn create_pm_report(
   data: Json<CreatePrivateMessageReport>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PrivateMessageReportResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   let reason = sanitize_html_api(data.reason.trim());
diff --git a/crates/api/src/private_message_report/list.rs b/crates/api/src/private_message_report/list.rs
index 21e0cab21804bf574d2eba56fcb1f01810be6338..2dc3e6efcc87310a468be19dc9a7769810c1d713 100644
--- a/crates/api/src/private_message_report/list.rs
+++ b/crates/api/src/private_message_report/list.rs
@@ -2,18 +2,20 @@ use actix_web::web::{Data, Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   private_message::{ListPrivateMessageReports, ListPrivateMessageReportsResponse},
-  utils::{is_admin, local_user_view_from_jwt},
+  utils::is_admin,
+};
+use lemmy_db_views::{
+  private_message_report_view::PrivateMessageReportQuery,
+  structs::LocalUserView,
 };
-use lemmy_db_views::private_message_report_view::PrivateMessageReportQuery;
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn list_pm_reports(
   data: Query<ListPrivateMessageReports>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<ListPrivateMessageReportsResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   is_admin(&local_user_view)?;
 
   let unresolved_only = data.unresolved_only.unwrap_or_default();
diff --git a/crates/api/src/private_message_report/resolve.rs b/crates/api/src/private_message_report/resolve.rs
index 7f3f0647ac6eafa4deca816bf58038a2a9d89493..202fdcd29d0d2ba05d87d2a9979aa1ceb4263ef1 100644
--- a/crates/api/src/private_message_report/resolve.rs
+++ b/crates/api/src/private_message_report/resolve.rs
@@ -2,19 +2,18 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   private_message::{PrivateMessageReportResponse, ResolvePrivateMessageReport},
-  utils::{is_admin, local_user_view_from_jwt},
+  utils::is_admin,
 };
 use lemmy_db_schema::{source::private_message_report::PrivateMessageReport, traits::Reportable};
-use lemmy_db_views::structs::PrivateMessageReportView;
+use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn resolve_pm_report(
   data: Json<ResolvePrivateMessageReport>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PrivateMessageReportResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   is_admin(&local_user_view)?;
 
   let report_id = data.report_id;
diff --git a/crates/api/src/site/block.rs b/crates/api/src/site/block.rs
index 3278767cfebc67cc89f823322e04cb8a0b01aa91..be48e8ce85c57419209d7078b3bb4708d79251f7 100644
--- a/crates/api/src/site/block.rs
+++ b/crates/api/src/site/block.rs
@@ -3,21 +3,20 @@ use actix_web::web::Json;
 use lemmy_api_common::{
   context::LemmyContext,
   site::{BlockInstance, BlockInstanceResponse},
-  utils::local_user_view_from_jwt,
 };
 use lemmy_db_schema::{
   source::instance_block::{InstanceBlock, InstanceBlockForm},
   traits::Blockable,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn block_instance(
   data: Json<BlockInstance>,
+  local_user_view: LocalUserView,
   context: Data<LemmyContext>,
 ) -> Result<Json<BlockInstanceResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let instance_id = data.instance_id;
   let person_id = local_user_view.person.id;
   let instance_block_form = InstanceBlockForm {
diff --git a/crates/api/src/site/leave_admin.rs b/crates/api/src/site/leave_admin.rs
index 24b7184ae205031bb21b9b751d15d7e55f630fc1..f25747ef312bc869c71ac35c8bc4718acb05b457 100644
--- a/crates/api/src/site/leave_admin.rs
+++ b/crates/api/src/site/leave_admin.rs
@@ -1,9 +1,5 @@
 use actix_web::web::{Data, Json};
-use lemmy_api_common::{
-  context::LemmyContext,
-  site::{GetSiteResponse, LeaveAdmin},
-  utils::{is_admin, local_user_view_from_jwt},
-};
+use lemmy_api_common::{context::LemmyContext, site::GetSiteResponse, utils::is_admin};
 use lemmy_db_schema::{
   source::{
     actor_language::SiteLanguage,
@@ -14,7 +10,7 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
-use lemmy_db_views::structs::{CustomEmojiView, SiteView};
+use lemmy_db_views::structs::{CustomEmojiView, LocalUserView, SiteView};
 use lemmy_db_views_actor::structs::PersonView;
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorType},
@@ -23,11 +19,9 @@ use lemmy_utils::{
 
 #[tracing::instrument(skip(context))]
 pub async fn leave_admin(
-  data: Json<LeaveAdmin>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<GetSiteResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   is_admin(&local_user_view)?;
 
   // Make sure there isn't just one admin (so if one leaves, there will still be one left)
diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs
index 6743469b7717426131535c001a659ab7697dabf8..9c66c72a1b8ed75857ba0d481bf92d2dd6d41db0 100644
--- a/crates/api/src/site/mod_log.rs
+++ b/crates/api/src/site/mod_log.rs
@@ -2,13 +2,14 @@ use actix_web::web::{Data, Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   site::{GetModlog, GetModlogResponse},
-  utils::{check_private_instance, is_admin, is_mod_or_admin, local_user_view_from_jwt_opt},
+  utils::{check_private_instance, is_admin, is_mod_or_admin},
 };
 use lemmy_db_schema::{
   newtypes::{CommunityId, PersonId},
   source::local_site::LocalSite,
   ModlogActionType,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_moderator::structs::{
   AdminPurgeCommentView,
   AdminPurgeCommunityView,
@@ -34,8 +35,8 @@ use ModlogActionType::*;
 pub async fn get_mod_log(
   data: Query<GetModlog>,
   context: Data<LemmyContext>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<GetModlogResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   check_private_instance(&local_user_view, &local_site)?;
diff --git a/crates/api/src/site/purge/comment.rs b/crates/api/src/site/purge/comment.rs
index fa0973cf102114325f4222253796ccce1718ec9c..ec4ef02af4d129d57c430a06b2bb08346aecda19 100644
--- a/crates/api/src/site/purge/comment.rs
+++ b/crates/api/src/site/purge/comment.rs
@@ -2,7 +2,7 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   site::{PurgeComment, PurgeItemResponse},
-  utils::{is_admin, local_user_view_from_jwt, sanitize_html_api_opt},
+  utils::{is_admin, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   source::{
@@ -11,15 +11,15 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn purge_comment(
   data: Json<PurgeComment>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PurgeItemResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Only let admin purge an item
   is_admin(&local_user_view)?;
 
diff --git a/crates/api/src/site/purge/community.rs b/crates/api/src/site/purge/community.rs
index 8e609501a52cc53a56f0b08271c4c92a418d24c9..8e731fa29debbcc2d9a300fbfced2d4647d823c9 100644
--- a/crates/api/src/site/purge/community.rs
+++ b/crates/api/src/site/purge/community.rs
@@ -3,12 +3,7 @@ use lemmy_api_common::{
   context::LemmyContext,
   request::purge_image_from_pictrs,
   site::{PurgeCommunity, PurgeItemResponse},
-  utils::{
-    is_admin,
-    local_user_view_from_jwt,
-    purge_image_posts_for_community,
-    sanitize_html_api_opt,
-  },
+  utils::{is_admin, purge_image_posts_for_community, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   source::{
@@ -17,15 +12,15 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn purge_community(
   data: Json<PurgeCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PurgeItemResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Only let admin purge an item
   is_admin(&local_user_view)?;
 
diff --git a/crates/api/src/site/purge/person.rs b/crates/api/src/site/purge/person.rs
index b2968ec3147b46a9f439e7917bdccad0c8a7c173..1e68db88f7e3699f3ee02e509d2e30717658fa7b 100644
--- a/crates/api/src/site/purge/person.rs
+++ b/crates/api/src/site/purge/person.rs
@@ -3,7 +3,7 @@ use lemmy_api_common::{
   context::LemmyContext,
   request::delete_image_from_pictrs,
   site::{PurgeItemResponse, PurgePerson},
-  utils::{is_admin, local_user_view_from_jwt, sanitize_html_api_opt},
+  utils::{is_admin, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   source::{
@@ -20,9 +20,8 @@ use lemmy_utils::error::LemmyError;
 pub async fn purge_person(
   data: Json<PurgePerson>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PurgeItemResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Only let admin purge an item
   is_admin(&local_user_view)?;
 
diff --git a/crates/api/src/site/purge/post.rs b/crates/api/src/site/purge/post.rs
index 53dbf44e3a004cba07d2c302c18a0c02f1f9763f..5e1ff4ea5891b2102a022e49c00f65796b810959 100644
--- a/crates/api/src/site/purge/post.rs
+++ b/crates/api/src/site/purge/post.rs
@@ -3,7 +3,7 @@ use lemmy_api_common::{
   context::LemmyContext,
   request::purge_image_from_pictrs,
   site::{PurgeItemResponse, PurgePost},
-  utils::{is_admin, local_user_view_from_jwt, sanitize_html_api_opt},
+  utils::{is_admin, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   source::{
@@ -12,15 +12,15 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn purge_post(
   data: Json<PurgePost>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PurgeItemResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Only let admin purge an item
   is_admin(&local_user_view)?;
 
diff --git a/crates/api/src/site/registration_applications/approve.rs b/crates/api/src/site/registration_applications/approve.rs
index 344fa82c8b53642b4d2732735d17d3fd49ef1dbd..036a60e00edceba5272b01c761ca5235f8894d2d 100644
--- a/crates/api/src/site/registration_applications/approve.rs
+++ b/crates/api/src/site/registration_applications/approve.rs
@@ -2,7 +2,7 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   site::{ApproveRegistrationApplication, RegistrationApplicationResponse},
-  utils::{is_admin, local_user_view_from_jwt, send_application_approved_email},
+  utils::{is_admin, send_application_approved_email},
 };
 use lemmy_db_schema::{
   source::{
@@ -18,9 +18,8 @@ use lemmy_utils::error::LemmyError;
 pub async fn approve_registration_application(
   data: Json<ApproveRegistrationApplication>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<RegistrationApplicationResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let app_id = data.id;
 
   // Only let admins do this
diff --git a/crates/api/src/site/registration_applications/list.rs b/crates/api/src/site/registration_applications/list.rs
index 0690f50fb73d2fec58be78ef767f95b57d804a82..30ce9aaf20f2db6357a81fd2dc36cf871eb198ca 100644
--- a/crates/api/src/site/registration_applications/list.rs
+++ b/crates/api/src/site/registration_applications/list.rs
@@ -2,18 +2,21 @@ use actix_web::web::{Data, Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   site::{ListRegistrationApplications, ListRegistrationApplicationsResponse},
-  utils::{is_admin, local_user_view_from_jwt},
+  utils::is_admin,
 };
 use lemmy_db_schema::source::local_site::LocalSite;
-use lemmy_db_views::registration_application_view::RegistrationApplicationQuery;
+use lemmy_db_views::{
+  registration_application_view::RegistrationApplicationQuery,
+  structs::LocalUserView,
+};
 use lemmy_utils::error::LemmyError;
 
 /// Lists registration applications, filterable by undenied only.
 pub async fn list_registration_applications(
   data: Query<ListRegistrationApplications>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<ListRegistrationApplicationsResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   // Make sure user is an admin
diff --git a/crates/api/src/site/registration_applications/unread_count.rs b/crates/api/src/site/registration_applications/unread_count.rs
index 49929c070d79de69c48aa0c08a8d1b3a2cdac89e..255859198ea7b3c232f7ba43de1751e7c5657076 100644
--- a/crates/api/src/site/registration_applications/unread_count.rs
+++ b/crates/api/src/site/registration_applications/unread_count.rs
@@ -1,18 +1,17 @@
-use actix_web::web::{Data, Json, Query};
+use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
-  site::{GetUnreadRegistrationApplicationCount, GetUnreadRegistrationApplicationCountResponse},
-  utils::{is_admin, local_user_view_from_jwt},
+  site::GetUnreadRegistrationApplicationCountResponse,
+  utils::is_admin,
 };
 use lemmy_db_schema::source::local_site::LocalSite;
-use lemmy_db_views::structs::RegistrationApplicationView;
+use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView};
 use lemmy_utils::error::LemmyError;
 
 pub async fn get_unread_registration_application_count(
-  data: Query<GetUnreadRegistrationApplicationCount>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<GetUnreadRegistrationApplicationCountResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   // Only let admins do this
diff --git a/crates/api_common/src/comment.rs b/crates/api_common/src/comment.rs
index ec9ca13282595442d92d0f7e93a21f554ad560f7..c2589fb2a88eceee44a7a2bc4a0a263e77e11c7b 100644
--- a/crates/api_common/src/comment.rs
+++ b/crates/api_common/src/comment.rs
@@ -1,4 +1,3 @@
-use crate::sensitive::Sensitive;
 use lemmy_db_schema::{
   newtypes::{CommentId, CommentReportId, CommunityId, LanguageId, LocalUserId, PostId},
   CommentSortType,
@@ -20,7 +19,6 @@ pub struct CreateComment {
   pub post_id: PostId,
   pub parent_id: Option<CommentId>,
   pub language_id: Option<LanguageId>,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -30,7 +28,6 @@ pub struct CreateComment {
 /// Fetch an individual comment.
 pub struct GetComment {
   pub id: CommentId,
-  pub auth: Option<Sensitive<String>>,
 }
 
 #[skip_serializing_none]
@@ -42,7 +39,6 @@ pub struct EditComment {
   pub comment_id: CommentId,
   pub content: Option<String>,
   pub language_id: Option<LanguageId>,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -53,7 +49,6 @@ pub struct EditComment {
 pub struct DistinguishComment {
   pub comment_id: CommentId,
   pub distinguished: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -64,7 +59,6 @@ pub struct DistinguishComment {
 pub struct DeleteComment {
   pub comment_id: CommentId,
   pub deleted: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -76,7 +70,6 @@ pub struct RemoveComment {
   pub comment_id: CommentId,
   pub removed: bool,
   pub reason: Option<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -86,7 +79,6 @@ pub struct RemoveComment {
 pub struct SaveComment {
   pub comment_id: CommentId,
   pub save: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -107,7 +99,6 @@ pub struct CreateCommentLike {
   pub comment_id: CommentId,
   /// Must be -1, 0, or 1 .
   pub score: i16,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -128,7 +119,6 @@ pub struct GetComments {
   pub saved_only: Option<bool>,
   pub liked_only: Option<bool>,
   pub disliked_only: Option<bool>,
-  pub auth: Option<Sensitive<String>>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -146,7 +136,6 @@ pub struct GetCommentsResponse {
 pub struct CreateCommentReport {
   pub comment_id: CommentId,
   pub reason: String,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -164,7 +153,6 @@ pub struct CommentReportResponse {
 pub struct ResolveCommentReport {
   pub report_id: CommentReportId,
   pub resolved: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -179,7 +167,6 @@ pub struct ListCommentReports {
   pub unresolved_only: Option<bool>,
   /// if no community is given, it returns reports for all communities moderated by the auth user
   pub community_id: Option<CommunityId>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
diff --git a/crates/api_common/src/community.rs b/crates/api_common/src/community.rs
index ff6ed1271a54963793e622e52776e95d9272a2b9..7c96344b5be44d1921c9ad1281af513f8848213c 100644
--- a/crates/api_common/src/community.rs
+++ b/crates/api_common/src/community.rs
@@ -1,4 +1,3 @@
-use crate::sensitive::Sensitive;
 use lemmy_db_schema::{
   newtypes::{CommunityId, LanguageId, PersonId},
   source::site::Site,
@@ -20,7 +19,6 @@ pub struct GetCommunity {
   pub id: Option<CommunityId>,
   /// Example: star_trek , or star_trek@xyz.tld
   pub name: Option<String>,
-  pub auth: Option<Sensitive<String>>,
 }
 
 #[skip_serializing_none]
@@ -56,7 +54,6 @@ pub struct CreateCommunity {
   /// Whether to restrict posting only to moderators.
   pub posting_restricted_to_mods: Option<bool>,
   pub discussion_languages: Option<Vec<LanguageId>>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -79,7 +76,6 @@ pub struct ListCommunities {
   pub show_nsfw: Option<bool>,
   pub page: Option<i64>,
   pub limit: Option<i64>,
-  pub auth: Option<Sensitive<String>>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -102,7 +98,6 @@ pub struct BanFromCommunity {
   pub remove_data: Option<bool>,
   pub reason: Option<String>,
   pub expires: Option<i64>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -122,7 +117,6 @@ pub struct AddModToCommunity {
   pub community_id: CommunityId,
   pub person_id: PersonId,
   pub added: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -153,7 +147,6 @@ pub struct EditCommunity {
   /// Whether to restrict posting only to moderators.
   pub posting_restricted_to_mods: Option<bool>,
   pub discussion_languages: Option<Vec<LanguageId>>,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -166,7 +159,6 @@ pub struct HideCommunity {
   pub community_id: CommunityId,
   pub hidden: bool,
   pub reason: Option<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -177,7 +169,6 @@ pub struct HideCommunity {
 pub struct DeleteCommunity {
   pub community_id: CommunityId,
   pub deleted: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -190,7 +181,6 @@ pub struct RemoveCommunity {
   pub removed: bool,
   pub reason: Option<String>,
   pub expires: Option<i64>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -200,7 +190,6 @@ pub struct RemoveCommunity {
 pub struct FollowCommunity {
   pub community_id: CommunityId,
   pub follow: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -210,7 +199,6 @@ pub struct FollowCommunity {
 pub struct BlockCommunity {
   pub community_id: CommunityId,
   pub block: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -230,5 +218,4 @@ pub struct BlockCommunityResponse {
 pub struct TransferCommunity {
   pub community_id: CommunityId,
   pub person_id: PersonId,
-  pub auth: Sensitive<String>,
 }
diff --git a/crates/api_common/src/custom_emoji.rs b/crates/api_common/src/custom_emoji.rs
index 7f3461ca794c8c2b5351980bfbd8959b0a99874d..83248dc91a932ae879b8234ff6cbd97aa41a2fe9 100644
--- a/crates/api_common/src/custom_emoji.rs
+++ b/crates/api_common/src/custom_emoji.rs
@@ -1,4 +1,3 @@
-use crate::sensitive::Sensitive;
 use lemmy_db_schema::newtypes::CustomEmojiId;
 use lemmy_db_views::structs::CustomEmojiView;
 use serde::{Deserialize, Serialize};
@@ -17,7 +16,6 @@ pub struct CreateCustomEmoji {
   pub image_url: Url,
   pub alt_text: String,
   pub keywords: Vec<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -31,7 +29,6 @@ pub struct EditCustomEmoji {
   pub image_url: Url,
   pub alt_text: String,
   pub keywords: Vec<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -40,7 +37,6 @@ pub struct EditCustomEmoji {
 /// Delete a custom emoji.
 pub struct DeleteCustomEmoji {
   pub id: CustomEmojiId,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Serialize, Deserialize, Clone)]
diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs
index 3f43f30dfdf872b3cf2adb8a88609d899dc4fd41..feb945b1791d60c160f2b24b9d4c0703fabb5237 100644
--- a/crates/api_common/src/person.rs
+++ b/crates/api_common/src/person.rs
@@ -119,7 +119,6 @@ pub struct SaveUserSettings {
   pub show_new_post_notifs: Option<bool>,
   /// A list of languages you are able to see discussion in.
   pub discussion_languages: Option<Vec<LanguageId>>,
-  pub auth: Sensitive<String>,
   /// Open links in a new tab
   pub open_links_in_new_tab: Option<bool>,
   /// Enable infinite scroll
@@ -134,7 +133,6 @@ pub struct ChangePassword {
   pub new_password: Sensitive<String>,
   pub new_password_verify: Sensitive<String>,
   pub old_password: Sensitive<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -167,7 +165,6 @@ pub struct GetPersonDetails {
   pub limit: Option<i64>,
   pub community_id: Option<CommunityId>,
   pub saved_only: Option<bool>,
-  pub auth: Option<Sensitive<String>>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -181,14 +178,6 @@ pub struct GetPersonDetailsResponse {
   pub moderates: Vec<CommunityModeratorView>,
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone, Default)]
-#[cfg_attr(feature = "full", derive(TS))]
-#[cfg_attr(feature = "full", ts(export))]
-/// Marks all notifications as read.
-pub struct MarkAllAsRead {
-  pub auth: Sensitive<String>,
-}
-
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
 #[cfg_attr(feature = "full", derive(TS))]
 #[cfg_attr(feature = "full", ts(export))]
@@ -196,7 +185,6 @@ pub struct MarkAllAsRead {
 pub struct AddAdmin {
   pub person_id: PersonId,
   pub added: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -219,18 +207,9 @@ pub struct BanPerson {
   pub remove_data: Option<bool>,
   pub reason: Option<String>,
   pub expires: Option<i64>,
-  pub auth: Sensitive<String>,
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone, Default)]
-#[cfg_attr(feature = "full", derive(TS))]
-#[cfg_attr(feature = "full", ts(export))]
-/// Get a list of banned persons.
 // TODO, this should be paged, since the list can be quite long.
-pub struct GetBannedPersons {
-  pub auth: Sensitive<String>,
-}
-
 #[derive(Debug, Serialize, Deserialize, Clone)]
 #[cfg_attr(feature = "full", derive(TS))]
 #[cfg_attr(feature = "full", ts(export))]
@@ -255,7 +234,6 @@ pub struct BanPersonResponse {
 pub struct BlockPerson {
   pub person_id: PersonId,
   pub block: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -277,7 +255,6 @@ pub struct GetReplies {
   pub page: Option<i64>,
   pub limit: Option<i64>,
   pub unread_only: Option<bool>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -299,7 +276,6 @@ pub struct GetPersonMentions {
   pub page: Option<i64>,
   pub limit: Option<i64>,
   pub unread_only: Option<bool>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -317,7 +293,6 @@ pub struct GetPersonMentionsResponse {
 pub struct MarkPersonMentionAsRead {
   pub person_mention_id: PersonMentionId,
   pub read: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -335,7 +310,6 @@ pub struct PersonMentionResponse {
 pub struct MarkCommentReplyAsRead {
   pub comment_reply_id: CommentReplyId,
   pub read: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -353,7 +327,6 @@ pub struct CommentReplyResponse {
 pub struct DeleteAccount {
   pub password: Sensitive<String>,
   pub delete_content: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -393,7 +366,6 @@ pub struct PasswordChangeAfterReset {
 /// Get a count of the number of reports.
 pub struct GetReportCount {
   pub community_id: Option<CommunityId>,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -408,14 +380,6 @@ pub struct GetReportCountResponse {
   pub private_message_reports: Option<i64>,
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone, Default)]
-#[cfg_attr(feature = "full", derive(TS))]
-#[cfg_attr(feature = "full", ts(export))]
-/// Get a count of unread notifications.
-pub struct GetUnreadCount {
-  pub auth: Sensitive<String>,
-}
-
 #[derive(Debug, Serialize, Deserialize, Clone)]
 #[cfg_attr(feature = "full", derive(TS))]
 #[cfg_attr(feature = "full", ts(export))]
diff --git a/crates/api_common/src/post.rs b/crates/api_common/src/post.rs
index bb5c1cccc2c32b5e9eeb0043c1f24de96712f10c..86f7217019c3273cc0da7b9a5331cf1dbe32fa35 100644
--- a/crates/api_common/src/post.rs
+++ b/crates/api_common/src/post.rs
@@ -1,4 +1,3 @@
-use crate::sensitive::Sensitive;
 use lemmy_db_schema::{
   newtypes::{CommentId, CommunityId, DbUrl, LanguageId, PostId, PostReportId},
   ListingType,
@@ -29,7 +28,6 @@ pub struct CreatePost {
   pub honeypot: Option<String>,
   pub nsfw: Option<bool>,
   pub language_id: Option<LanguageId>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -47,7 +45,6 @@ pub struct PostResponse {
 pub struct GetPost {
   pub id: Option<PostId>,
   pub comment_id: Option<CommentId>,
-  pub auth: Option<Sensitive<String>>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -78,7 +75,6 @@ pub struct GetPosts {
   pub saved_only: Option<bool>,
   pub liked_only: Option<bool>,
   pub disliked_only: Option<bool>,
-  pub auth: Option<Sensitive<String>>,
   pub page_cursor: Option<PaginationCursor>,
 }
 
@@ -101,7 +97,6 @@ pub struct CreatePostLike {
   pub post_id: PostId,
   /// Score must be -1, 0, or 1.
   pub score: i16,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -118,7 +113,6 @@ pub struct EditPost {
   pub body: Option<String>,
   pub nsfw: Option<bool>,
   pub language_id: Option<LanguageId>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -128,7 +122,6 @@ pub struct EditPost {
 pub struct DeletePost {
   pub post_id: PostId,
   pub deleted: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -140,7 +133,6 @@ pub struct RemovePost {
   pub post_id: PostId,
   pub removed: bool,
   pub reason: Option<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -150,7 +142,6 @@ pub struct RemovePost {
 pub struct MarkPostAsRead {
   pub post_id: PostId,
   pub read: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -160,7 +151,6 @@ pub struct MarkPostAsRead {
 pub struct LockPost {
   pub post_id: PostId,
   pub locked: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -171,7 +161,6 @@ pub struct FeaturePost {
   pub post_id: PostId,
   pub featured: bool,
   pub feature_type: PostFeatureType,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -181,7 +170,6 @@ pub struct FeaturePost {
 pub struct SavePost {
   pub post_id: PostId,
   pub save: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -191,7 +179,6 @@ pub struct SavePost {
 pub struct CreatePostReport {
   pub post_id: PostId,
   pub reason: String,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -209,7 +196,6 @@ pub struct PostReportResponse {
 pub struct ResolvePostReport {
   pub report_id: PostReportId,
   pub resolved: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -224,7 +210,6 @@ pub struct ListPostReports {
   pub unresolved_only: Option<bool>,
   /// if no community is given, it returns reports for all communities moderated by the auth user
   pub community_id: Option<CommunityId>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
diff --git a/crates/api_common/src/private_message.rs b/crates/api_common/src/private_message.rs
index d27c4f659150304e06b73c3a5691bc9706372a69..8d469127db722da3dad4b20cee58377ec4e3c4d6 100644
--- a/crates/api_common/src/private_message.rs
+++ b/crates/api_common/src/private_message.rs
@@ -1,4 +1,3 @@
-use crate::sensitive::Sensitive;
 use lemmy_db_schema::newtypes::{PersonId, PrivateMessageId, PrivateMessageReportId};
 use lemmy_db_views::structs::{PrivateMessageReportView, PrivateMessageView};
 use serde::{Deserialize, Serialize};
@@ -13,7 +12,6 @@ use ts_rs::TS;
 pub struct CreatePrivateMessage {
   pub content: String,
   pub recipient_id: PersonId,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -23,7 +21,6 @@ pub struct CreatePrivateMessage {
 pub struct EditPrivateMessage {
   pub private_message_id: PrivateMessageId,
   pub content: String,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -33,7 +30,6 @@ pub struct EditPrivateMessage {
 pub struct DeletePrivateMessage {
   pub private_message_id: PrivateMessageId,
   pub deleted: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -43,7 +39,6 @@ pub struct DeletePrivateMessage {
 pub struct MarkPrivateMessageAsRead {
   pub private_message_id: PrivateMessageId,
   pub read: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -56,7 +51,6 @@ pub struct GetPrivateMessages {
   pub page: Option<i64>,
   pub limit: Option<i64>,
   pub creator_id: Option<PersonId>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -82,7 +76,6 @@ pub struct PrivateMessageResponse {
 pub struct CreatePrivateMessageReport {
   pub private_message_id: PrivateMessageId,
   pub reason: String,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -100,7 +93,6 @@ pub struct PrivateMessageReportResponse {
 pub struct ResolvePrivateMessageReport {
   pub report_id: PrivateMessageReportId,
   pub resolved: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -114,7 +106,6 @@ pub struct ListPrivateMessageReports {
   pub limit: Option<i64>,
   /// Only shows the unresolved reports
   pub unresolved_only: Option<bool>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs
index 7ed94f2de0b4a8c71319e01c458d3d98b5ce6cca..b047c6dd03e684adee9f33c7455fd67544a58883 100644
--- a/crates/api_common/src/site.rs
+++ b/crates/api_common/src/site.rs
@@ -1,4 +1,3 @@
-use crate::sensitive::Sensitive;
 use lemmy_db_schema::{
   newtypes::{CommentId, CommunityId, InstanceId, LanguageId, PersonId, PostId},
   source::{instance::Instance, language::Language, tagline::Tagline},
@@ -62,7 +61,6 @@ pub struct Search {
   pub listing_type: Option<ListingType>,
   pub page: Option<i64>,
   pub limit: Option<i64>,
-  pub auth: Option<Sensitive<String>>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -85,7 +83,6 @@ pub struct SearchResponse {
 pub struct ResolveObject {
   /// Can be the full url, or a shortened version like: !fediverse@lemmy.ml
   pub q: String,
-  pub auth: Option<Sensitive<String>>,
 }
 
 #[skip_serializing_none]
@@ -113,7 +110,6 @@ pub struct GetModlog {
   pub limit: Option<i64>,
   pub type_: Option<ModlogActionType>,
   pub other_person_id: Option<PersonId>,
-  pub auth: Option<Sensitive<String>>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -184,7 +180,6 @@ pub struct CreateSite {
   pub blocked_instances: Option<Vec<String>>,
   pub taglines: Option<Vec<String>>,
   pub registration_mode: Option<RegistrationMode>,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -263,16 +258,6 @@ pub struct EditSite {
   pub registration_mode: Option<RegistrationMode>,
   /// Whether to email admins for new reports.
   pub reports_email_admins: Option<bool>,
-  pub auth: Sensitive<String>,
-}
-
-#[skip_serializing_none]
-#[derive(Debug, Serialize, Deserialize, Clone, Default)]
-#[cfg_attr(feature = "full", derive(TS))]
-#[cfg_attr(feature = "full", ts(export))]
-/// Fetches the site.
-pub struct GetSite {
-  pub auth: Option<Sensitive<String>>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -326,14 +311,6 @@ pub struct MyUserInfo {
   pub discussion_languages: Vec<LanguageId>,
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS))]
-#[cfg_attr(feature = "full", ts(export))]
-/// Leaves the admin team.
-pub struct LeaveAdmin {
-  pub auth: Sensitive<String>,
-}
-
 #[derive(Debug, Serialize, Deserialize, Clone)]
 #[cfg_attr(feature = "full", derive(TS))]
 #[cfg_attr(feature = "full", ts(export))]
@@ -352,7 +329,6 @@ pub struct FederatedInstances {
 pub struct PurgePerson {
   pub person_id: PersonId,
   pub reason: Option<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -363,7 +339,6 @@ pub struct PurgePerson {
 pub struct PurgeCommunity {
   pub community_id: CommunityId,
   pub reason: Option<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -374,7 +349,6 @@ pub struct PurgeCommunity {
 pub struct PurgePost {
   pub post_id: PostId,
   pub reason: Option<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
@@ -385,7 +359,6 @@ pub struct PurgePost {
 pub struct PurgeComment {
   pub comment_id: CommentId,
   pub reason: Option<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Serialize, Deserialize, Clone)]
@@ -406,7 +379,6 @@ pub struct ListRegistrationApplications {
   pub unread_only: Option<bool>,
   pub page: Option<i64>,
   pub limit: Option<i64>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -426,7 +398,6 @@ pub struct ApproveRegistrationApplication {
   pub id: i32,
   pub approve: bool,
   pub deny_reason: Option<String>,
-  pub auth: Sensitive<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -437,14 +408,6 @@ pub struct RegistrationApplicationResponse {
   pub registration_application: RegistrationApplicationView,
 }
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
-#[cfg_attr(feature = "full", derive(TS))]
-#[cfg_attr(feature = "full", ts(export))]
-/// Gets a count of unread registration applications.
-pub struct GetUnreadRegistrationApplicationCount {
-  pub auth: Sensitive<String>,
-}
-
 #[derive(Debug, Serialize, Deserialize, Clone)]
 #[cfg_attr(feature = "full", derive(TS))]
 #[cfg_attr(feature = "full", ts(export))]
@@ -460,7 +423,6 @@ pub struct GetUnreadRegistrationApplicationCountResponse {
 pub struct BlockInstance {
   pub instance_id: InstanceId,
   pub block: bool,
-  pub auth: Sensitive<String>,
 }
 
 #[skip_serializing_none]
diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs
index 9d8181a2eda646046515c4613956b887b4dc28e4..26ef18c9eed0d7e061bad1450823b6df694078bb 100644
--- a/crates/api_common/src/utils.rs
+++ b/crates/api_common/src/utils.rs
@@ -1,14 +1,9 @@
-use crate::{
-  context::LemmyContext,
-  request::purge_image_from_pictrs,
-  sensitive::Sensitive,
-  site::FederatedInstances,
-};
+use crate::{context::LemmyContext, request::purge_image_from_pictrs, site::FederatedInstances};
 use anyhow::Context;
 use chrono::{DateTime, Utc};
 use lemmy_db_schema::{
   impls::person::is_banned,
-  newtypes::{CommunityId, DbUrl, LocalUserId, PersonId, PostId},
+  newtypes::{CommunityId, DbUrl, PersonId, PostId},
   source::{
     comment::{Comment, CommentUpdateForm},
     community::{Community, CommunityModerator, CommunityUpdateForm},
@@ -20,11 +15,9 @@ use lemmy_db_schema::{
     person::{Person, PersonUpdateForm},
     person_block::PersonBlock,
     post::{Post, PostRead, PostReadForm},
-    registration_application::RegistrationApplication,
   },
   traits::{Crud, Readable},
   utils::DbPool,
-  RegistrationMode,
 };
 use lemmy_db_views::{comment_view::CommentQuery, structs::LocalUserView};
 use lemmy_db_views_actor::structs::{
@@ -33,9 +26,8 @@ use lemmy_db_views_actor::structs::{
   CommunityView,
 };
 use lemmy_utils::{
-  claims::Claims,
   email::{send_email, translations::Lang},
-  error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType},
+  error::{LemmyError, LemmyErrorExt, LemmyErrorType},
   location_info,
   rate_limit::RateLimitConfig,
   settings::structs::Settings,
@@ -134,58 +126,6 @@ pub async fn mark_post_as_unread(
     .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)
 }
 
-#[tracing::instrument(skip_all)]
-pub async fn local_user_view_from_jwt(
-  jwt: &str,
-  context: &LemmyContext,
-) -> Result<LocalUserView, LemmyError> {
-  let claims = Claims::decode(jwt, &context.secret().jwt_secret)
-    .with_lemmy_type(LemmyErrorType::NotLoggedIn)?
-    .claims;
-  let local_user_id = LocalUserId(claims.sub);
-  let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
-  check_user_valid(
-    local_user_view.person.banned,
-    local_user_view.person.ban_expires,
-    local_user_view.person.deleted,
-  )?;
-
-  check_validator_time(&local_user_view.local_user.validator_time, &claims)?;
-
-  Ok(local_user_view)
-}
-
-#[tracing::instrument(skip_all)]
-pub async fn local_user_view_from_jwt_opt(
-  jwt: Option<&Sensitive<String>>,
-  context: &LemmyContext,
-) -> Option<LocalUserView> {
-  local_user_view_from_jwt(jwt?, context).await.ok()
-}
-#[tracing::instrument(skip_all)]
-pub async fn local_user_view_from_jwt_opt_new(
-  local_user_view: &mut Option<LocalUserView>,
-  jwt: Option<&Sensitive<String>>,
-  context: &LemmyContext,
-) {
-  if local_user_view.is_none() {
-    *local_user_view = local_user_view_from_jwt_opt(jwt, context).await;
-  }
-}
-
-/// Checks if user's token was issued before user's password reset.
-pub fn check_validator_time(
-  validator_time: &DateTime<Utc>,
-  claims: &Claims,
-) -> Result<(), LemmyError> {
-  let user_validation_time = validator_time.timestamp();
-  if user_validation_time > claims.iat {
-    Err(LemmyErrorType::NotLoggedIn)?
-  } else {
-    Ok(())
-  }
-}
-
 pub fn check_user_valid(
   banned: bool,
   ban_expires: Option<DateTime<Utc>>,
@@ -505,32 +445,6 @@ pub async fn send_new_report_email_to_admins(
   Ok(())
 }
 
-pub async fn check_registration_application(
-  local_user_view: &LocalUserView,
-  local_site: &LocalSite,
-  pool: &mut DbPool<'_>,
-) -> Result<(), LemmyError> {
-  if (local_site.registration_mode == RegistrationMode::RequireApplication
-    || local_site.registration_mode == RegistrationMode::Closed)
-    && !local_user_view.local_user.accepted_application
-    && !local_user_view.local_user.admin
-  {
-    // Fetch the registration, see if its denied
-    let local_user_id = local_user_view.local_user.id;
-    let registration = RegistrationApplication::find_by_local_user_id(pool, local_user_id).await?;
-    if let Some(deny_reason) = registration.deny_reason {
-      let lang = get_interface_language(local_user_view);
-      let registration_denied_message = format!("{}: {}", lang.registration_denied(), deny_reason);
-      Err(LemmyErrorType::RegistrationDenied(
-        registration_denied_message,
-      ))?
-    } else {
-      Err(LemmyErrorType::RegistrationApplicationIsPending)?
-    }
-  }
-  Ok(())
-}
-
 pub fn check_private_instance_and_federation_enabled(
   local_site: &LocalSite,
 ) -> Result<(), LemmyError> {
diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs
index 3ba19d1b93c7d18dc329155bee2ad2c4480b73f1..c9aa4f774e36468969826b85bd757129be04bd17 100644
--- a/crates/api_crud/src/comment/create.rs
+++ b/crates/api_crud/src/comment/create.rs
@@ -12,7 +12,6 @@ use lemmy_api_common::{
     generate_local_apub_endpoint,
     get_post,
     local_site_to_slur_regex,
-    local_user_view_from_jwt,
     sanitize_html_api,
     EndpointType,
   },
@@ -28,6 +27,7 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Likeable},
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
   utils::{
@@ -43,8 +43,8 @@ const MAX_COMMENT_DEPTH_LIMIT: usize = 100;
 pub async fn create_comment(
   data: Json<CreateComment>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommentResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   let content = remove_slurs(
diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs
index 5f5db905ef6c6f43f84db636c83f68dcd080006d..1c986d03c397072a43dbcc93227bfb559431586f 100644
--- a/crates/api_crud/src/comment/delete.rs
+++ b/crates/api_crud/src/comment/delete.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   comment::{CommentResponse, DeleteComment},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{check_community_ban, local_user_view_from_jwt},
+  utils::check_community_ban,
 };
 use lemmy_db_schema::{
   source::{
@@ -14,16 +14,15 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
-use lemmy_db_views::structs::CommentView;
+use lemmy_db_views::structs::{CommentView, LocalUserView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn delete_comment(
   data: Json<DeleteComment>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommentResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let comment_id = data.comment_id;
   let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
 
diff --git a/crates/api_crud/src/comment/read.rs b/crates/api_crud/src/comment/read.rs
index 5f07f74961e97165dd1a0fd61bce0325db709161..733d08682a134db06e6ded33aad073d79da20cdb 100644
--- a/crates/api_crud/src/comment/read.rs
+++ b/crates/api_crud/src/comment/read.rs
@@ -3,17 +3,18 @@ use lemmy_api_common::{
   build_response::build_comment_response,
   comment::{CommentResponse, GetComment},
   context::LemmyContext,
-  utils::{check_private_instance, local_user_view_from_jwt_opt},
+  utils::check_private_instance,
 };
 use lemmy_db_schema::source::local_site::LocalSite;
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn get_comment(
   data: Query<GetComment>,
   context: Data<LemmyContext>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<CommentResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   check_private_instance(&local_user_view, &local_site)?;
diff --git a/crates/api_crud/src/comment/remove.rs b/crates/api_crud/src/comment/remove.rs
index 2bb5c75f2f9b9fdf12de3b6ee9f105254eb0eb2a..601101bb8cef42300a8bed63533faeb25e522f79 100644
--- a/crates/api_crud/src/comment/remove.rs
+++ b/crates/api_crud/src/comment/remove.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   comment::{CommentResponse, RemoveComment},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
+  utils::{check_community_ban, is_mod_or_admin},
 };
 use lemmy_db_schema::{
   source::{
@@ -15,16 +15,15 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
-use lemmy_db_views::structs::CommentView;
+use lemmy_db_views::structs::{CommentView, LocalUserView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn remove_comment(
   data: Json<RemoveComment>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommentResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let comment_id = data.comment_id;
   let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
 
diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs
index bf6bdff84f992cf04791cb9ff8ad8c4032399630..2d966d0fa95c0419777008f00005bac47de21ebe 100644
--- a/crates/api_crud/src/comment/update.rs
+++ b/crates/api_crud/src/comment/update.rs
@@ -5,12 +5,7 @@ use lemmy_api_common::{
   comment::{CommentResponse, EditComment},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{
-    check_community_ban,
-    local_site_to_slur_regex,
-    local_user_view_from_jwt,
-    sanitize_html_api_opt,
-  },
+  utils::{check_community_ban, local_site_to_slur_regex, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   source::{
@@ -21,7 +16,7 @@ use lemmy_db_schema::{
   traits::Crud,
   utils::naive_now,
 };
-use lemmy_db_views::structs::CommentView;
+use lemmy_db_views::structs::{CommentView, LocalUserView};
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
   utils::{
@@ -35,8 +30,8 @@ use lemmy_utils::{
 pub async fn update_comment(
   data: Json<EditComment>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommentResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   let comment_id = data.comment_id;
diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs
index d6c07c128814bfa9b9c367b61b31321e1c16dfc3..633fa81a9a79fd75a8d1cba3d341946e922276e0 100644
--- a/crates/api_crud/src/community/create.rs
+++ b/crates/api_crud/src/community/create.rs
@@ -11,7 +11,6 @@ use lemmy_api_common::{
     generate_shared_inbox_url,
     is_admin,
     local_site_to_slur_regex,
-    local_user_view_from_jwt,
     sanitize_html_api,
     sanitize_html_api_opt,
     EndpointType,
@@ -32,7 +31,7 @@ use lemmy_db_schema::{
   traits::{ApubActor, Crud, Followable, Joinable},
   utils::diesel_option_overwrite_to_url_create,
 };
-use lemmy_db_views::structs::SiteView;
+use lemmy_db_views::structs::{LocalUserView, SiteView};
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
   utils::{
@@ -45,8 +44,8 @@ use lemmy_utils::{
 pub async fn create_community(
   data: Json<CreateCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommunityResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let site_view = SiteView::read_local(&mut context.pool()).await?;
   let local_site = site_view.local_site;
 
diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs
index 9c8db512df1636bb330fee9998aad88474081f05..7c94e0ccc4c3b59cba86d2afa222400179034d69 100644
--- a/crates/api_crud/src/community/delete.rs
+++ b/crates/api_crud/src/community/delete.rs
@@ -5,12 +5,13 @@ use lemmy_api_common::{
   community::{CommunityResponse, DeleteCommunity},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{is_top_mod, local_user_view_from_jwt},
+  utils::is_top_mod,
 };
 use lemmy_db_schema::{
   source::community::{Community, CommunityUpdateForm},
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::CommunityModeratorView;
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
@@ -18,9 +19,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn delete_community(
   data: Json<DeleteCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommunityResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Fetch the community mods
   let community_id = data.community_id;
   let community_mods =
diff --git a/crates/api_crud/src/community/list.rs b/crates/api_crud/src/community/list.rs
index 1f620dd9c98887c2303758a62366d5d4e6012bf0..0879421ba92b3679ee96573b309d09dbb21b95e4 100644
--- a/crates/api_crud/src/community/list.rs
+++ b/crates/api_crud/src/community/list.rs
@@ -2,9 +2,10 @@ use actix_web::web::{Data, Json, Query};
 use lemmy_api_common::{
   community::{ListCommunities, ListCommunitiesResponse},
   context::LemmyContext,
-  utils::{check_private_instance, is_admin, local_user_view_from_jwt_opt},
+  utils::{check_private_instance, is_admin},
 };
 use lemmy_db_schema::source::local_site::LocalSite;
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::community_view::CommunityQuery;
 use lemmy_utils::error::LemmyError;
 
@@ -12,8 +13,8 @@ use lemmy_utils::error::LemmyError;
 pub async fn list_communities(
   data: Query<ListCommunities>,
   context: Data<LemmyContext>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<ListCommunitiesResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
   let local_site = LocalSite::read(&mut context.pool()).await?;
   let is_admin = local_user_view
     .as_ref()
diff --git a/crates/api_crud/src/community/remove.rs b/crates/api_crud/src/community/remove.rs
index f0bee639de6abbcb75541249ba34f05f43fcad22..3a2cc654a04d583ef5e8c1c8135f3d38653c65dd 100644
--- a/crates/api_crud/src/community/remove.rs
+++ b/crates/api_crud/src/community/remove.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   community::{CommunityResponse, RemoveCommunity},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{is_admin, local_user_view_from_jwt},
+  utils::is_admin,
 };
 use lemmy_db_schema::{
   source::{
@@ -14,6 +14,7 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
   utils::time::naive_from_unix,
@@ -23,9 +24,8 @@ use lemmy_utils::{
 pub async fn remove_community(
   data: Json<RemoveCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommunityResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Verify its an admin (only an admin can remove a community)
   is_admin(&local_user_view)?;
 
diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs
index 122cab94e16e315b4627e504d075ed8eb5d4bcd2..893128d8c5f53c29eeb1a8f7eecc04c3a3ecb010 100644
--- a/crates/api_crud/src/community/update.rs
+++ b/crates/api_crud/src/community/update.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   community::{CommunityResponse, EditCommunity},
   context::LemmyContext,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{local_site_to_slur_regex, local_user_view_from_jwt, sanitize_html_api_opt},
+  utils::{local_site_to_slur_regex, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   newtypes::PersonId,
@@ -17,6 +17,7 @@ use lemmy_db_schema::{
   traits::Crud,
   utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::CommunityModeratorView;
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
@@ -27,8 +28,8 @@ use lemmy_utils::{
 pub async fn update_community(
   data: Json<EditCommunity>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CommunityResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   let slur_regex = local_site_to_slur_regex(&local_site);
diff --git a/crates/api_crud/src/custom_emoji/create.rs b/crates/api_crud/src/custom_emoji/create.rs
index 046003627e69a834177a5ef38512e982eec89c46..7a2ae9b2d9540f0983f48f4550a83456801df863 100644
--- a/crates/api_crud/src/custom_emoji/create.rs
+++ b/crates/api_crud/src/custom_emoji/create.rs
@@ -3,23 +3,22 @@ use actix_web::web::Json;
 use lemmy_api_common::{
   context::LemmyContext,
   custom_emoji::{CreateCustomEmoji, CustomEmojiResponse},
-  utils::{is_admin, local_user_view_from_jwt, sanitize_html_api},
+  utils::{is_admin, sanitize_html_api},
 };
 use lemmy_db_schema::source::{
   custom_emoji::{CustomEmoji, CustomEmojiInsertForm},
   custom_emoji_keyword::{CustomEmojiKeyword, CustomEmojiKeywordInsertForm},
   local_site::LocalSite,
 };
-use lemmy_db_views::structs::CustomEmojiView;
+use lemmy_db_views::structs::{CustomEmojiView, LocalUserView};
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn create_custom_emoji(
   data: Json<CreateCustomEmoji>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CustomEmojiResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let local_site = LocalSite::read(&mut context.pool()).await?;
   // Make sure user is an admin
   is_admin(&local_user_view)?;
diff --git a/crates/api_crud/src/custom_emoji/delete.rs b/crates/api_crud/src/custom_emoji/delete.rs
index be88d310fb11a3eb3da23e15c874354ecbbbcde7..44cddd52017747880169f855a33c7d4df795a427 100644
--- a/crates/api_crud/src/custom_emoji/delete.rs
+++ b/crates/api_crud/src/custom_emoji/delete.rs
@@ -3,18 +3,18 @@ use actix_web::web::Json;
 use lemmy_api_common::{
   context::LemmyContext,
   custom_emoji::{DeleteCustomEmoji, DeleteCustomEmojiResponse},
-  utils::{is_admin, local_user_view_from_jwt},
+  utils::is_admin,
 };
 use lemmy_db_schema::source::custom_emoji::CustomEmoji;
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn delete_custom_emoji(
   data: Json<DeleteCustomEmoji>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<DeleteCustomEmojiResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Make sure user is an admin
   is_admin(&local_user_view)?;
   CustomEmoji::delete(&mut context.pool(), data.id).await?;
diff --git a/crates/api_crud/src/custom_emoji/update.rs b/crates/api_crud/src/custom_emoji/update.rs
index 965250feb107f264a024cc62b4d918b93b170dc2..5c115d5c1bed07588deb634ecbc62ab368339d36 100644
--- a/crates/api_crud/src/custom_emoji/update.rs
+++ b/crates/api_crud/src/custom_emoji/update.rs
@@ -3,23 +3,22 @@ use actix_web::web::Json;
 use lemmy_api_common::{
   context::LemmyContext,
   custom_emoji::{CustomEmojiResponse, EditCustomEmoji},
-  utils::{is_admin, local_user_view_from_jwt, sanitize_html_api},
+  utils::{is_admin, sanitize_html_api},
 };
 use lemmy_db_schema::source::{
   custom_emoji::{CustomEmoji, CustomEmojiUpdateForm},
   custom_emoji_keyword::{CustomEmojiKeyword, CustomEmojiKeywordInsertForm},
   local_site::LocalSite,
 };
-use lemmy_db_views::structs::CustomEmojiView;
+use lemmy_db_views::structs::{CustomEmojiView, LocalUserView};
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn update_custom_emoji(
   data: Json<EditCustomEmoji>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<CustomEmojiResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let local_site = LocalSite::read(&mut context.pool()).await?;
   // Make sure user is an admin
   is_admin(&local_user_view)?;
diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs
index 8cc6ffe625c1ee2457336dbaf94b956080d51636..5c061cb37d5a98d4f5ac268d3b29b11214d109c4 100644
--- a/crates/api_crud/src/post/create.rs
+++ b/crates/api_crud/src/post/create.rs
@@ -12,7 +12,6 @@ use lemmy_api_common::{
     generate_local_apub_endpoint,
     honeypot_check,
     local_site_to_slur_regex,
-    local_user_view_from_jwt,
     mark_post_as_read,
     sanitize_html_api,
     sanitize_html_api_opt,
@@ -29,6 +28,7 @@ use lemmy_db_schema::{
   },
   traits::{Crud, Likeable},
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
@@ -46,8 +46,8 @@ use webmention::{Webmention, WebmentionError};
 pub async fn create_post(
   data: Json<CreatePost>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   let slur_regex = local_site_to_slur_regex(&local_site);
diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs
index 0f471a527855ece947965c1fc3418f9c8a3ce828..90c95c6b22f36ee3a1a986e679e5acd599ea9412 100644
--- a/crates/api_crud/src/post/delete.rs
+++ b/crates/api_crud/src/post/delete.rs
@@ -5,21 +5,21 @@ use lemmy_api_common::{
   context::LemmyContext,
   post::{DeletePost, PostResponse},
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{check_community_ban, check_community_deleted_or_removed, local_user_view_from_jwt},
+  utils::{check_community_ban, check_community_deleted_or_removed},
 };
 use lemmy_db_schema::{
   source::post::{Post, PostUpdateForm},
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::{LemmyError, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn delete_post(
   data: Json<DeletePost>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let post_id = data.post_id;
   let orig_post = Post::read(&mut context.pool(), post_id).await?;
 
diff --git a/crates/api_crud/src/post/read.rs b/crates/api_crud/src/post/read.rs
index d37b1a5825819cb875325cbb9b6660fab312d72e..352f97fe1f13b3ede79e6c85ce956f4294b40235 100644
--- a/crates/api_crud/src/post/read.rs
+++ b/crates/api_crud/src/post/read.rs
@@ -2,19 +2,17 @@ use actix_web::web::{Data, Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   post::{GetPost, GetPostResponse},
-  utils::{
-    check_private_instance,
-    is_mod_or_admin_opt,
-    local_user_view_from_jwt_opt,
-    mark_post_as_read,
-  },
+  utils::{check_private_instance, is_mod_or_admin_opt, mark_post_as_read},
 };
 use lemmy_db_schema::{
   aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
   source::{comment::Comment, local_site::LocalSite, post::Post},
   traits::Crud,
 };
-use lemmy_db_views::{post_view::PostQuery, structs::PostView};
+use lemmy_db_views::{
+  post_view::PostQuery,
+  structs::{LocalUserView, PostView},
+};
 use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
@@ -22,8 +20,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn get_post(
   data: Query<GetPost>,
   context: Data<LemmyContext>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<GetPostResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   check_private_instance(&local_user_view, &local_site)?;
diff --git a/crates/api_crud/src/post/remove.rs b/crates/api_crud/src/post/remove.rs
index ee100cfdc2288518f1cdce84c2621c1819646ca8..52f380d88e8fa6e30f05e0c500c6ddb3cd30926e 100644
--- a/crates/api_crud/src/post/remove.rs
+++ b/crates/api_crud/src/post/remove.rs
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   context::LemmyContext,
   post::{PostResponse, RemovePost},
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
+  utils::{check_community_ban, is_mod_or_admin},
 };
 use lemmy_db_schema::{
   source::{
@@ -14,15 +14,15 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn remove_post(
   data: Json<RemovePost>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   let post_id = data.post_id;
   let orig_post = Post::read(&mut context.pool(), post_id).await?;
 
diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs
index 880991b8e1c13702b05ac6635d4d741c3ffe908a..04e6191a86b1ecea3312b8cc30cfa50d410e98fb 100644
--- a/crates/api_crud/src/post/update.rs
+++ b/crates/api_crud/src/post/update.rs
@@ -6,12 +6,7 @@ use lemmy_api_common::{
   post::{EditPost, PostResponse},
   request::fetch_site_data,
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{
-    check_community_ban,
-    local_site_to_slur_regex,
-    local_user_view_from_jwt,
-    sanitize_html_api_opt,
-  },
+  utils::{check_community_ban, local_site_to_slur_regex, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   source::{
@@ -22,6 +17,7 @@ use lemmy_db_schema::{
   traits::Crud,
   utils::{diesel_option_overwrite, naive_now},
 };
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
   utils::{
@@ -35,8 +31,8 @@ use std::ops::Deref;
 pub async fn update_post(
   data: Json<EditPost>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PostResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   let data_url = data.url.as_ref();
diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs
index 1e22935ce119a28406d78b0601fe03c3800c6c74..c0bdb8ed112ce1a3a0a8d5e6952ddc09d0d46380 100644
--- a/crates/api_crud/src/private_message/create.rs
+++ b/crates/api_crud/src/private_message/create.rs
@@ -9,7 +9,6 @@ use lemmy_api_common::{
     generate_local_apub_endpoint,
     get_interface_language,
     local_site_to_slur_regex,
-    local_user_view_from_jwt,
     sanitize_html_api,
     send_email_to_user,
     EndpointType,
@@ -32,8 +31,8 @@ use lemmy_utils::{
 pub async fn create_private_message(
   data: Json<CreatePrivateMessage>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PrivateMessageResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   let content = sanitize_html_api(&data.content);
diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs
index 65db3b1c1341ad1a886a6b40d53b9b57bed263e4..ef0864d70a92d26080dd42213051080ab76f9eff 100644
--- a/crates/api_crud/src/private_message/delete.rs
+++ b/crates/api_crud/src/private_message/delete.rs
@@ -4,22 +4,20 @@ use lemmy_api_common::{
   context::LemmyContext,
   private_message::{DeletePrivateMessage, PrivateMessageResponse},
   send_activity::{ActivityChannel, SendActivityData},
-  utils::local_user_view_from_jwt,
 };
 use lemmy_db_schema::{
   source::private_message::{PrivateMessage, PrivateMessageUpdateForm},
   traits::Crud,
 };
-use lemmy_db_views::structs::PrivateMessageView;
+use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
 use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn delete_private_message(
   data: Json<DeletePrivateMessage>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PrivateMessageResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
-
   // Checking permissions
   let private_message_id = data.private_message_id;
   let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?;
diff --git a/crates/api_crud/src/private_message/read.rs b/crates/api_crud/src/private_message/read.rs
index 0d3ba8620eb5fe6b3afa02818ba342294d7d437d..933d410f11abc90ad1dd6c683d96b84db8d3c135 100644
--- a/crates/api_crud/src/private_message/read.rs
+++ b/crates/api_crud/src/private_message/read.rs
@@ -2,17 +2,16 @@ use actix_web::web::{Data, Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   private_message::{GetPrivateMessages, PrivateMessagesResponse},
-  utils::local_user_view_from_jwt,
 };
-use lemmy_db_views::private_message_view::PrivateMessageQuery;
+use lemmy_db_views::{private_message_view::PrivateMessageQuery, structs::LocalUserView};
 use lemmy_utils::error::LemmyError;
 
 #[tracing::instrument(skip(context))]
 pub async fn get_private_message(
   data: Query<GetPrivateMessages>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PrivateMessagesResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), &context).await?;
   let person_id = local_user_view.person.id;
 
   let page = data.page;
diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs
index 258dd8bcb93049bd6b4fd241ffd6b1ad374cd1e1..7e55c6416dd67a8582a5e8c611d1f5e7e4053325 100644
--- a/crates/api_crud/src/private_message/update.rs
+++ b/crates/api_crud/src/private_message/update.rs
@@ -4,7 +4,7 @@ use lemmy_api_common::{
   context::LemmyContext,
   private_message::{EditPrivateMessage, PrivateMessageResponse},
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{local_site_to_slur_regex, local_user_view_from_jwt, sanitize_html_api},
+  utils::{local_site_to_slur_regex, sanitize_html_api},
 };
 use lemmy_db_schema::{
   source::{
@@ -14,7 +14,7 @@ use lemmy_db_schema::{
   traits::Crud,
   utils::naive_now,
 };
-use lemmy_db_views::structs::PrivateMessageView;
+use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
   utils::{slurs::remove_slurs, validation::is_valid_body_field},
@@ -24,8 +24,8 @@ use lemmy_utils::{
 pub async fn update_private_message(
   data: Json<EditPrivateMessage>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<PrivateMessageResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   // Checking permissions
diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs
index 5cf9a04a70b2a14920a000ea7a099f33f11f1944..136a187f217264d67087ca23432bcd14cf7fd279 100644
--- a/crates/api_crud/src/site/create.rs
+++ b/crates/api_crud/src/site/create.rs
@@ -8,7 +8,6 @@ use lemmy_api_common::{
     generate_site_inbox_url,
     is_admin,
     local_site_rate_limit_to_rate_limit_config,
-    local_user_view_from_jwt,
     sanitize_html_api,
     sanitize_html_api_opt,
   },
@@ -24,7 +23,7 @@ use lemmy_db_schema::{
   traits::Crud,
   utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
 };
-use lemmy_db_views::structs::SiteView;
+use lemmy_db_views::structs::{LocalUserView, SiteView};
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorType, LemmyResult},
   utils::{
@@ -44,8 +43,8 @@ use url::Url;
 pub async fn create_site(
   data: Json<CreateSite>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<SiteResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   // Make sure user is an admin; other types of users should not create site data...
@@ -589,7 +588,6 @@ mod tests {
       blocked_instances: None,
       taglines: None,
       registration_mode: site_registration_mode,
-      auth: Default::default(),
     }
   }
 }
diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs
index d435b86841cdec6d75a48843a3d8cf9d8d4226c7..2f16b86960600981fc66f6cdaaf08a48b70a164e 100644
--- a/crates/api_crud/src/site/read.rs
+++ b/crates/api_crud/src/site/read.rs
@@ -1,17 +1,12 @@
-use actix_web::web::{Data, Json, Query};
+use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
-  sensitive::Sensitive,
-  site::{GetSite, GetSiteResponse, MyUserInfo},
-  utils::{check_user_valid, check_validator_time},
+  site::{GetSiteResponse, MyUserInfo},
 };
-use lemmy_db_schema::{
-  newtypes::LocalUserId,
-  source::{
-    actor_language::{LocalUserLanguage, SiteLanguage},
-    language::Language,
-    tagline::Tagline,
-  },
+use lemmy_db_schema::source::{
+  actor_language::{LocalUserLanguage, SiteLanguage},
+  language::Language,
+  tagline::Tagline,
 };
 use lemmy_db_views::structs::{CustomEmojiView, LocalUserView, SiteView};
 use lemmy_db_views_actor::structs::{
@@ -23,24 +18,21 @@ use lemmy_db_views_actor::structs::{
   PersonView,
 };
 use lemmy_utils::{
-  claims::Claims,
   error::{LemmyError, LemmyErrorExt, LemmyErrorType},
   version,
 };
 
 #[tracing::instrument(skip(context))]
 pub async fn get_site(
-  data: Query<GetSite>,
   context: Data<LemmyContext>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<GetSiteResponse>, LemmyError> {
   let site_view = SiteView::read_local(&mut context.pool()).await?;
 
   let admins = PersonView::admins(&mut context.pool()).await?;
 
   // Build the local user
-  let my_user = if let Some(local_user_view) =
-    local_user_settings_view_from_jwt_opt(data.auth.as_ref(), &context).await
-  {
+  let my_user = if let Some(local_user_view) = local_user_view {
     let person_id = local_user_view.person.id;
     let local_user_id = local_user_view.local_user.id;
 
@@ -100,32 +92,3 @@ pub async fn get_site(
     custom_emojis,
   }))
 }
-
-#[tracing::instrument(skip_all)]
-async fn local_user_settings_view_from_jwt_opt(
-  jwt: Option<&Sensitive<String>>,
-  context: &LemmyContext,
-) -> Option<LocalUserView> {
-  match jwt {
-    Some(jwt) => {
-      let claims = Claims::decode(jwt.as_ref(), &context.secret().jwt_secret)
-        .ok()?
-        .claims;
-      let local_user_id = LocalUserId(claims.sub);
-      let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id)
-        .await
-        .ok()?;
-      check_user_valid(
-        local_user_view.person.banned,
-        local_user_view.person.ban_expires,
-        local_user_view.person.deleted,
-      )
-      .ok()?;
-
-      check_validator_time(&local_user_view.local_user.validator_time, &claims).ok()?;
-
-      Some(local_user_view)
-    }
-    None => None,
-  }
-}
diff --git a/crates/api_crud/src/site/update.rs b/crates/api_crud/src/site/update.rs
index 4d1fbd4d99b39db50b04487caaf8f9f8e72c38c8..792faa7859ab2120921894d0a46ca468ca598010 100644
--- a/crates/api_crud/src/site/update.rs
+++ b/crates/api_crud/src/site/update.rs
@@ -3,12 +3,7 @@ use actix_web::web::{Data, Json};
 use lemmy_api_common::{
   context::LemmyContext,
   site::{EditSite, SiteResponse},
-  utils::{
-    is_admin,
-    local_site_rate_limit_to_rate_limit_config,
-    local_user_view_from_jwt,
-    sanitize_html_api_opt,
-  },
+  utils::{is_admin, local_site_rate_limit_to_rate_limit_config, sanitize_html_api_opt},
 };
 use lemmy_db_schema::{
   source::{
@@ -25,7 +20,7 @@ use lemmy_db_schema::{
   utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
   RegistrationMode,
 };
-use lemmy_db_views::structs::SiteView;
+use lemmy_db_views::structs::{LocalUserView, SiteView};
 use lemmy_utils::{
   error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
   utils::{
@@ -44,8 +39,8 @@ use lemmy_utils::{
 pub async fn update_site(
   data: Json<EditSite>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<SiteResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
   let site_view = SiteView::read_local(&mut context.pool()).await?;
   let local_site = site_view.local_site;
   let site = site_view.site;
@@ -588,7 +583,6 @@ mod tests {
       taglines: None,
       registration_mode: site_registration_mode,
       reports_email_admins: None,
-      auth: Default::default(),
     }
   }
 }
diff --git a/crates/api_crud/src/user/delete.rs b/crates/api_crud/src/user/delete.rs
index bf1dcdab1ddb266ee4439156ffc74963bbd3d18e..b3abb532f3ce6e690a35757f3ed34af1d1bea6b9 100644
--- a/crates/api_crud/src/user/delete.rs
+++ b/crates/api_crud/src/user/delete.rs
@@ -5,18 +5,18 @@ use lemmy_api_common::{
   context::LemmyContext,
   person::{DeleteAccount, DeleteAccountResponse},
   send_activity::{ActivityChannel, SendActivityData},
-  utils::{local_user_view_from_jwt, purge_user_account},
+  utils::purge_user_account,
 };
 use lemmy_db_schema::source::person::Person;
+use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::error::{LemmyError, LemmyErrorType};
 
 #[tracing::instrument(skip(context))]
 pub async fn delete_account(
   data: Json<DeleteAccount>,
   context: Data<LemmyContext>,
+  local_user_view: LocalUserView,
 ) -> Result<Json<DeleteAccountResponse>, LemmyError> {
-  let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), &context).await?;
-
   // Verify the password
   let valid: bool = verify(
     &data.password,
diff --git a/crates/apub/src/api/list_comments.rs b/crates/apub/src/api/list_comments.rs
index cec2ed9c5931210a59fce3544582c637eb0923ab..7d1de019e6d430b65611c356b556096203b0aeae 100644
--- a/crates/apub/src/api/list_comments.rs
+++ b/crates/apub/src/api/list_comments.rs
@@ -8,7 +8,7 @@ use actix_web::web::{Json, Query};
 use lemmy_api_common::{
   comment::{GetComments, GetCommentsResponse},
   context::LemmyContext,
-  utils::{check_private_instance, local_user_view_from_jwt_opt_new},
+  utils::check_private_instance,
 };
 use lemmy_db_schema::{
   source::{comment::Comment, community::Community, local_site::LocalSite},
@@ -21,9 +21,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn list_comments(
   data: Query<GetComments>,
   context: Data<LemmyContext>,
-  mut local_user_view: Option<LocalUserView>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<GetCommentsResponse>, LemmyError> {
-  local_user_view_from_jwt_opt_new(&mut local_user_view, data.auth.as_ref(), &context).await;
   let local_site = LocalSite::read(&mut context.pool()).await?;
   check_private_instance(&local_user_view, &local_site)?;
 
diff --git a/crates/apub/src/api/list_posts.rs b/crates/apub/src/api/list_posts.rs
index 425e2adf2a738bdb4e7b6a219838e0d8b520a004..dc3618c50fd968779b13e2bc418bc4c05d38dcb8 100644
--- a/crates/apub/src/api/list_posts.rs
+++ b/crates/apub/src/api/list_posts.rs
@@ -8,7 +8,7 @@ use actix_web::web::{Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   post::{GetPosts, GetPostsResponse},
-  utils::{check_private_instance, local_user_view_from_jwt_opt_new},
+  utils::check_private_instance,
 };
 use lemmy_db_schema::source::{community::Community, local_site::LocalSite};
 use lemmy_db_views::{
@@ -21,9 +21,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
 pub async fn list_posts(
   data: Query<GetPosts>,
   context: Data<LemmyContext>,
-  mut local_user_view: Option<LocalUserView>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<GetPostsResponse>, LemmyError> {
-  local_user_view_from_jwt_opt_new(&mut local_user_view, data.auth.as_ref(), &context).await;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   check_private_instance(&local_user_view, &local_site)?;
diff --git a/crates/apub/src/api/read_community.rs b/crates/apub/src/api/read_community.rs
index 76f7f580ddbc8c1edf8e2236d33feb03425917f4..afa6fb8293c3d45c3b99fbd3dd7933a8dd43fa29 100644
--- a/crates/apub/src/api/read_community.rs
+++ b/crates/apub/src/api/read_community.rs
@@ -4,7 +4,7 @@ use actix_web::web::{Json, Query};
 use lemmy_api_common::{
   community::{GetCommunity, GetCommunityResponse},
   context::LemmyContext,
-  utils::{check_private_instance, is_mod_or_admin_opt, local_user_view_from_jwt_opt_new},
+  utils::{check_private_instance, is_mod_or_admin_opt},
 };
 use lemmy_db_schema::source::{
   actor_language::CommunityLanguage,
@@ -20,9 +20,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorTy
 pub async fn get_community(
   data: Query<GetCommunity>,
   context: Data<LemmyContext>,
-  mut local_user_view: Option<LocalUserView>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<GetCommunityResponse>, LemmyError> {
-  local_user_view_from_jwt_opt_new(&mut local_user_view, data.auth.as_ref(), &context).await;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   if data.name.is_none() && data.id.is_none() {
diff --git a/crates/apub/src/api/read_person.rs b/crates/apub/src/api/read_person.rs
index 754a942f194a84022d3a61f5eff6559b464d92be..26ad287f147cae07348fbaeeee84e1a3d61f5a19 100644
--- a/crates/apub/src/api/read_person.rs
+++ b/crates/apub/src/api/read_person.rs
@@ -4,7 +4,7 @@ use actix_web::web::{Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   person::{GetPersonDetails, GetPersonDetailsResponse},
-  utils::{check_private_instance, local_user_view_from_jwt_opt_new},
+  utils::check_private_instance,
 };
 use lemmy_db_schema::{
   source::{local_site::LocalSite, person::Person},
@@ -18,14 +18,13 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
 pub async fn read_person(
   data: Query<GetPersonDetails>,
   context: Data<LemmyContext>,
-  mut local_user_view: Option<LocalUserView>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<GetPersonDetailsResponse>, LemmyError> {
   // Check to make sure a person name or an id is given
   if data.username.is_none() && data.person_id.is_none() {
     Err(LemmyErrorType::NoIdGiven)?
   }
 
-  local_user_view_from_jwt_opt_new(&mut local_user_view, data.auth.as_ref(), &context).await;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   check_private_instance(&local_user_view, &local_site)?;
diff --git a/crates/apub/src/api/resolve_object.rs b/crates/apub/src/api/resolve_object.rs
index b8c0cef147f68a9e5aee63a445f17a891a652f6a..e081377f6f620bdd6728151fcaa5fa753beddd13 100644
--- a/crates/apub/src/api/resolve_object.rs
+++ b/crates/apub/src/api/resolve_object.rs
@@ -9,7 +9,7 @@ use diesel::NotFound;
 use lemmy_api_common::{
   context::LemmyContext,
   site::{ResolveObject, ResolveObjectResponse},
-  utils::{check_private_instance, local_user_view_from_jwt_opt_new},
+  utils::check_private_instance,
 };
 use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool};
 use lemmy_db_views::structs::{CommentView, LocalUserView, PostView};
@@ -20,9 +20,8 @@ use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
 pub async fn resolve_object(
   data: Query<ResolveObject>,
   context: Data<LemmyContext>,
-  mut local_user_view: Option<LocalUserView>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<ResolveObjectResponse>, LemmyError> {
-  local_user_view_from_jwt_opt_new(&mut local_user_view, data.auth.as_ref(), &context).await;
   let local_site = LocalSite::read(&mut context.pool()).await?;
   check_private_instance(&local_user_view, &local_site)?;
   let person_id = local_user_view.map(|v| v.person.id);
diff --git a/crates/apub/src/api/search.rs b/crates/apub/src/api/search.rs
index 262f91681b124e093f4ca52b20e8e1fae2956d3f..0c7231e8f70bd12cbf6ff4bf67ac160d8618ada2 100644
--- a/crates/apub/src/api/search.rs
+++ b/crates/apub/src/api/search.rs
@@ -4,7 +4,7 @@ use actix_web::web::{Json, Query};
 use lemmy_api_common::{
   context::LemmyContext,
   site::{Search, SearchResponse},
-  utils::{check_private_instance, is_admin, local_user_view_from_jwt_opt_new},
+  utils::{check_private_instance, is_admin},
 };
 use lemmy_db_schema::{
   source::{community::Community, local_site::LocalSite},
@@ -19,9 +19,8 @@ use lemmy_utils::error::LemmyError;
 pub async fn search(
   data: Query<Search>,
   context: Data<LemmyContext>,
-  mut local_user_view: Option<LocalUserView>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<Json<SearchResponse>, LemmyError> {
-  local_user_view_from_jwt_opt_new(&mut local_user_view, data.auth.as_ref(), &context).await;
   let local_site = LocalSite::read(&mut context.pool()).await?;
 
   check_private_instance(&local_user_view, &local_site)?;
diff --git a/crates/routes/src/images.rs b/crates/routes/src/images.rs
index 5133cfd8239cb40d3162604c55968db4cf67c9f4..db33414185fd495f661364eb7bdba3f91df3121a 100644
--- a/crates/routes/src/images.rs
+++ b/crates/routes/src/images.rs
@@ -11,15 +11,13 @@ use actix_web::{
   HttpResponse,
 };
 use futures::stream::{Stream, StreamExt};
-use lemmy_api_common::{context::LemmyContext, utils::local_user_view_from_jwt};
-use lemmy_db_schema::{
-  newtypes::LocalUserId,
-  source::{
-    image_upload::{ImageUpload, ImageUploadForm},
-    local_site::LocalSite,
-  },
+use lemmy_api_common::context::LemmyContext;
+use lemmy_db_schema::source::{
+  image_upload::{ImageUpload, ImageUploadForm},
+  local_site::LocalSite,
 };
-use lemmy_utils::{claims::Claims, rate_limit::RateLimitCell, REQWEST_TIMEOUT};
+use lemmy_db_views::structs::LocalUserView;
+use lemmy_utils::{rate_limit::RateLimitCell, REQWEST_TIMEOUT};
 use reqwest::Body;
 use reqwest_middleware::{ClientWithMiddleware, RequestBuilder};
 use serde::{Deserialize, Serialize};
@@ -96,15 +94,10 @@ async fn upload(
   body: web::Payload,
   client: web::Data<ClientWithMiddleware>,
   context: web::Data<LemmyContext>,
+  // require login
+  local_user_view: LocalUserView,
 ) -> Result<HttpResponse, Error> {
   // TODO: check rate limit here
-  let jwt = req.cookie("jwt").ok_or(error::ErrorUnauthorized(
-    "No auth header for picture upload",
-  ))?;
-  let claims = Claims::decode(jwt.value(), &context.secret().jwt_secret);
-  if claims.is_err() {
-    return Ok(HttpResponse::Unauthorized().finish());
-  };
 
   let pictrs_config = context.settings().pictrs_config()?;
   let image_url = format!("{}image", pictrs_config.url);
@@ -123,10 +116,9 @@ async fn upload(
   let status = res.status();
   let images = res.json::<Images>().await.map_err(error::ErrorBadRequest)?;
   if let Some(images) = &images.files {
-    let local_user_id = LocalUserId(claims?.claims.sub);
     for uploaded_image in images {
       let form = ImageUploadForm {
-        local_user_id,
+        local_user_id: local_user_view.local_user.id,
         pictrs_alias: uploaded_image.file.to_string(),
         pictrs_delete_token: uploaded_image.delete_token.to_string(),
       };
@@ -145,21 +137,14 @@ async fn full_res(
   req: HttpRequest,
   client: web::Data<ClientWithMiddleware>,
   context: web::Data<LemmyContext>,
+  local_user_view: Option<LocalUserView>,
 ) -> Result<HttpResponse, Error> {
   // block access to images if instance is private and unauthorized, public
   let local_site = LocalSite::read(&mut context.pool())
     .await
     .map_err(error::ErrorBadRequest)?;
-  if local_site.private_instance {
-    let jwt = req.cookie("jwt").ok_or(error::ErrorUnauthorized(
-      "No auth header for picture access",
-    ))?;
-    if local_user_view_from_jwt(jwt.value(), &context)
-      .await
-      .is_err()
-    {
-      return Ok(HttpResponse::Unauthorized().finish());
-    };
+  if local_site.private_instance && local_user_view.is_none() {
+    return Ok(HttpResponse::Unauthorized().finish());
   }
   let name = &filename.into_inner();
 
@@ -219,6 +204,8 @@ async fn delete(
   req: HttpRequest,
   client: web::Data<ClientWithMiddleware>,
   context: web::Data<LemmyContext>,
+  // require login
+  _local_user_view: LocalUserView,
 ) -> Result<HttpResponse, Error> {
   let (token, file) = components.into_inner();
 
diff --git a/src/session_middleware.rs b/src/session_middleware.rs
index 5824f33df7281367da6a133cbeb03a99b933dfad..c48f6f1c7e576754fa295cb1ba45da3943ed0246 100644
--- a/src/session_middleware.rs
+++ b/src/session_middleware.rs
@@ -6,10 +6,19 @@ use actix_web::{
   Error,
   HttpMessage,
 };
+use chrono::{DateTime, Utc};
 use core::future::Ready;
 use futures_util::future::LocalBoxFuture;
-use lemmy_api_common::{context::LemmyContext, utils::local_user_view_from_jwt};
-use lemmy_utils::error::{LemmyError, LemmyErrorType};
+use lemmy_api_common::{
+  context::LemmyContext,
+  lemmy_db_views::structs::LocalUserView,
+  utils::check_user_valid,
+};
+use lemmy_db_schema::newtypes::LocalUserId;
+use lemmy_utils::{
+  claims::Claims,
+  error::{LemmyError, LemmyErrorExt2, LemmyErrorType},
+};
 use reqwest::header::HeaderValue;
 use std::{future::ready, rc::Rc};
 
@@ -118,3 +127,108 @@ where
     })
   }
 }
+
+#[tracing::instrument(skip_all)]
+async fn local_user_view_from_jwt(
+  jwt: &str,
+  context: &LemmyContext,
+) -> Result<LocalUserView, LemmyError> {
+  let claims = Claims::decode(jwt, &context.secret().jwt_secret)
+    .with_lemmy_type(LemmyErrorType::NotLoggedIn)?
+    .claims;
+  let local_user_id = LocalUserId(claims.sub);
+  let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
+  check_user_valid(
+    local_user_view.person.banned,
+    local_user_view.person.ban_expires,
+    local_user_view.person.deleted,
+  )?;
+
+  check_validator_time(&local_user_view.local_user.validator_time, &claims)?;
+
+  Ok(local_user_view)
+}
+
+/// Checks if user's token was issued before user's password reset.
+fn check_validator_time(validator_time: &DateTime<Utc>, claims: &Claims) -> Result<(), LemmyError> {
+  let user_validation_time = validator_time.timestamp();
+  if user_validation_time > claims.iat {
+    Err(LemmyErrorType::NotLoggedIn)?
+  } else {
+    Ok(())
+  }
+}
+
+#[cfg(test)]
+mod tests {
+  #![allow(clippy::unwrap_used)]
+  #![allow(clippy::indexing_slicing)]
+
+  use super::*;
+  use lemmy_db_schema::{
+    source::{
+      instance::Instance,
+      local_user::{LocalUser, LocalUserInsertForm},
+      person::{Person, PersonInsertForm},
+      secret::Secret,
+    },
+    traits::Crud,
+    utils::build_db_pool_for_tests,
+  };
+  use lemmy_utils::{claims::Claims, settings::SETTINGS};
+  use serial_test::serial;
+  use std::env;
+
+  #[tokio::test]
+  #[serial]
+  async fn test_session_auth() {
+    let pool = &build_db_pool_for_tests().await;
+    let pool = &mut pool.into();
+    let secret = Secret::init(pool).await.unwrap();
+
+    // test.sh sets `LEMMY_CONFIG_LOCATION=../../config/config.hjson` for code under crates folder.
+    // this results in a config not found error, so we need to unset this var and use default.
+    env::remove_var("LEMMY_CONFIG_LOCATION");
+    let settings = &SETTINGS.to_owned();
+
+    let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+      .await
+      .unwrap();
+
+    let new_person = PersonInsertForm::builder()
+      .name("Gerry9812".into())
+      .public_key("pubkey".to_string())
+      .instance_id(inserted_instance.id)
+      .build();
+
+    let inserted_person = Person::create(pool, &new_person).await.unwrap();
+
+    let local_user_form = LocalUserInsertForm::builder()
+      .person_id(inserted_person.id)
+      .password_encrypted("123456".to_string())
+      .build();
+
+    let inserted_local_user = LocalUser::create(pool, &local_user_form).await.unwrap();
+
+    let jwt = Claims::jwt(
+      inserted_local_user.id.0,
+      &secret.jwt_secret,
+      &settings.hostname,
+    )
+    .unwrap();
+    let claims = Claims::decode(&jwt, &secret.jwt_secret).unwrap().claims;
+    let check = check_validator_time(&inserted_local_user.validator_time, &claims);
+    assert!(check.is_ok());
+
+    // The check should fail, since the validator time is now newer than the jwt issue time
+    let updated_local_user =
+      LocalUser::update_password(pool, inserted_local_user.id, "password111")
+        .await
+        .unwrap();
+    let check_after = check_validator_time(&updated_local_user.validator_time, &claims);
+    assert!(check_after.is_err());
+
+    let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
+    assert_eq!(1, num_deleted);
+  }
+}