@peterbud/nuxt-aegis 1.1.0-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -0
- package/dist/module.d.mts +6 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +354 -0
- package/dist/runtime/app/composables/useAuth.d.ts +85 -0
- package/dist/runtime/app/composables/useAuth.js +187 -0
- package/dist/runtime/app/middleware/auth-logged-in.d.ts +16 -0
- package/dist/runtime/app/middleware/auth-logged-in.js +25 -0
- package/dist/runtime/app/middleware/auth-logged-out.d.ts +20 -0
- package/dist/runtime/app/middleware/auth-logged-out.js +17 -0
- package/dist/runtime/app/pages/AuthCallback.d.vue.ts +3 -0
- package/dist/runtime/app/pages/AuthCallback.vue +92 -0
- package/dist/runtime/app/pages/AuthCallback.vue.d.ts +3 -0
- package/dist/runtime/app/plugins/api.client.d.ts +11 -0
- package/dist/runtime/app/plugins/api.client.js +92 -0
- package/dist/runtime/app/plugins/api.server.d.ts +13 -0
- package/dist/runtime/app/plugins/api.server.js +28 -0
- package/dist/runtime/app/plugins/ssr-state.server.d.ts +2 -0
- package/dist/runtime/app/plugins/ssr-state.server.js +13 -0
- package/dist/runtime/app/router.options.d.ts +12 -0
- package/dist/runtime/app/router.options.js +11 -0
- package/dist/runtime/app/utils/logger.d.ts +18 -0
- package/dist/runtime/app/utils/logger.js +48 -0
- package/dist/runtime/app/utils/redirectValidation.d.ts +18 -0
- package/dist/runtime/app/utils/redirectValidation.js +21 -0
- package/dist/runtime/app/utils/routeMatching.d.ts +13 -0
- package/dist/runtime/app/utils/routeMatching.js +10 -0
- package/dist/runtime/app/utils/tokenStore.d.ts +24 -0
- package/dist/runtime/app/utils/tokenStore.js +14 -0
- package/dist/runtime/app/utils/tokenUtils.d.ts +17 -0
- package/dist/runtime/app/utils/tokenUtils.js +4 -0
- package/dist/runtime/server/middleware/auth.d.ts +6 -0
- package/dist/runtime/server/middleware/auth.js +82 -0
- package/dist/runtime/server/plugins/ssr-auth.d.ts +7 -0
- package/dist/runtime/server/plugins/ssr-auth.js +82 -0
- package/dist/runtime/server/providers/auth0.d.ts +12 -0
- package/dist/runtime/server/providers/auth0.js +57 -0
- package/dist/runtime/server/providers/github.d.ts +12 -0
- package/dist/runtime/server/providers/github.js +44 -0
- package/dist/runtime/server/providers/google.d.ts +12 -0
- package/dist/runtime/server/providers/google.js +46 -0
- package/dist/runtime/server/providers/mock.d.ts +37 -0
- package/dist/runtime/server/providers/mock.js +129 -0
- package/dist/runtime/server/providers/oauthBase.d.ts +72 -0
- package/dist/runtime/server/providers/oauthBase.js +183 -0
- package/dist/runtime/server/routes/impersonate.post.d.ts +21 -0
- package/dist/runtime/server/routes/impersonate.post.js +68 -0
- package/dist/runtime/server/routes/logout.post.d.ts +9 -0
- package/dist/runtime/server/routes/logout.post.js +24 -0
- package/dist/runtime/server/routes/me.get.d.ts +6 -0
- package/dist/runtime/server/routes/me.get.js +11 -0
- package/dist/runtime/server/routes/mock/authorize.get.d.ts +29 -0
- package/dist/runtime/server/routes/mock/authorize.get.js +103 -0
- package/dist/runtime/server/routes/mock/token.post.d.ts +31 -0
- package/dist/runtime/server/routes/mock/token.post.js +88 -0
- package/dist/runtime/server/routes/mock/userinfo.get.d.ts +27 -0
- package/dist/runtime/server/routes/mock/userinfo.get.js +59 -0
- package/dist/runtime/server/routes/password/change.post.d.ts +4 -0
- package/dist/runtime/server/routes/password/change.post.js +108 -0
- package/dist/runtime/server/routes/password/login-verify.get.d.ts +2 -0
- package/dist/runtime/server/routes/password/login-verify.get.js +79 -0
- package/dist/runtime/server/routes/password/login.post.d.ts +4 -0
- package/dist/runtime/server/routes/password/login.post.js +66 -0
- package/dist/runtime/server/routes/password/register-verify.get.d.ts +2 -0
- package/dist/runtime/server/routes/password/register-verify.get.js +86 -0
- package/dist/runtime/server/routes/password/register.post.d.ts +4 -0
- package/dist/runtime/server/routes/password/register.post.js +87 -0
- package/dist/runtime/server/routes/password/reset-complete.post.d.ts +4 -0
- package/dist/runtime/server/routes/password/reset-complete.post.js +75 -0
- package/dist/runtime/server/routes/password/reset-request.post.d.ts +5 -0
- package/dist/runtime/server/routes/password/reset-request.post.js +52 -0
- package/dist/runtime/server/routes/password/reset-verify.get.d.ts +2 -0
- package/dist/runtime/server/routes/password/reset-verify.get.js +50 -0
- package/dist/runtime/server/routes/refresh.post.d.ts +8 -0
- package/dist/runtime/server/routes/refresh.post.js +102 -0
- package/dist/runtime/server/routes/token.post.d.ts +28 -0
- package/dist/runtime/server/routes/token.post.js +90 -0
- package/dist/runtime/server/routes/unimpersonate.post.d.ts +16 -0
- package/dist/runtime/server/routes/unimpersonate.post.js +65 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/runtime/server/utils/auth.d.ts +94 -0
- package/dist/runtime/server/utils/auth.js +54 -0
- package/dist/runtime/server/utils/authCodeStore.d.ts +137 -0
- package/dist/runtime/server/utils/authCodeStore.js +123 -0
- package/dist/runtime/server/utils/cookies.d.ts +15 -0
- package/dist/runtime/server/utils/cookies.js +23 -0
- package/dist/runtime/server/utils/customClaims.d.ts +37 -0
- package/dist/runtime/server/utils/customClaims.js +45 -0
- package/dist/runtime/server/utils/handler.d.ts +77 -0
- package/dist/runtime/server/utils/handler.js +7 -0
- package/dist/runtime/server/utils/impersonation.d.ts +48 -0
- package/dist/runtime/server/utils/impersonation.js +259 -0
- package/dist/runtime/server/utils/jwt.d.ts +24 -0
- package/dist/runtime/server/utils/jwt.js +77 -0
- package/dist/runtime/server/utils/logger.d.ts +18 -0
- package/dist/runtime/server/utils/logger.js +49 -0
- package/dist/runtime/server/utils/magicCodeStore.d.ts +27 -0
- package/dist/runtime/server/utils/magicCodeStore.js +66 -0
- package/dist/runtime/server/utils/mockCodeStore.d.ts +89 -0
- package/dist/runtime/server/utils/mockCodeStore.js +71 -0
- package/dist/runtime/server/utils/password.d.ts +33 -0
- package/dist/runtime/server/utils/password.js +48 -0
- package/dist/runtime/server/utils/refreshToken.d.ts +74 -0
- package/dist/runtime/server/utils/refreshToken.js +108 -0
- package/dist/runtime/server/utils/resetSessionStore.d.ts +12 -0
- package/dist/runtime/server/utils/resetSessionStore.js +29 -0
- package/dist/runtime/tasks/cleanup/magic-codes.d.ts +10 -0
- package/dist/runtime/tasks/cleanup/magic-codes.js +79 -0
- package/dist/runtime/tasks/cleanup/refresh-tokens.d.ts +10 -0
- package/dist/runtime/tasks/cleanup/refresh-tokens.js +55 -0
- package/dist/runtime/tasks/cleanup/reset-sessions.d.ts +8 -0
- package/dist/runtime/tasks/cleanup/reset-sessions.js +45 -0
- package/dist/runtime/types/augmentation.d.ts +73 -0
- package/dist/runtime/types/augmentation.js +0 -0
- package/dist/runtime/types/authCode.d.ts +60 -0
- package/dist/runtime/types/authCode.js +0 -0
- package/dist/runtime/types/callbacks.d.ts +54 -0
- package/dist/runtime/types/callbacks.js +0 -0
- package/dist/runtime/types/config.d.ts +129 -0
- package/dist/runtime/types/config.js +0 -0
- package/dist/runtime/types/hooks.d.ts +118 -0
- package/dist/runtime/types/hooks.js +0 -0
- package/dist/runtime/types/index.d.ts +13 -0
- package/dist/runtime/types/index.js +1 -0
- package/dist/runtime/types/providers.d.ts +212 -0
- package/dist/runtime/types/providers.js +0 -0
- package/dist/runtime/types/refresh.d.ts +61 -0
- package/dist/runtime/types/refresh.js +0 -0
- package/dist/runtime/types/routes.d.ts +30 -0
- package/dist/runtime/types/routes.js +0 -0
- package/dist/runtime/types/token.d.ts +182 -0
- package/dist/runtime/types/token.js +0 -0
- package/dist/types.d.mts +7 -0
- package/package.json +80 -0
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Nuxt Aegis
|
|
2
|
+
|
|
3
|
+
[](https://github.com/peterbud/nuxt-aegis/actions/workflows/ci.yml)
|
|
4
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
5
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
6
|
+
[![License][license-src]][license-href]
|
|
7
|
+
[![Nuxt][nuxt-src]][nuxt-href]
|
|
8
|
+
|
|
9
|
+
**OAuth-based authentication with JWT token management for Nuxt 3/4**
|
|
10
|
+
|
|
11
|
+
> [!WARNING]
|
|
12
|
+
> **Experimental Module**: Nuxt Aegis is currently in active development and should be considered as work in progress. It is **not recommended for production use** at this time. APIs and features may change without notice.
|
|
13
|
+
|
|
14
|
+
Nuxt Aegis is a a Nuxt module that orchestrates the authentication flow between external identity providers (Google, GitHub, Auth0, etc.) and your application. It handles the complexity of OAuth 2.0, JWT token generation, and automatic token refresh, letting you focus on your application logic.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Why Nuxt Aegis?
|
|
18
|
+
|
|
19
|
+
Unlike cookie-based solutions that lock your API to the browser, Nuxt Aegis uses **industry-standard JWT bearer tokens**. This means your API is ready for:
|
|
20
|
+
- 📱 Mobile applications
|
|
21
|
+
- 🖥️ CLI tools
|
|
22
|
+
- 🌐 Third-party integrations
|
|
23
|
+
- ⚡ Universal access across platforms
|
|
24
|
+
|
|
25
|
+
### How It Works
|
|
26
|
+
|
|
27
|
+
1. **Authentication (Identity) Provider** (e.g., Google) authenticates the user.
|
|
28
|
+
2. **Nuxt Aegis** verifies the identity retrieves the user information, and issues it's own application specific JWT.
|
|
29
|
+
3. **Your App** receives the JWT token and uses it for authorization.
|
|
30
|
+
|
|
31
|
+
You get full control over user data persistence while Aegis handles the security lifecycle.
|
|
32
|
+
|
|
33
|
+
## ✨ Key Features
|
|
34
|
+
|
|
35
|
+
- 🔐 **OAuth 2.0 & OpenID Connect** - Built-in support for Google, GitHub, Auth0, and Microsoft Entra ID.
|
|
36
|
+
- 🔑 **Password Authentication** - Secure username/password flow with magic link verification.
|
|
37
|
+
- 🎫 **JWT Management** - Automatic token generation, validation, and signing (HS256/RS256).
|
|
38
|
+
- 🔄 **Auto-Refresh** - Seamless background token refresh with secure HTTP-only cookies.
|
|
39
|
+
- 🛡️ **Route Protection** - Declarative middleware for both server API routes and client-side pages.
|
|
40
|
+
- 🧪 **Mock Provider** - Built-in testing provider to simulate auth flows without external services.
|
|
41
|
+
- 🎨 **Custom Claims** - Easily inject application-specific data (roles, permissions or similar) into tokens.
|
|
42
|
+
- 🎭 **Impersonation** - Support for user impersonation with full audit logging
|
|
43
|
+
- ⚙️ **SSR Friendly** - Works seamlessly with Nuxt's server-side rendering.
|
|
44
|
+
- 🥽 **Type Safe** - Written in TypeScript with full type definitions for a great developer experience.
|
|
45
|
+
|
|
46
|
+
## 🚀 Quick Start
|
|
47
|
+
|
|
48
|
+
### 1. Install
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx nuxi module add nuxt-aegis
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 2. Configure
|
|
55
|
+
|
|
56
|
+
Add the module and provider configuration to `nuxt.config.ts`:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
export default defineNuxtConfig({
|
|
60
|
+
modules: ['nuxt-aegis'],
|
|
61
|
+
|
|
62
|
+
nuxtAegis: {
|
|
63
|
+
token: {
|
|
64
|
+
secret: process.env.NUXT_AEGIS_TOKEN_SECRET!,
|
|
65
|
+
},
|
|
66
|
+
providers: {
|
|
67
|
+
google: {
|
|
68
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
69
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. Create Auth Route
|
|
77
|
+
|
|
78
|
+
Create a server route handler to initialize the provider:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// server/routes/auth/google.get.ts
|
|
82
|
+
export default defineOAuthGoogleEventHandler({
|
|
83
|
+
onUserInfo: (providerUserInfo, _tokens, _event) => {
|
|
84
|
+
return {
|
|
85
|
+
id: providerUserInfo.sub as string,
|
|
86
|
+
email: providerUserInfo.email,
|
|
87
|
+
name: providerUserInfo.name,
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 4. Use in Components
|
|
94
|
+
|
|
95
|
+
Access authentication state anywhere in your app:
|
|
96
|
+
|
|
97
|
+
```vue
|
|
98
|
+
<script setup lang="ts">
|
|
99
|
+
const { user, isLoggedIn, login, logout } = useAuth()
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<template>
|
|
103
|
+
<div v-if="isLoggedIn">
|
|
104
|
+
<h1>Welcome, {{ user?.name }}!</h1>
|
|
105
|
+
<button @click="logout()">Logout</button>
|
|
106
|
+
</div>
|
|
107
|
+
<button v-else @click="login('google')">
|
|
108
|
+
Login with Google
|
|
109
|
+
</button>
|
|
110
|
+
</template>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## 📖 Documentation
|
|
114
|
+
|
|
115
|
+
Ready to dive deeper? Check out the full documentation:
|
|
116
|
+
|
|
117
|
+
- **[Getting Started](./docs/getting-started/installation.md)** - Installation and setup guides.
|
|
118
|
+
- **[Providers](./docs/providers/)** - Configure Google, GitHub, Auth0, Password, and Mock providers.
|
|
119
|
+
- **[Route Protection](./docs/guides/route-protection.md)** - Learn how to protect your pages and API routes.
|
|
120
|
+
- **[Custom Claims](./docs/guides/custom-claims.md)** - Add custom data to your JWTs.
|
|
121
|
+
- **[API Reference](./docs/api/)** - Detailed API documentation.
|
|
122
|
+
|
|
123
|
+
## Contributing
|
|
124
|
+
|
|
125
|
+
Contributions are welcome! Please see the [Requirements Specification](/specs/requirements.md) for detailed technical requirements.
|
|
126
|
+
|
|
127
|
+
## Development
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Install dependencies
|
|
131
|
+
pnpm install
|
|
132
|
+
|
|
133
|
+
# Start development server
|
|
134
|
+
pnpm dev
|
|
135
|
+
|
|
136
|
+
# Run tests
|
|
137
|
+
pnpm test
|
|
138
|
+
|
|
139
|
+
# Lint & type check
|
|
140
|
+
pnpm lint
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
[MIT License](./LICENSE)
|
|
146
|
+
|
|
147
|
+
## Acknowledgments
|
|
148
|
+
|
|
149
|
+
- Built with [Nuxt Module Builder](https://github.com/nuxt/module-builder)
|
|
150
|
+
- JWT handling powered by [jose](https://github.com/panva/jose)
|
|
151
|
+
- Heavily inspired by the [nuxt-auth-utils](https://github.com/atinux/nuxt-auth-utils) and Nuxt community
|
|
152
|
+
|
|
153
|
+
Made with ❤️ for the Nuxt community
|
|
154
|
+
|
|
155
|
+
<!-- Badges -->
|
|
156
|
+
[npm-version-src]: https://img.shields.io/npm/v/nuxt-aegis/latest.svg\?style\=flat\&colorA\=020420\&colorB\=00DC82
|
|
157
|
+
[npm-version-href]: https://npmjs.com/package/@peterbud/nuxt-aegis
|
|
158
|
+
|
|
159
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-aegis.svg\?style\=flat\&colorA\=020420\&colorB\=00DC82
|
|
160
|
+
[npm-downloads-href]: https://npm.chart.dev/@peterbud/nuxt-aegis
|
|
161
|
+
|
|
162
|
+
[license-src]: https://img.shields.io/npm/l/nuxt-aegis.svg\?style\=flat\&colorA\=020420\&colorB\=00DC82
|
|
163
|
+
[license-href]: https://npmjs.com/package/@peterbud/nuxt-aegis
|
|
164
|
+
|
|
165
|
+
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420\?logo\=nuxt.js
|
|
166
|
+
[nuxt-href]: https://nuxt.com
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { defineNuxtModule, updateRuntimeConfig, useLogger, createResolver, addPlugin, addImports, extendPages, addServerImportsDir, addServerImports, addServerHandler, addServerPlugin, addRouteMiddleware, addTemplate } from '@nuxt/kit';
|
|
2
|
+
import { defu } from 'defu';
|
|
3
|
+
|
|
4
|
+
const module$1 = defineNuxtModule({
|
|
5
|
+
meta: {
|
|
6
|
+
name: "nuxt-aegis",
|
|
7
|
+
configKey: "nuxtAegis"
|
|
8
|
+
},
|
|
9
|
+
// Default configuration options of the Nuxt module
|
|
10
|
+
defaults: {
|
|
11
|
+
devtools: true,
|
|
12
|
+
token: {
|
|
13
|
+
secret: "",
|
|
14
|
+
// Must be provided by user or generated
|
|
15
|
+
expiresIn: "1h",
|
|
16
|
+
// Access token expiry
|
|
17
|
+
algorithm: "HS256",
|
|
18
|
+
issuer: "nuxt-aegis",
|
|
19
|
+
audience: ""
|
|
20
|
+
},
|
|
21
|
+
tokenRefresh: {
|
|
22
|
+
enabled: true,
|
|
23
|
+
automaticRefresh: true,
|
|
24
|
+
cookie: {
|
|
25
|
+
cookieName: "nuxt-aegis-refresh",
|
|
26
|
+
maxAge: 60 * 60 * 24 * 7,
|
|
27
|
+
// 7 days
|
|
28
|
+
secure: true,
|
|
29
|
+
sameSite: "lax",
|
|
30
|
+
httpOnly: true,
|
|
31
|
+
path: "/"
|
|
32
|
+
},
|
|
33
|
+
encryption: {
|
|
34
|
+
enabled: false,
|
|
35
|
+
// SC-16: Encryption disabled by default
|
|
36
|
+
algorithm: "aes-256-gcm"
|
|
37
|
+
// SC-17: Default encryption algorithm
|
|
38
|
+
},
|
|
39
|
+
storage: {
|
|
40
|
+
driver: "fs",
|
|
41
|
+
// RS-10: Default to filesystem storage
|
|
42
|
+
prefix: "refresh:",
|
|
43
|
+
base: "./.data/refresh-tokens"
|
|
44
|
+
},
|
|
45
|
+
ssrTokenExpiry: "5m"
|
|
46
|
+
// SSR token expiry (short-lived)
|
|
47
|
+
},
|
|
48
|
+
authCode: {
|
|
49
|
+
expiresIn: 60
|
|
50
|
+
// CS-4, CF-9: Authorization code expiry in seconds (default 60s)
|
|
51
|
+
},
|
|
52
|
+
redirect: {
|
|
53
|
+
logout: "/",
|
|
54
|
+
success: "/",
|
|
55
|
+
error: "/"
|
|
56
|
+
},
|
|
57
|
+
clientMiddleware: {
|
|
58
|
+
enabled: false,
|
|
59
|
+
global: false,
|
|
60
|
+
redirectTo: "/login",
|
|
61
|
+
loggedOutRedirectTo: "/",
|
|
62
|
+
publicRoutes: []
|
|
63
|
+
},
|
|
64
|
+
endpoints: {
|
|
65
|
+
authPath: "/auth",
|
|
66
|
+
loginPath: "/auth",
|
|
67
|
+
callbackPath: "/auth/callback",
|
|
68
|
+
logoutPath: "/auth/logout",
|
|
69
|
+
refreshPath: "/auth/refresh",
|
|
70
|
+
userInfoPath: "/api/user/me"
|
|
71
|
+
},
|
|
72
|
+
logging: {
|
|
73
|
+
level: "info",
|
|
74
|
+
security: false
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
setup(options, nuxt) {
|
|
78
|
+
options.enableSSR = options.enableSSR ?? (nuxt.options.ssr === true ? true : false);
|
|
79
|
+
updateRuntimeConfig({
|
|
80
|
+
public: {
|
|
81
|
+
nuxtAegis: {
|
|
82
|
+
authPath: options.endpoints?.authPath || "/auth",
|
|
83
|
+
loginPath: options.endpoints?.loginPath || options.endpoints?.authPath || "/auth",
|
|
84
|
+
callbackPath: options.endpoints?.callbackPath || "/auth/callback",
|
|
85
|
+
logoutPath: options.endpoints?.logoutPath || "/auth/logout",
|
|
86
|
+
refreshPath: options.endpoints?.refreshPath || "/auth/refresh",
|
|
87
|
+
userInfoPath: options.endpoints?.userInfoPath || "/api/user/me",
|
|
88
|
+
redirect: options.redirect,
|
|
89
|
+
tokenRefresh: options.tokenRefresh,
|
|
90
|
+
clientMiddleware: options.clientMiddleware,
|
|
91
|
+
logging: options.logging,
|
|
92
|
+
enableSSR: options.enableSSR
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
nuxtAegis: options
|
|
96
|
+
});
|
|
97
|
+
const runtimeConfig = nuxt.options.runtimeConfig;
|
|
98
|
+
if (options.enableSSR && nuxt.options.ssr === false) {
|
|
99
|
+
const logger2 = useLogger("nuxt-aegis");
|
|
100
|
+
logger2.warn(
|
|
101
|
+
"nuxtAegis.enableSSR is true but Nuxt SSR is disabled. SSR authentication will not work. Set ssr: true in nuxt.config.ts or disable enableSSR."
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
const resolver = createResolver(import.meta.url);
|
|
105
|
+
if (options.tokenRefresh?.encryption?.enabled) {
|
|
106
|
+
const encryptionKey = options.tokenRefresh.encryption.key || process.env.NUXT_AEGIS_ENCRYPTION_KEY;
|
|
107
|
+
if (!encryptionKey) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
"[Nuxt Aegis] Encryption is enabled but no encryption key is configured. Please set tokenRefresh.encryption.key in nuxt.config.ts or NUXT_AEGIS_ENCRYPTION_KEY environment variable."
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
if (!runtimeConfig.nuxtAegis) {
|
|
113
|
+
runtimeConfig.nuxtAegis = {};
|
|
114
|
+
}
|
|
115
|
+
if (!runtimeConfig.nuxtAegis.tokenRefresh) {
|
|
116
|
+
runtimeConfig.nuxtAegis.tokenRefresh = {};
|
|
117
|
+
}
|
|
118
|
+
if (!runtimeConfig.nuxtAegis.tokenRefresh.encryption) {
|
|
119
|
+
runtimeConfig.nuxtAegis.tokenRefresh.encryption = {};
|
|
120
|
+
}
|
|
121
|
+
runtimeConfig.nuxtAegis.tokenRefresh.encryption.key = encryptionKey;
|
|
122
|
+
}
|
|
123
|
+
addPlugin(resolver.resolve("./runtime/app/plugins/api.client"));
|
|
124
|
+
addImports([
|
|
125
|
+
{
|
|
126
|
+
name: "useAuth",
|
|
127
|
+
from: resolver.resolve("./runtime/app/composables/useAuth")
|
|
128
|
+
}
|
|
129
|
+
]);
|
|
130
|
+
extendPages((pages) => {
|
|
131
|
+
pages.push({
|
|
132
|
+
name: "Callback",
|
|
133
|
+
path: `${runtimeConfig.nuxtAegis?.endpoints?.callbackPath}` || `${runtimeConfig.public.nuxtAegis.authPath}/callback`,
|
|
134
|
+
file: resolver.resolve("./runtime/app/pages/AuthCallback.vue")
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
addServerImportsDir(resolver.resolve("./runtime/server/providers"));
|
|
138
|
+
addServerImports([
|
|
139
|
+
// Authentication utilities (from auth.ts)
|
|
140
|
+
{ name: "requireAuth", from: resolver.resolve("./runtime/server/utils/auth") },
|
|
141
|
+
{ name: "getAuthUser", from: resolver.resolve("./runtime/server/utils/auth") },
|
|
142
|
+
{ name: "generateAccessToken", from: resolver.resolve("./runtime/server/utils/auth") },
|
|
143
|
+
{ name: "verifyToken", from: resolver.resolve("./runtime/server/utils/auth") },
|
|
144
|
+
{ name: "generateAuthTokens", from: resolver.resolve("./runtime/server/utils/auth") },
|
|
145
|
+
// Refresh token management utilities (from refreshToken.ts)
|
|
146
|
+
{ name: "revokeRefreshToken", from: resolver.resolve("./runtime/server/utils/refreshToken") },
|
|
147
|
+
{ name: "deleteUserRefreshTokens", from: resolver.resolve("./runtime/server/utils/refreshToken") },
|
|
148
|
+
{ name: "hashRefreshToken", from: resolver.resolve("./runtime/server/utils/refreshToken") },
|
|
149
|
+
// Cookie utilities (from cookies.ts)
|
|
150
|
+
{ name: "setRefreshTokenCookie", from: resolver.resolve("./runtime/server/utils/cookies") },
|
|
151
|
+
{ name: "getRefreshTokenFromCookie", from: resolver.resolve("./runtime/server/utils/cookies") },
|
|
152
|
+
// Custom claims utilities (from customClaims.ts)
|
|
153
|
+
{ name: "applyCustomClaims", from: resolver.resolve("./runtime/server/utils/customClaims") },
|
|
154
|
+
// Handler utilities (from handler.ts)
|
|
155
|
+
{ name: "defineAegisHandler", from: resolver.resolve("./runtime/server/utils/handler") },
|
|
156
|
+
{ name: "useAegisHandler", from: resolver.resolve("./runtime/server/utils/handler") }
|
|
157
|
+
]);
|
|
158
|
+
addServerHandler({
|
|
159
|
+
route: runtimeConfig.public.nuxtAegis.userInfoPath,
|
|
160
|
+
handler: resolver.resolve("./runtime/server/routes/me.get"),
|
|
161
|
+
method: "get"
|
|
162
|
+
});
|
|
163
|
+
addServerHandler({
|
|
164
|
+
route: runtimeConfig.public.nuxtAegis.logoutPath,
|
|
165
|
+
handler: resolver.resolve("./runtime/server/routes/logout.post"),
|
|
166
|
+
method: "post"
|
|
167
|
+
});
|
|
168
|
+
addServerHandler({
|
|
169
|
+
route: `${runtimeConfig.public.nuxtAegis.authPath}/token`,
|
|
170
|
+
handler: resolver.resolve("./runtime/server/routes/token.post"),
|
|
171
|
+
method: "post"
|
|
172
|
+
});
|
|
173
|
+
addServerHandler({
|
|
174
|
+
route: runtimeConfig.public.nuxtAegis.refreshPath,
|
|
175
|
+
handler: resolver.resolve("./runtime/server/routes/refresh.post"),
|
|
176
|
+
method: "post"
|
|
177
|
+
});
|
|
178
|
+
if (options.impersonation?.enabled) {
|
|
179
|
+
addServerHandler({
|
|
180
|
+
route: `${runtimeConfig.public.nuxtAegis.authPath}/impersonate`,
|
|
181
|
+
handler: resolver.resolve("./runtime/server/routes/impersonate.post"),
|
|
182
|
+
method: "post"
|
|
183
|
+
});
|
|
184
|
+
addServerHandler({
|
|
185
|
+
route: `${runtimeConfig.public.nuxtAegis.authPath}/unimpersonate`,
|
|
186
|
+
handler: resolver.resolve("./runtime/server/routes/unimpersonate.post"),
|
|
187
|
+
method: "post"
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (options.providers?.password) {
|
|
191
|
+
const passwordRoutes = [
|
|
192
|
+
{ path: "password/register", file: "register.post", method: "post" },
|
|
193
|
+
{ path: "password/register-verify", file: "register-verify.get", method: "get" },
|
|
194
|
+
{ path: "password/login", file: "login.post", method: "post" },
|
|
195
|
+
{ path: "password/login-verify", file: "login-verify.get", method: "get" },
|
|
196
|
+
{ path: "password/reset-request", file: "reset-request.post", method: "post" },
|
|
197
|
+
{ path: "password/reset-verify", file: "reset-verify.get", method: "get" },
|
|
198
|
+
{ path: "password/reset-complete", file: "reset-complete.post", method: "post" },
|
|
199
|
+
{ path: "password/change", file: "change.post", method: "post" }
|
|
200
|
+
];
|
|
201
|
+
for (const route of passwordRoutes) {
|
|
202
|
+
addServerHandler({
|
|
203
|
+
route: `${runtimeConfig.public.nuxtAegis.authPath}/${route.path}`,
|
|
204
|
+
handler: resolver.resolve(`./runtime/server/routes/password/${route.file}`),
|
|
205
|
+
method: route.method
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
addServerHandler({
|
|
210
|
+
handler: resolver.resolve("./runtime/server/middleware/auth"),
|
|
211
|
+
middleware: true
|
|
212
|
+
});
|
|
213
|
+
if (options.enableSSR) {
|
|
214
|
+
addServerPlugin(resolver.resolve("./runtime/server/plugins/ssr-auth"));
|
|
215
|
+
addPlugin(resolver.resolve("./runtime/app/plugins/api.server"));
|
|
216
|
+
addPlugin(resolver.resolve("./runtime/app/plugins/ssr-state.server"));
|
|
217
|
+
}
|
|
218
|
+
const logger = useLogger("nuxt-aegis");
|
|
219
|
+
if (options.clientMiddleware?.enabled) {
|
|
220
|
+
const cm = options.clientMiddleware;
|
|
221
|
+
if (cm.global) {
|
|
222
|
+
const userPublicRoutes = cm.publicRoutes || [];
|
|
223
|
+
const redirectRoutes = [cm.redirectTo, cm.loggedOutRedirectTo];
|
|
224
|
+
const allPublicRoutes = [.../* @__PURE__ */ new Set([...userPublicRoutes, ...redirectRoutes])];
|
|
225
|
+
options.clientMiddleware.publicRoutes = allPublicRoutes;
|
|
226
|
+
if (allPublicRoutes.length === 0) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
"[nuxt-aegis] clientMiddleware.global is enabled but no publicRoutes are configured. At minimum, the redirectTo and loggedOutRedirectTo routes will be automatically included."
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
const userPublicRoutes = cm.publicRoutes || [];
|
|
233
|
+
if (userPublicRoutes.length > 0) {
|
|
234
|
+
logger.warn(
|
|
235
|
+
"[nuxt-aegis] clientMiddleware.publicRoutes is configured but will be ignored because clientMiddleware.global is false. The auth-logged-in middleware will only run on pages where you explicitly add it via definePageMeta({ middleware: ['auth-logged-in'] })."
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
addRouteMiddleware({
|
|
240
|
+
name: "auth-logged-in",
|
|
241
|
+
path: resolver.resolve("./runtime/app/middleware/auth-logged-in"),
|
|
242
|
+
global: options.clientMiddleware.global === true
|
|
243
|
+
});
|
|
244
|
+
addRouteMiddleware({
|
|
245
|
+
name: "auth-logged-out",
|
|
246
|
+
path: resolver.resolve("./runtime/app/middleware/auth-logged-out"),
|
|
247
|
+
global: false
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
251
|
+
if (!isProduction) {
|
|
252
|
+
const mockConfig = options.providers?.mock;
|
|
253
|
+
if (mockConfig) {
|
|
254
|
+
addServerHandler({
|
|
255
|
+
route: `${runtimeConfig.public.nuxtAegis.authPath}/mock/authorize`,
|
|
256
|
+
handler: resolver.resolve("./runtime/server/routes/mock/authorize.get"),
|
|
257
|
+
method: "get"
|
|
258
|
+
});
|
|
259
|
+
addServerHandler({
|
|
260
|
+
route: `${runtimeConfig.public.nuxtAegis.authPath}/mock/token`,
|
|
261
|
+
handler: resolver.resolve("./runtime/server/routes/mock/token.post"),
|
|
262
|
+
method: "post"
|
|
263
|
+
});
|
|
264
|
+
addServerHandler({
|
|
265
|
+
route: `${runtimeConfig.public.nuxtAegis.authPath}/mock/userinfo`,
|
|
266
|
+
handler: resolver.resolve("./runtime/server/routes/mock/userinfo.get"),
|
|
267
|
+
method: "get"
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (!nuxt.options.nitro) {
|
|
272
|
+
nuxt.options.nitro = {};
|
|
273
|
+
}
|
|
274
|
+
if (!nuxt.options.nitro.experimental) {
|
|
275
|
+
nuxt.options.nitro.experimental = {};
|
|
276
|
+
}
|
|
277
|
+
nuxt.options.nitro.experimental.tasks = true;
|
|
278
|
+
nuxt.hook("nitro:config", (nitroConfig) => {
|
|
279
|
+
nitroConfig.scanDirs = nitroConfig.scanDirs || [];
|
|
280
|
+
nitroConfig.scanDirs.push(resolver.resolve("./runtime"));
|
|
281
|
+
});
|
|
282
|
+
if (!nuxt.options.nitro.scheduledTasks) {
|
|
283
|
+
nuxt.options.nitro.scheduledTasks = {};
|
|
284
|
+
}
|
|
285
|
+
nuxt.options.nitro.scheduledTasks["0 2 * * *"] = [
|
|
286
|
+
"cleanup:refresh-tokens",
|
|
287
|
+
"cleanup:magic-codes",
|
|
288
|
+
"cleanup:reset-sessions"
|
|
289
|
+
];
|
|
290
|
+
if (!nuxt.options.nitro.storage) {
|
|
291
|
+
nuxt.options.nitro.storage = {};
|
|
292
|
+
}
|
|
293
|
+
const storageConfig = options.tokenRefresh?.storage || {};
|
|
294
|
+
nuxt.options.nitro.storage.refreshTokenStore = defu(nuxt.options.nitro.storage.refreshTokenStore, {
|
|
295
|
+
driver: storageConfig.driver || "fs",
|
|
296
|
+
base: storageConfig.base || "./.data/refresh-tokens"
|
|
297
|
+
});
|
|
298
|
+
nuxt.options.nitro.storage.authCodeStore = defu(nuxt.options.nitro.storage.authCodeStore, {
|
|
299
|
+
driver: "memory"
|
|
300
|
+
});
|
|
301
|
+
nuxt.options.nitro.storage.magicCodeStore = defu(nuxt.options.nitro.storage.magicCodeStore, {
|
|
302
|
+
driver: "memory"
|
|
303
|
+
});
|
|
304
|
+
nuxt.options.nitro.storage.resetSessionStore = defu(nuxt.options.nitro.storage.resetSessionStore, {
|
|
305
|
+
driver: "memory"
|
|
306
|
+
});
|
|
307
|
+
nuxt.hook("pages:routerOptions", (routerOptions) => {
|
|
308
|
+
routerOptions.files.push({
|
|
309
|
+
path: resolver.resolve("./runtime/app/router.options")
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
const typesPath = resolver.resolve("./runtime/types");
|
|
313
|
+
addTemplate({
|
|
314
|
+
filename: "types/nuxt-aegis.d.ts",
|
|
315
|
+
write: true,
|
|
316
|
+
getContents: () => {
|
|
317
|
+
return `import type { NitroAegisAuth, CustomTokenClaims } from ${JSON.stringify(typesPath)}
|
|
318
|
+
|
|
319
|
+
type NuxtAegisRouteRules = {
|
|
320
|
+
/**
|
|
321
|
+
* Authentication requirement for this route
|
|
322
|
+
* - true | 'required' | 'protected': Route requires authentication
|
|
323
|
+
* - false | 'public' | 'skip': Route is public and skips authentication
|
|
324
|
+
* - undefined: Route is not protected (opt-in behavior)
|
|
325
|
+
*/
|
|
326
|
+
auth?: NitroAegisAuth
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
declare module 'nitropack/types' {
|
|
330
|
+
interface NitroRouteRules {
|
|
331
|
+
nuxtAegis?: NuxtAegisRouteRules
|
|
332
|
+
}
|
|
333
|
+
interface NitroRouteConfig {
|
|
334
|
+
nuxtAegis?: NuxtAegisRouteRules
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
declare module 'nitropack' {
|
|
339
|
+
interface NitroRouteRules {
|
|
340
|
+
nuxtAegis?: NuxtAegisRouteRules
|
|
341
|
+
}
|
|
342
|
+
interface NitroRouteConfig {
|
|
343
|
+
nuxtAegis?: NuxtAegisRouteRules
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Export user-facing types for direct import
|
|
348
|
+
export { type CustomTokenClaims }`;
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { ComputedRef } from 'vue';
|
|
2
|
+
import type { TokenPayload } from '../../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Return type for the useAuth composable
|
|
5
|
+
* @template T - Token payload type extending TokenPayload (defaults to TokenPayload)
|
|
6
|
+
*/
|
|
7
|
+
interface UseAuthReturn<T extends TokenPayload = TokenPayload> {
|
|
8
|
+
/** Reactive property indicating whether a user is logged in */
|
|
9
|
+
isLoggedIn: ComputedRef<boolean>;
|
|
10
|
+
/** Reactive property indicating the authentication state is being initialized */
|
|
11
|
+
isLoading: ComputedRef<boolean>;
|
|
12
|
+
/** Reactive property containing the current user's data */
|
|
13
|
+
user: ComputedRef<T | null>;
|
|
14
|
+
/** Error state for authentication operations */
|
|
15
|
+
error: ComputedRef<string | null>;
|
|
16
|
+
/** Reactive property indicating if currently impersonating another user */
|
|
17
|
+
isImpersonating: ComputedRef<boolean>;
|
|
18
|
+
/** Reactive property containing original user data when impersonating */
|
|
19
|
+
originalUser: ComputedRef<{
|
|
20
|
+
originalUserId: string;
|
|
21
|
+
originalUserEmail?: string;
|
|
22
|
+
originalUserName?: string;
|
|
23
|
+
} | null>;
|
|
24
|
+
/** Method to initiate the authentication flow */
|
|
25
|
+
login: (provider?: string, redirectTo?: string) => Promise<void>;
|
|
26
|
+
/** Method to end the user session */
|
|
27
|
+
logout: (redirectTo?: string) => Promise<void>;
|
|
28
|
+
/** Method to refresh the authentication state */
|
|
29
|
+
refresh: () => Promise<void>;
|
|
30
|
+
/** Method to impersonate another user (admin only) */
|
|
31
|
+
impersonate: (targetUserId: string, reason?: string) => Promise<void>;
|
|
32
|
+
/** Method to stop impersonation and restore original session */
|
|
33
|
+
stopImpersonation: () => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Composable for managing authentication state and actions
|
|
37
|
+
*
|
|
38
|
+
* This composable provides methods for OAuth authentication using a CODE-based flow:
|
|
39
|
+
*
|
|
40
|
+
* Authentication Flow:
|
|
41
|
+
* 1. login() redirects to OAuth provider authentication page
|
|
42
|
+
* 2. Provider redirects back with short-lived authorization CODE (60s)
|
|
43
|
+
* 3. AuthCallback page exchanges CODE for JWT tokens via /auth/token
|
|
44
|
+
* 4. Access token stored in memory (reactive ref, cleared on refresh)
|
|
45
|
+
* 5. Refresh token stored as HttpOnly, Secure cookie
|
|
46
|
+
*
|
|
47
|
+
* Token Storage:
|
|
48
|
+
* - Access token in memory only (NOT sessionStorage)
|
|
49
|
+
* - Automatically cleared on page refresh/window close
|
|
50
|
+
* - Refresh token cookie used to obtain new access token after refresh
|
|
51
|
+
*
|
|
52
|
+
* State Management:
|
|
53
|
+
* - isLoggedIn reactive property (true when user authenticated)
|
|
54
|
+
* - isLoading reactive property (true during state initialization)
|
|
55
|
+
* - user reactive property (null when not authenticated)
|
|
56
|
+
* - State synchronized reactively across all components
|
|
57
|
+
*
|
|
58
|
+
* Methods:
|
|
59
|
+
* - login(provider) - Initiate OAuth flow
|
|
60
|
+
* - logout() - End user session
|
|
61
|
+
* - refresh() - Restore authentication state
|
|
62
|
+
*
|
|
63
|
+
* @template T - Custom token payload type extending TokenPayload
|
|
64
|
+
* @returns {UseAuthReturn<T>} Authentication state and methods
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* // Without custom claims (default)
|
|
69
|
+
* const { user, login, logout } = useAuth()
|
|
70
|
+
*
|
|
71
|
+
* // With custom claims
|
|
72
|
+
* import type { CustomTokenClaims } from '#build/types/nuxt-aegis'
|
|
73
|
+
*
|
|
74
|
+
* type AppTokenPayload = CustomTokenClaims<{
|
|
75
|
+
* role: string
|
|
76
|
+
* permissions: string[]
|
|
77
|
+
* organizationId: string
|
|
78
|
+
* }>
|
|
79
|
+
*
|
|
80
|
+
* const { user, login, logout } = useAuth<AppTokenPayload>()
|
|
81
|
+
* // user.value?.role is now type-safe
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function useAuth<T extends TokenPayload = TokenPayload>(): UseAuthReturn<T>;
|
|
85
|
+
export {};
|