refactor(auth): migrate fully to HTTP-only cookie sessions
Remove client-side token storage and Authorization header injection Backend login now only returns user data, omitting the session token Remove Authorization from backend CORS allowed headers Clean up obsolete VITE_* environment variable fallbacks Update Modal component to use Vue useId() instead of Math.random()
This commit is contained in:
@@ -11,7 +11,7 @@ import Skeleton from '../components/Skeleton.vue';
|
||||
import Tabs, { type TabOption } from '../components/Tabs.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import { iconAdd, iconArtifact } from '../icons';
|
||||
import { api, getAuthToken, type AncientArtifact, type AuthUser, type ListPage, type Options } from '../services/api';
|
||||
import { api, type AncientArtifact, type AuthUser, type ListPage, type Options } from '../services/api';
|
||||
import ItemEdit from './ItemEdit.vue';
|
||||
|
||||
const route = useRoute();
|
||||
@@ -150,12 +150,10 @@ function loadMoreArtifacts() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!options.value) {
|
||||
try {
|
||||
|
||||
@@ -13,7 +13,6 @@ import TranslationFields from '../components/TranslationFields.vue';
|
||||
import { iconAdd, iconCancel, iconDelete, iconDish, iconEdit, iconItem, iconSave } from '../icons';
|
||||
import {
|
||||
api,
|
||||
getAuthToken,
|
||||
type AuthUser,
|
||||
type Dish,
|
||||
type DishCategory,
|
||||
@@ -301,12 +300,10 @@ async function loadEditorOptions() {
|
||||
|
||||
async function loadPage() {
|
||||
loading.value = true;
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
await Promise.all([initialCategoriesLoaded.value ? Promise.resolve() : loadDish(), loadEditorOptions()]);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import LoadMoreSentinel from '../components/LoadMoreSentinel.vue';
|
||||
import PageHeader from '../components/PageHeader.vue';
|
||||
import Skeleton from '../components/Skeleton.vue';
|
||||
import { iconAdd, iconHabitat } from '../icons';
|
||||
import { api, getAuthToken, type AuthUser, type Habitat, type ListPage } from '../services/api';
|
||||
import { api, type AuthUser, type Habitat, type ListPage } from '../services/api';
|
||||
import HabitatEdit from './HabitatEdit.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -125,12 +125,10 @@ function loadMoreHabitats() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!initialPageLoaded.value) {
|
||||
await loadHabitats();
|
||||
|
||||
@@ -11,7 +11,7 @@ import Skeleton from '../components/Skeleton.vue';
|
||||
import Tabs, { type TabOption } from '../components/Tabs.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import { iconAdd, iconChevronDown, iconChevronUp, iconItem } from '../icons';
|
||||
import { api, getAuthToken, type AuthUser, type Item, type ListPage, type Options } from '../services/api';
|
||||
import { api, type AuthUser, type Item, type ListPage, type Options } from '../services/api';
|
||||
import ItemEdit from './ItemEdit.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -527,12 +527,10 @@ function loadMoreItems() {
|
||||
onMounted(async () => {
|
||||
document.addEventListener('pointerdown', onCreateDefaultsDocumentPointerDown);
|
||||
document.addEventListener('keydown', onDocumentKeydown);
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!options.value) {
|
||||
try {
|
||||
|
||||
@@ -28,10 +28,8 @@ import {
|
||||
} from '../icons';
|
||||
import {
|
||||
api,
|
||||
getAuthToken,
|
||||
moderationUpdateEvent,
|
||||
onAuthTokenChange,
|
||||
setAuthToken,
|
||||
onAuthChange,
|
||||
type AiModerationStatus,
|
||||
type AuthUser,
|
||||
type CommentSort,
|
||||
@@ -112,17 +110,11 @@ function summaryText(value: string, maxLength: number) {
|
||||
}
|
||||
|
||||
async function loadCurrentUser() {
|
||||
if (!getAuthToken()) {
|
||||
currentUser.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.me();
|
||||
currentUser.value = response.user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
setAuthToken(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,14 +832,13 @@ onMounted(() => {
|
||||
document.addEventListener('click', closeReactionPickerFromDocument);
|
||||
document.addEventListener('keydown', closeReactionPickerFromKeyboard);
|
||||
window.addEventListener(moderationUpdateEvent, handleModerationUpdate);
|
||||
const hadAuthToken = getAuthToken() !== null;
|
||||
void (async () => {
|
||||
await loadCurrentUser();
|
||||
if (!initialPostLoaded.value || hadAuthToken) {
|
||||
if (!initialPostLoaded.value || currentUser.value) {
|
||||
await loadPost();
|
||||
}
|
||||
})();
|
||||
removeAuthListener = onAuthTokenChange(() => {
|
||||
removeAuthListener = onAuthChange(() => {
|
||||
void loadCurrentUser();
|
||||
void loadPost();
|
||||
});
|
||||
|
||||
@@ -35,10 +35,8 @@ import {
|
||||
} from '../icons';
|
||||
import {
|
||||
api,
|
||||
getAuthToken,
|
||||
moderationUpdateEvent,
|
||||
onAuthTokenChange,
|
||||
setAuthToken,
|
||||
onAuthChange,
|
||||
type AiModerationStatus,
|
||||
type AuthUser,
|
||||
type CommentSort,
|
||||
@@ -252,20 +250,12 @@ const submitLabel = computed(() => {
|
||||
async function loadCurrentUser() {
|
||||
authReady.value = false;
|
||||
|
||||
if (!getAuthToken()) {
|
||||
currentUser.value = null;
|
||||
activeFeedScope.value = 'all';
|
||||
authReady.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.me();
|
||||
currentUser.value = response.user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
activeFeedScope.value = 'all';
|
||||
setAuthToken(null);
|
||||
} finally {
|
||||
authReady.value = true;
|
||||
}
|
||||
@@ -1376,7 +1366,6 @@ onMounted(() => {
|
||||
document.addEventListener('click', closeReactionPickerFromDocument);
|
||||
document.addEventListener('keydown', closeReactionPickerFromKeyboard);
|
||||
window.addEventListener(moderationUpdateEvent, handleModerationUpdate);
|
||||
const hadAuthToken = getAuthToken() !== null;
|
||||
void (async () => {
|
||||
await loadCurrentUser();
|
||||
if (!initialLanguagesLoaded.value) {
|
||||
@@ -1387,12 +1376,12 @@ onMounted(() => {
|
||||
await loadLifeCategories();
|
||||
initialOptionsLoaded.value = true;
|
||||
}
|
||||
if (!initialPostsLoaded.value || hadAuthToken) {
|
||||
if (!initialPostsLoaded.value || currentUser.value) {
|
||||
await loadPosts();
|
||||
initialPostsLoaded.value = true;
|
||||
}
|
||||
})();
|
||||
removeAuthListener = onAuthTokenChange(() => {
|
||||
removeAuthListener = onAuthChange(() => {
|
||||
void (async () => {
|
||||
await loadCurrentUser();
|
||||
await loadPosts();
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useRoute, useRouter } from 'vue-router';
|
||||
import PageHeader from '../components/PageHeader.vue';
|
||||
import StatusMessage from '../components/StatusMessage.vue';
|
||||
import { iconLogin } from '../icons';
|
||||
import { api, setAuthToken } from '../services/api';
|
||||
import { api, notifyAuthChange } from '../services/api';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -22,12 +22,12 @@ async function submitLogin() {
|
||||
errorMessage.value = '';
|
||||
|
||||
try {
|
||||
const response = await api.login({
|
||||
await api.login({
|
||||
email: email.value,
|
||||
password: password.value,
|
||||
rememberMe: rememberMe.value
|
||||
});
|
||||
setAuthToken(response.token, { persistent: rememberMe.value });
|
||||
notifyAuthChange();
|
||||
|
||||
const redirect =
|
||||
typeof route.query.redirect === 'string' && route.query.redirect.startsWith('/')
|
||||
|
||||
@@ -10,7 +10,7 @@ import PageHeader from '../components/PageHeader.vue';
|
||||
import Skeleton from '../components/Skeleton.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import { iconAdd } from '../icons';
|
||||
import { api, getAuthToken, type AuthUser, type ListPage, type Options, type Pokemon } from '../services/api';
|
||||
import { api, type AuthUser, type ListPage, type Options, type Pokemon } from '../services/api';
|
||||
import PokemonEdit from './PokemonEdit.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -158,12 +158,10 @@ function pokemonCardImage(item: Pokemon) {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!options.value) {
|
||||
try {
|
||||
|
||||
@@ -12,7 +12,7 @@ import Skeleton from '../components/Skeleton.vue';
|
||||
import Tabs, { type TabOption } from '../components/Tabs.vue';
|
||||
import { iconBack, iconEdit, iconRecipe } from '../icons';
|
||||
import { applySeo, resolvedSeoHead, resolveSeo } from '../seo';
|
||||
import { api, getAuthToken, type AuthUser, type RecipeDetail } from '../services/api';
|
||||
import { api, type AuthUser, type RecipeDetail } from '../services/api';
|
||||
import RecipeEdit from './RecipeEdit.vue';
|
||||
|
||||
const route = useRoute();
|
||||
@@ -96,12 +96,10 @@ async function loadRecipeDetail() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!initialRecipeLoaded.value) {
|
||||
await loadRecipeDetail();
|
||||
|
||||
@@ -8,7 +8,7 @@ import Skeleton from '../components/Skeleton.vue';
|
||||
import StatusMessage from '../components/StatusMessage.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import { iconAdd, iconCancel, iconDelete, iconSave } from '../icons';
|
||||
import { api, getAuthToken, type AuthUser, type ConfigType, type Item, type Options, type RecipePayload } from '../services/api';
|
||||
import { api, type AuthUser, type ConfigType, type Item, type Options, type RecipePayload } from '../services/api';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -105,11 +105,6 @@ async function loadOptions() {
|
||||
}
|
||||
|
||||
async function loadCurrentUser() {
|
||||
if (!getAuthToken()) {
|
||||
currentUser.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
|
||||
@@ -11,7 +11,7 @@ import Skeleton from '../components/Skeleton.vue';
|
||||
import Tabs, { type TabOption } from '../components/Tabs.vue';
|
||||
import TagsSelect from '../components/TagsSelect.vue';
|
||||
import { iconAdd, iconNoRecipe, iconRecipe } from '../icons';
|
||||
import { api, getAuthToken, type AuthUser, type Item, type ListPage, type Options } from '../services/api';
|
||||
import { api, type AuthUser, type Item, type ListPage, type Options } from '../services/api';
|
||||
import RecipeEdit from './RecipeEdit.vue';
|
||||
|
||||
const options = ref<Options | null>(null);
|
||||
@@ -170,12 +170,10 @@ function loadMoreItems() {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (getAuthToken()) {
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
try {
|
||||
currentUser.value = (await api.me()).user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
}
|
||||
if (!options.value) {
|
||||
try {
|
||||
|
||||
@@ -26,9 +26,7 @@ import {
|
||||
} from '../icons';
|
||||
import {
|
||||
api,
|
||||
getAuthToken,
|
||||
notifyAuthChange,
|
||||
setAuthToken,
|
||||
type AuthUser,
|
||||
type DiscussionEntityType,
|
||||
type LifePost,
|
||||
@@ -229,7 +227,6 @@ feeds.value = initialPublicProfile.value.feeds?.items ?? [];
|
||||
feedsCursor.value = initialPublicProfile.value.feeds?.nextCursor ?? null;
|
||||
feedsHasMore.value = initialPublicProfile.value.feeds?.hasMore ?? false;
|
||||
const initialPublicProfileLoaded = ref(initialPublicProfile.value.profile !== null);
|
||||
const initialFeedsLoaded = ref(initialPublicProfile.value.feeds !== null);
|
||||
loading.value = !initialPublicProfileLoaded.value;
|
||||
const profileSeo = computed(() =>
|
||||
profile.value && !isAccountRoute.value
|
||||
@@ -324,18 +321,12 @@ function resetActivity() {
|
||||
}
|
||||
|
||||
async function loadOptionalCurrentUser() {
|
||||
if (!getAuthToken()) {
|
||||
currentUser.value = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.me();
|
||||
currentUser.value = response.user;
|
||||
return response.user;
|
||||
} catch {
|
||||
currentUser.value = null;
|
||||
setAuthToken(null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -723,11 +714,7 @@ function commentTargetTitle(comment: UserCommentActivity): string {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (isAccountRoute.value || getAuthToken() || !initialPublicProfileLoaded.value) {
|
||||
void loadProfile();
|
||||
} else if (!initialFeedsLoaded.value) {
|
||||
void loadFeeds(true);
|
||||
}
|
||||
void loadProfile();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user