Skip to content

Commit 2db5828

Browse files
authored
v3.1.0 - Refactor: HttpOnly cookies (#90)
* v3.1.0 - Refactor: HttpOnly cookies - This update aims to provide support for httpOnly cookies in all the schemes and providers for this module. * fix: dev mode base url * fix: remove token on reset
1 parent b346846 commit 2db5828

25 files changed

+478
-174
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nuxt-alt/auth",
3-
"version": "3.0.5",
3+
"version": "3.1.0",
44
"description": "An alternative module to @nuxtjs/auth",
55
"homepage": "https://github.com/nuxt-alt/auth",
66
"author": "Denoder",

src/module.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ModuleOptions } from './types';
2-
import { addImports, addPluginTemplate, addTemplate, createResolver, defineNuxtModule, installModule, addRouteMiddleware } from '@nuxt/kit';
2+
import { addImports, addPluginTemplate, addTemplate, createResolver, defineNuxtModule, installModule, addRouteMiddleware, addServerHandler } from '@nuxt/kit';
33
import { name, version } from '../package.json';
44
import { resolveStrategies } from './resolve';
55
import { moduleDefaults } from './options';
@@ -94,6 +94,14 @@ export default defineNuxtModule({
9494
// Transpile
9595
nuxt.options.build.transpile.push(runtime, providers, utils)
9696

97+
if (nuxt.options.ssr) {
98+
addServerHandler({
99+
route: '/_auth/reset',
100+
method: 'post',
101+
handler: resolver.resolve(runtime, 'token-nitro'),
102+
})
103+
}
104+
97105
// Middleware
98106
if (options.enableMiddleware) {
99107
addRouteMiddleware({

src/resolve.ts

Lines changed: 131 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Strategy, ModuleOptions, ProviderNames, SchemeNames } from './types';
22
import type { Nuxt } from '@nuxt/schema';
3+
import { addAuthorize, addLocalAuthorize, assignAbsoluteEndpoints, assignDefaults } from './utils/provider';
34
import { ProviderAliases } from './runtime/providers';
45
import * as AUTH_PROVIDERS from './runtime/providers';
56
import { resolvePath } from '@nuxt/kit';
@@ -16,6 +17,113 @@ export const BuiltinSchemes = {
1617
auth0: 'Auth0Scheme',
1718
};
1819

20+
export const OAUTH2DEFAULTS = {
21+
accessType: undefined,
22+
redirectUri: undefined,
23+
logoutRedirectUri: undefined,
24+
clientId: undefined,
25+
clientSecretTransport: 'body',
26+
audience: undefined,
27+
grantType: undefined,
28+
responseMode: undefined,
29+
acrValues: undefined,
30+
autoLogout: false,
31+
endpoints: {
32+
logout: undefined,
33+
authorization: undefined,
34+
token: undefined,
35+
userInfo: undefined,
36+
},
37+
scope: [],
38+
token: {
39+
property: 'access_token',
40+
expiresProperty: 'expires_in',
41+
type: 'Bearer',
42+
name: 'Authorization',
43+
maxAge: false,
44+
global: true,
45+
prefix: '_token.',
46+
expirationPrefix: '_token_expiration.',
47+
},
48+
idToken: {
49+
property: 'id_token',
50+
maxAge: 1800,
51+
prefix: '_id_token.',
52+
expirationPrefix: '_id_token_expiration.',
53+
},
54+
refreshToken: {
55+
property: 'refresh_token',
56+
maxAge: 60 * 60 * 24 * 30,
57+
prefix: '_refresh_token.',
58+
expirationPrefix: '_refresh_token_expiration.',
59+
httpOnly: false,
60+
},
61+
user: {
62+
property: false,
63+
},
64+
responseType: 'token',
65+
codeChallengeMethod: false,
66+
clientWindow: false,
67+
clientWindowWidth: 400,
68+
clientWindowHeight: 600
69+
};
70+
71+
export const LOCALDEFAULTS = {
72+
cookie: {
73+
name: undefined
74+
},
75+
endpoints: {
76+
csrf: {
77+
url: '/api/csrf-cookie',
78+
},
79+
login: {
80+
url: '/api/auth/login',
81+
method: 'post',
82+
},
83+
logout: {
84+
url: '/api/auth/logout',
85+
method: 'post',
86+
},
87+
user: {
88+
url: '/api/auth/user',
89+
method: 'get',
90+
},
91+
refresh: {
92+
url: '/api/auth/refresh',
93+
method: 'POST',
94+
},
95+
},
96+
token: {
97+
expiresProperty: 'expires_in',
98+
property: 'token',
99+
type: 'Bearer',
100+
name: 'Authorization',
101+
maxAge: false,
102+
global: true,
103+
required: true,
104+
prefix: '_token.',
105+
expirationPrefix: '_token_expiration.',
106+
},
107+
refreshToken: {
108+
property: 'refresh_token',
109+
data: 'refresh_token',
110+
maxAge: 60 * 60 * 24 * 30,
111+
required: true,
112+
tokenRequired: false,
113+
prefix: '_refresh_token.',
114+
expirationPrefix: '_refresh_token_expiration.',
115+
httpOnly: false,
116+
},
117+
autoLogout: false,
118+
user: {
119+
property: 'user',
120+
autoFetch: true,
121+
},
122+
clientId: false,
123+
grantType: false,
124+
scope: false,
125+
};
126+
19127
export interface ImportOptions {
20128
name: string;
21129
as: string;
@@ -44,13 +152,15 @@ export async function resolveStrategies(nuxt: Nuxt, options: ModuleOptions) {
44152
strategy.provider = strategy.name as ProviderNames;
45153
}
46154

155+
// Determine if SSR is enabled
156+
strategy.ssr = nuxt.options.ssr
157+
47158
// Try to resolve provider
48-
const provider = await resolveProvider(strategy.provider);
159+
const provider = await resolveProvider(strategy.provider, nuxt, strategy);
49160

50161
delete strategy.provider;
51162

52-
// check that the provider isn't a nuxt module
53-
if (typeof provider === 'function') {
163+
if (typeof provider === "function") {
54164
provider(nuxt, strategy);
55165
}
56166

@@ -103,7 +213,7 @@ export async function resolveScheme(scheme: string) {
103213
}
104214
}
105215

106-
export async function resolveProvider(provider: string | ((...args: any[]) => any)) {
216+
export async function resolveProvider(provider: string | ((...args: any[]) => any), nuxt: Nuxt, strategy: Strategy) {
107217

108218
provider = (ProviderAliases[provider as keyof typeof ProviderAliases] || provider);
109219

@@ -113,11 +223,26 @@ export async function resolveProvider(provider: string | ((...args: any[]) => an
113223

114224
// return the provider
115225
if (typeof provider === 'function') {
116-
return provider;
226+
return provider(nuxt, strategy);
117227
}
118228

119229
// return an empty function as it doesn't use a provider
120230
if (typeof provider === 'string') {
121-
return (nuxt, strategy) => {}
231+
return (nuxt: Nuxt, strategy: Strategy) => {
232+
if (['oauth2', 'openIDConnect', 'auth0'].includes(strategy.scheme!) && strategy.ssr) {
233+
assignDefaults(strategy as any, OAUTH2DEFAULTS)
234+
addAuthorize(nuxt, strategy as any, true)
235+
}
236+
237+
if (['refresh', 'local', 'cookie'].includes(strategy.scheme!) && strategy.ssr) {
238+
assignDefaults(strategy as any, LOCALDEFAULTS)
239+
240+
if (strategy.url) {
241+
assignAbsoluteEndpoints(strategy as any);
242+
}
243+
244+
addLocalAuthorize(nuxt, strategy as any)
245+
}
246+
}
122247
}
123248
}

src/runtime/core/auth.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export class Auth {
6767
return redirects;
6868
}
6969

70-
checkTokenValidation() {
70+
#checkTokenValidation() {
7171
this.#tokenValidationInterval = setInterval(async () => {
7272
// Perform scheme checks.
7373
const { valid, tokenExpired, refreshTokenExpired, isRefreshable } = this.check(true);
@@ -195,12 +195,12 @@ export class Auth {
195195
}
196196

197197
if (enableTokenValidation && loggedIn) {
198-
this.checkTokenValidation()
198+
this.#checkTokenValidation()
199199
}
200200
})
201201

202202
if (enableTokenValidation && this.loggedIn) {
203-
this.checkTokenValidation()
203+
this.#checkTokenValidation()
204204
}
205205
}
206206
}
@@ -311,9 +311,7 @@ export class Auth {
311311
this.refreshStrategy.refreshToken.reset();
312312
}
313313

314-
return this.strategy.reset!(
315-
...(args as [options?: { resetInterceptor: boolean }])
316-
);
314+
return this.strategy.reset!(...(args as [options?: { resetInterceptor: boolean }]));
317315
}
318316

319317
async refreshTokens(): Promise<HTTPResponse<any> | void> {
@@ -372,13 +370,24 @@ export class Auth {
372370
return Promise.reject(new Error('[AUTH] add the @nuxt-alt/http module to nuxt.config file'));
373371
}
374372

375-
return this.ctx.$http.raw(request).catch((error: Error) => {
376-
// Call all error handlers
377-
this.callOnError(error, { method: 'request' });
373+
if (process.server && this.ctx.ssrContext) {
374+
return this.ctx.ssrContext.event.$http.raw(request).catch((error: Error) => {
375+
// Call all error handlers
376+
this.callOnError(error, { method: 'request' });
377+
378+
// Throw error
379+
return Promise.reject(error);
380+
});
381+
} else {
382+
return this.ctx.$http.raw(request).catch((error: Error) => {
383+
// Call all error handlers
384+
this.callOnError(error, { method: 'request' });
385+
386+
// Throw error
387+
return Promise.reject(error);
388+
});
389+
}
378390

379-
// Throw error
380-
return Promise.reject(error);
381-
});
382391
}
383392

384393
async requestWith(endpoint?: HTTPRequest, defaults?: HTTPRequest): Promise<HTTPResponse<any>> {

src/runtime/core/storage.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,13 @@ export class Storage {
1313
#initPiniaStore!: AuthStore;
1414
#initStore!: Ref<AuthState>;
1515
state: AuthState;
16-
#state: AuthState;
16+
memory!: Ref<AuthState>;
1717
#piniaEnabled: boolean = false;
1818

1919
constructor(ctx: NuxtApp, options: ModuleOptions) {
2020
this.ctx = ctx;
2121
this.options = options;
2222
this.state = options.initialState!
23-
this.#state = options.initialState!
2423

2524
this.#initState();
2625
}
@@ -104,7 +103,7 @@ export class Storage {
104103
async #initState() {
105104
// Use pinia for local state's if possible
106105
const pinia = this.ctx.$pinia as Pinia
107-
this.#piniaEnabled = this.options.stores.pinia!.enabled && !!pinia;
106+
this.#piniaEnabled = this.options.stores.pinia!?.enabled! && !!pinia;
108107

109108
if (this.#piniaEnabled) {
110109
const { defineStore } = await import('pinia')
@@ -121,6 +120,8 @@ export class Storage {
121120

122121
this.state = this.#initStore.value
123122
}
123+
124+
this.memory = useState<AuthState>('auth-internal', () => ({}))
124125
}
125126

126127
get pinia() {
@@ -133,7 +134,7 @@ export class Storage {
133134

134135
setState(key: string, value: any) {
135136
if (key.startsWith('_')) {
136-
this.#state[key] = value;
137+
this.memory.value[key] = value;
137138
}
138139
else if (this.#piniaEnabled) {
139140
this.#initPiniaStore.$patch({ [key]: value });
@@ -149,7 +150,7 @@ export class Storage {
149150
if (!key.startsWith('_')) {
150151
return this.state[key];
151152
} else {
152-
return this.#state[key];
153+
return this.memory.value[key];
153154
}
154155
}
155156

0 commit comments

Comments
 (0)