Jwt rough setup

This commit is contained in:
2026-03-20 18:54:27 +00:00
parent 5271343a25
commit 2374574220
7 changed files with 81 additions and 36 deletions

View File

@@ -50,17 +50,23 @@
import { onMounted } from 'vue'
import '@/assets/css/app-theme.css'
import SessionTimeout from './components/SessionTimeout.vue'
const authCookie = useCookie('.AspNetCore.Identity.Application')
const isLoggedIn = useState('isLoggedIn', () => false)
onMounted(() => {
if (authCookie.value) isLoggedIn.value = true
if (import.meta.client) {
const token = localStorage.getItem('auth_token')
if (token) {
isLoggedIn.value = true
}
}
})
const logout = () => {
authCookie.value = null
isLoggedIn.value = false
if (import.meta.client) localStorage.removeItem('token')
if (import.meta.client) {
localStorage.removeItem('auth_token')
localStorage.removeItem('token')
}
navigateTo('/login')
}
</script>

View File

@@ -145,23 +145,27 @@ const handleAuth = async () => {
authLoading.value = true
const endpoint = isLogin.value ? 'api/auth/login' : 'api/auth/register'
const url = isLogin.value
? `${config.public.apiBase}${endpoint}?useCookies=true&useSessionCookies=false`
: `${config.public.apiBase}${endpoint}`
const url = `${config.public.apiBase}${endpoint}`
try {
const response = await $fetch(url, {
method: 'POST',
body: {
email: email.value,
password: password.value
},
credentials: 'include'
password: password.value,
useCookies: false,
useSessionCookies: false
}
})
if (isLogin.value) {
isLoggedIn.value = true
navigateTo('/')
if (response.accessToken) {
localStorage.setItem('auth_token', response.accessToken)
isLoggedIn.value = true
navigateTo('/')
} else {
throw new Error('Token not received')
}
} else {
isLogin.value = true
authLoading.value = false

View File

@@ -3,18 +3,33 @@ export default defineNuxtPlugin((nuxtApp) => {
const isLoggedIn = useState('isLoggedIn');
nuxtApp.hook('app:created', () => {
const originalFetch = globalThis.$fetch;
globalThis.$fetch = originalFetch.create({
onRequest({ options }) {
if (import.meta.client) {
const token = localStorage.getItem('auth_token');
if (token) {
const headers = new Headers(options.headers);
headers.set('Authorization', `Bearer ${token}`);
options.headers = headers;
}
}
},
onResponseError({ response }) {
if (response.status === 401) {
console.warn("Session Interceptor: Caught 401 Unauthorized.");
const route = useRoute();
if (route.path !== '/login') {
isLoggedIn.value = false;
if (import.meta.client) {
localStorage.removeItem('auth_token');
}
showTimeout.value = true;
}
}

View File

@@ -21,8 +21,8 @@ vi.stubGlobal('$fetch', mockFetch)
const mockNavigate = vi.fn()
vi.stubGlobal('navigateTo', mockNavigate)
vi.stubGlobal('localStorage', { setItem: vi.fn(), getItem: vi.fn(), removeItem: vi.fn() })
// Mock Nuxt's useState
vi.stubGlobal('useState', () => ({ value: false }))
describe('LoginPage.vue', () => {
@@ -37,15 +37,12 @@ describe('LoginPage.vue', () => {
it('switches between Login and Register modes', async () => {
const wrapper = mount(LoginPage, mountOptions)
// Default is Login
expect(wrapper.find('h1').text()).toBe('Sign In')
expect(wrapper.find('input[label="Confirm Password"]').exists()).toBe(false)
// Click toggle
await wrapper.find('.auth-toggle-btn').trigger('click')
expect(wrapper.find('h1').text()).toBe('Join Us')
// V-expand-transition might need a tick or we check the v-if logic
expect(wrapper.vm.isLogin).toBe(false)
})
@@ -65,7 +62,7 @@ describe('LoginPage.vue', () => {
})
it('calls login API and redirects on success', async () => {
mockFetch.mockResolvedValueOnce({ token: 'fake-token' })
mockFetch.mockResolvedValueOnce({ accessToken: 'fake-token' })
const wrapper = mount(LoginPage, mountOptions)
const vm = wrapper.vm as any