Skip to content

Commit

Permalink
Add auth token revalidation
Browse files Browse the repository at this point in the history
  • Loading branch information
Dobefu committed Apr 20, 2024
1 parent 39a4b7d commit 6533e77
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 35 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"require": {
"composer/installers": "^2.0",
"cweagans/composer-patches": "~1.0",
"dobefu/nuxtify_profile": "^1.0.0-alpha5",
"dobefu/nuxtify_profile": "^1.0.x-dev",
"drupal/core-composer-scaffold": "^10.2",
"drupal/core-recommended": "^10"
},
Expand Down
9 changes: 6 additions & 3 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions frontend/base/components/base/LanguageSelector.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<script setup lang="ts">
const switchLocalePath = useSwitchLocalePath()
const { locale, t } = useI18n()
const { locale: initialLocale, t } = useI18n()
const locale = ref(initialLocale.value)
watch(
() => locale.value,
() => {
return navigateTo(switchLocalePath(locale.value))
locale,
async () => {
return await navigateTo(switchLocalePath(locale.value))
},
)
Expand Down
38 changes: 33 additions & 5 deletions frontend/base/composables/useAuth.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
export default async function useAuth() {
const { locale } = useI18n()
import process from 'node:process'

export default async function useAuth() {
const user = useState<User | undefined>('user')

const getUser = async (): Promise<User | undefined> => {
if (process.client)
return

const uid = useCookie('auth.uid')
const token = useCookie('auth.token')

Expand Down Expand Up @@ -31,7 +34,7 @@ export default async function useAuth() {
user.value = await getUser()
})

const signIn = async (email: string, password: string) => {
const signIn = async (email: string, password: string, locale: string) => {
if (!email.length || !password.length) {
return {
data: null,
Expand All @@ -46,7 +49,7 @@ export default async function useAuth() {
'Content-Type': 'application/json',
},
body: JSON.stringify({
langcode: locale.value,
langcode: locale,
email,
password,
}),
Expand Down Expand Up @@ -82,6 +85,7 @@ export default async function useAuth() {
email: string,
password: string,
password_confirm: string,
locale: string,
) => {
if (!email.length || !password.length) {
return {
Expand All @@ -104,7 +108,7 @@ export default async function useAuth() {
'Content-Type': 'application/json',
},
body: JSON.stringify({
langcode: locale.value,
langcode: locale,
email,
password,
password_confirm,
Expand Down Expand Up @@ -176,6 +180,29 @@ export default async function useAuth() {
}
}

const refresh = async () => {
const token = useCookie('auth.token')

try {
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
token: token.value,
}),
})

if (!response || Math.floor(response.status / 100) !== 2) {
const json = await response.json()

throw new Error(json.message)
}
}
catch (error) {}
}

const signOut = async () => {
const uid = useCookie('auth.uid')
const token = useCookie('auth.token')
Expand All @@ -191,6 +218,7 @@ export default async function useAuth() {
signIn,
signUp,
verify,
refresh,
signOut,
}
}
25 changes: 9 additions & 16 deletions frontend/base/middleware/auth.global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,26 @@ interface routeAuthMeta {

export default defineNuxtRouteMiddleware(async (to, _from) => {
const routeAuthMeta = to.meta.auth as routeAuthMeta
const { user, refresh } = await useAuth()

const uid = useCookie('auth.uid')
const token = useCookie('auth.token')

if (!user.value) {
uid.value = undefined
token.value = undefined
}

if (user)
await refresh()

if (!uid.value || !token.value) {
if (routeAuthMeta?.authOnly)
return navigateTo('/user/login')

return
}

let user

try {
user = await $fetch('/api/auth/session', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ uid: uid.value, token: token.value }),
})
}
catch (error) {
uid.value = undefined
token.value = undefined
}

if (!user) {
if (routeAuthMeta?.authOnly)
return navigateTo('/user/login')
Expand Down
3 changes: 2 additions & 1 deletion frontend/base/pages/user/login/index.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
const { t } = useI18n()
const { t, locale } = useI18n()
const localePath = useLocalePath()
const isLoading = ref(false)
Expand All @@ -18,6 +18,7 @@ async function login(e: SubmitEvent) {
const { error } = await signIn(
email.value,
password.value,
locale.value,
)
if (error)
Expand Down
3 changes: 2 additions & 1 deletion frontend/base/pages/user/register/index.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
const { t } = useI18n()
const { t, locale } = useI18n()
const localePath = useLocalePath()
const isLoading = ref(false)
Expand All @@ -20,6 +20,7 @@ async function register(e: SubmitEvent) {
email.value,
password.value,
password_confirm.value,
locale.value,
)
if (error)
Expand Down
7 changes: 6 additions & 1 deletion frontend/base/server/api/auth/login.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import { type H3Event, eventHandler, readBody } from 'h3'
export default eventHandler(async (event: H3Event) => {
const body = await readBody(event)

const token = await $fetch(`${process.env.NUXT_PUBLIC_BACKEND_URL}/session/token`)
const tokenResponse = await fetch(`${process.env.NUXT_PUBLIC_BACKEND_URL}/session/token`)

if (!tokenResponse || tokenResponse.status !== 200)
return new Response('Unauthorized', { status: 401 })

const token = await tokenResponse.text()

if (!token)
return new Response('Unauthorized', { status: 401 })
Expand Down
62 changes: 62 additions & 0 deletions frontend/base/server/api/auth/refresh.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import process from 'node:process'
import { type H3Event, eventHandler, readBody } from 'h3'

export default eventHandler(async (event: H3Event) => {
const body = await readBody(event)

const tokenResponse = await fetch(`${process.env.NUXT_PUBLIC_BACKEND_URL}/session/token`)

if (!tokenResponse || tokenResponse.status !== 200)
return new Response('Unauthorized', { status: 401 })

const token = await tokenResponse.text()

if (!token)
return new Response('Unauthorized', { status: 401 })

try {
const response = await fetch(
`${process.env.NUXT_PUBLIC_BACKEND_URL}/jwt/token`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': token as string,
'Authorization': `Bearer ${body.token}`,
},
body: JSON.stringify({
name: body.email,
pass: body.password,
preferred_langcode: [{ value: body.langcode }],
}),
},
)

if (!response || response.status !== 200) {
const json = await response.json()

if (json.message) {
return new Response(JSON.stringify({
message: json.message,
}), { status: 422 })
}

return new Response('Unauthorized', { status: 401 })
}

const jwtToken = await response.json()

if (!jwtToken.token)
return new Response('Unauthorized', { status: 401 })

setCookie(event, 'auth.token', jwtToken.token)

return {
token: jwtToken.token,
}
}
catch (error) {
console.error(error)
return new Response('Unauthorized', { status: 401 })
}
})
9 changes: 6 additions & 3 deletions frontend/base/server/api/auth/session.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ export default eventHandler(async (event): Promise<User | undefined> => {
const uid = body.uid

try {
const user = await $fetch<{
[key: string]: { value: string }[]
}>(`${process.env.NUXT_PUBLIC_BACKEND_URL}/user/${uid}?_format=json`, {
const response = await fetch(`${process.env.NUXT_PUBLIC_BACKEND_URL}/user/${uid}?_format=json`, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
})

if (!response || response.status !== 200)
return

const user = await response.json()

return {
id: uid,
email: user.mail[0].value,
Expand Down

0 comments on commit 6533e77

Please sign in to comment.