Skip to content

Commit 392a7c5

Browse files
committed
feat: add support for token authentication mode
1 parent 65efc1a commit 392a7c5

File tree

9 files changed

+95
-35
lines changed

9 files changed

+95
-35
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,5 @@
5959
"vitest": "^2.0.5",
6060
"vue-tsc": "^2.1.2"
6161
},
62-
"packageManager": "pnpm@9.6.0+sha512.38dc6fba8dba35b39340b9700112c2fe1e12f10b17134715a4aa98ccf7bb035e76fd981cf0bb384dfa98f8d6af5481c2bef2f4266a24bfa20c34eb7147ce0b5e"
62+
"packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c"
6363
}

src/module.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
addImportsDir,
77
} from '@nuxt/kit'
88
import { defu } from 'defu'
9-
import type { ModuleOptions } from './runtime/types'
9+
import type { ModuleOptions } from './runtime/types/options'
10+
import { registerTypeTemplates } from './templates'
1011

1112
const MODULE_NAME = 'nuxt-laravel-echo'
1213

@@ -51,6 +52,8 @@ export default defineNuxtModule<ModuleOptions>({
5152
addPlugin(resolver.resolve('./runtime/plugin.client'))
5253
addImportsDir(resolver.resolve('./runtime/composables'))
5354

55+
registerTypeTemplates(resolver)
56+
5457
logger.info('Laravel Echo module initialized!')
5558
},
5659
})
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { EchoAppConfig } from '../types/config'
2+
import { useAppConfig } from '#app'
3+
4+
export const useEchoAppConfig = (): EchoAppConfig => {
5+
return (useAppConfig().echo ?? {}) as EchoAppConfig
6+
}

src/runtime/composables/useEchoConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ModuleOptions } from '../types'
1+
import type { ModuleOptions } from '../types/options'
22
import { useRuntimeConfig } from '#imports'
33

44
export const useEchoConfig = (): ModuleOptions => {

src/runtime/plugin.client.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import type { ChannelAuthorizationData } from 'pusher-js/types/src/core/auth/opt
44
import { type ConsolaInstance, createConsola } from 'consola'
55
import type { FetchOptions } from 'ofetch'
66
import { useEchoConfig } from './composables/useEchoConfig'
7-
import type { Authentication, ModuleOptions } from './types'
8-
import { createError, defineNuxtPlugin, useCookie } from '#app'
7+
import type { Authentication, ModuleOptions } from './types/options'
8+
import { useEchoAppConfig } from './composables/useEchoAppConfig'
9+
import { createError, defineNuxtPlugin, useCookie, updateAppConfig, type NuxtApp } from '#app'
910

1011
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1112
const Pusher = (PusherPkg as any).default || PusherPkg
@@ -26,6 +27,7 @@ function createEchoLogger(logLevel: number) {
2627
const readCsrfCookie = (name: string) => useCookie(name, { readonly: true })
2728

2829
function createFetchClient(
30+
app: NuxtApp,
2931
authentication: Required<Authentication>,
3032
logger: ConsolaInstance
3133
) {
@@ -35,7 +37,6 @@ function createFetchClient(
3537
retry: false,
3638

3739
async onRequest(context) {
38-
// todo: move this to interceptors
3940
if (authentication.mode === 'cookie') {
4041
let csrfToken = readCsrfCookie(authentication.csrfCookie)
4142

@@ -57,11 +58,21 @@ function createFetchClient(
5758
context.options.headers.set(authentication.csrfHeader, csrfToken.value)
5859
}
5960

60-
// todo: move this to interceptors
6161
if (authentication.mode === 'token') {
62-
const { tokenStorage } = useAppConfig().echo.authentication
63-
const token = await tokenStorage.get()
64-
context.options.headers.set('Authorization', 'Bearer ' + token)
62+
const { tokenStorage } = useEchoAppConfig()
63+
64+
if (!tokenStorage) {
65+
throw createError('Token storage is not defined')
66+
}
67+
68+
const token = await tokenStorage.get(app)
69+
70+
if (!token) {
71+
logger.debug('Authorization token is missing, unable to set header')
72+
return
73+
}
74+
75+
context.options.headers.set('Authorization', `Bearer ${token}`)
6576
}
6677
},
6778
}
@@ -70,10 +81,11 @@ function createFetchClient(
7081
}
7182

7283
function createAuthorizer(
84+
app: NuxtApp,
7385
authentication: Required<Authentication>,
7486
logger: ConsolaInstance
7587
) {
76-
const client = createFetchClient(authentication, logger)
88+
const client = createFetchClient(app, authentication, logger)
7789

7890
return (channel: Channel, _: Options) => {
7991
return {
@@ -96,12 +108,13 @@ function createAuthorizer(
96108
}
97109
}
98110

99-
function prepareEchoOptions(config: ModuleOptions, logger: ConsolaInstance) {
111+
function prepareEchoOptions(app: NuxtApp, config: ModuleOptions, logger: ConsolaInstance) {
100112
const forceTLS = config.scheme === 'https'
101113
const additionalOptions = config.properties || {}
102114

103115
const authorizer = config.authentication
104116
? createAuthorizer(
117+
app,
105118
config.authentication as Required<Authentication>,
106119
logger
107120
)
@@ -147,25 +160,24 @@ async function setupDefaultTokenStorage(nuxtApp: NuxtApp, logger: ConsolaInstanc
147160
nuxtApp.runWithContext(() => {
148161
updateAppConfig({
149162
echo: {
150-
authentication: {
151-
tokenStorage: defaultStorage.cookieTokenStorage,
152-
}
163+
tokenStorage: defaultStorage.cookieTokenStorage,
153164
},
154165
})
155166
})
156167
}
157168

158169
export default defineNuxtPlugin(async (_nuxtApp) => {
170+
const nuxtApp = _nuxtApp as NuxtApp
159171
const config = useEchoConfig()
160-
const appConfig = useAppConfig()
172+
const appConfig = useEchoAppConfig()
161173
const logger = createEchoLogger(config.logLevel)
162174

163-
if (config.authentication?.mode === 'token' && !appConfig.echo?.authentication?.tokenStorage) {
164-
await setupDefaultTokenStorage(_nuxtApp, logger)
175+
if (config.authentication?.mode === 'token' && !appConfig.tokenStorage) {
176+
await setupDefaultTokenStorage(nuxtApp, logger)
165177
}
166178

167179
window.Pusher = Pusher
168-
window.Echo = new Echo(prepareEchoOptions(config, logger))
180+
window.Echo = new Echo(prepareEchoOptions(nuxtApp, config, logger))
169181

170182
logger.debug('Laravel Echo client initialized')
171183

src/runtime/storages/cookieTokenStorage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { unref } from 'vue'
2-
import type { TokenStorage } from '~/src/runtime/types'
2+
import type { TokenStorage } from '../types/config'
33
import { useCookie, type NuxtApp } from '#app'
44

55
const cookieTokenKey = 'sanctum.token.cookie'

src/runtime/types/config.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { NuxtApp } from '#app'
2+
3+
/**
4+
* Handlers to work with authentication token.
5+
*/
6+
export interface TokenStorage {
7+
/**
8+
* Function to load a token from the storage.
9+
*/
10+
get: (app: NuxtApp) => Promise<string | undefined>
11+
/**
12+
* Function to save a token to the storage.
13+
*/
14+
set: (app: NuxtApp, token?: string) => Promise<void>
15+
}
16+
17+
/**
18+
* Echo configuration for the application side with user-defined handlers.
19+
*/
20+
export interface EchoAppConfig {
21+
/**
22+
* Token storage handlers to be used by the client.
23+
*/
24+
tokenStorage?: TokenStorage
25+
}

src/runtime/types.ts renamed to src/runtime/types/options.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ export interface Authentication {
2929
* @default 'X-XSRF-TOKEN'
3030
*/
3131
csrfHeader?: string
32-
/**
33-
* Token storage handlers to be used by the client.
34-
*/
35-
tokenStorage?: TokenStorage
3632
}
3733

3834
export interface ModuleOptions {
@@ -95,14 +91,3 @@ export interface ModuleOptions {
9591
*/
9692
properties?: object
9793
}
98-
99-
export interface TokenStorage {
100-
/**
101-
* Function to load a token from the storage.
102-
*/
103-
get: (app: NuxtApp) => Promise<string | undefined>
104-
/**
105-
* Function to save a token to the storage.
106-
*/
107-
set: (app: NuxtApp, token?: string) => Promise<void>
108-
}

src/templates.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { addTypeTemplate, type Resolver } from '@nuxt/kit'
2+
3+
export const registerTypeTemplates = (resolver: Resolver) => {
4+
addTypeTemplate({
5+
filename: 'types/echo.d.ts',
6+
getContents: () => `// Generated by nuxt-laravel-echo module
7+
import type { EchoAppConfig } from '${resolver.resolve('./runtime/types/config.ts')}';
8+
9+
declare module 'nuxt/schema' {
10+
interface AppConfig {
11+
echo?: EchoAppConfig;
12+
}
13+
interface AppConfigInput {
14+
echo?: EchoAppConfig;
15+
}
16+
}
17+
18+
declare module '@nuxt/schema' {
19+
interface AppConfig {
20+
echo?: EchoAppConfig;
21+
}
22+
interface AppConfigInput {
23+
echo?: EchoAppConfig;
24+
}
25+
}
26+
27+
export {};`,
28+
})
29+
}

0 commit comments

Comments
 (0)