Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions examples/example-nuxt-web/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
NUXT_AUTH0_DOMAIN=
NUXT_AUTH0_CLIENT_ID=
NUXT_AUTH0_CLIENT_SECRET=
NUXT_AUTH0_SESSION_SECRET=
NUXT_AUTH0_APP_BASE_URL=
24 changes: 24 additions & 0 deletions examples/example-nuxt-web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist

# Node dependencies
node_modules

# Logs
logs
*.log

# Misc
.DS_Store
.fleet
.idea

# Local env files
.env
.env.*
!.env.example
48 changes: 48 additions & 0 deletions examples/example-nuxt-web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Nuxt Example

This example demonstrates how to use the `auth0-server-js` package to authenticate users in a Nuxt application.

## Install dependencies

Install the dependencies using npm:

```bash
npm install
```

## Configuration

Rename `.env.example` to `.env` and configure the domain and audience:

```ts
NUXT_AUTH0_DOMAIN=YOUR_AUTH0_DOMAIN
NUXT_AUTH0_CLIENT_ID=YOUR_AUTH0_CLIENT_ID
NUXT_AUTH0_CLIENT_SECRET=YOUR_AUTH0_CLIENT_SECRET
NUXT_AUTH0_SESSION_SECRET=YOUR_AUTH0_SESSION_SECRET
NUXT_AUTH0_APP_BASE_URL=http://localhost:3000
```

The `NUXT_AUTH0_SESSION_SECRET` is the key used to encrypt the session cookie. You can generate a secret using `openssl`:

```shell
openssl rand -hex 64
```

The `NUXT_AUTH0_APP_BASE_URL` is the URL that your application is running on. When developing locally, this is most commonly `http://localhost:3000`.

With the configuration in place, the example can be started by running:

```bash
npm run start
```

The application has 3 routes:

- `/`: The home route, displaying a message depending on the authentication state.
- `/public`: A public route that can be accessed without authentication.
- `/private`: A private route that can only be accessed by authenticated users.


In order to access the `/private` and route, you need to ensure the user is authenticated.

Additionally, navigating to the `/private` endpoint, without being authenticated, will redirect the user to the Auth0, and then redirect them back to the `/private` route after authentication.
61 changes: 61 additions & 0 deletions examples/example-nuxt-web/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script setup lang="ts">
const { user, isAuthenticated } = await useAuth();

// We can also interact with the SDK like this
if (import.meta.server) {
const auth0Client = useAuth0Client();
const { accessToken } = await auth0Client.getAccessToken();
console.log(accessToken);
}
</script>

<template>
<nav class="py-2 bg-body-tertiary border-bottom">
<div class="container d-flex flex-wrap">
<a
href="/"
class="d-flex align-items-center mb-2 mb-lg-0 text-white text-decoration-none"
>
<img src="/img/auth0.png" width="36" height="36" alt="Auth0 logo" />
</a>
<ul class="nav me-auto">
<li class="nav-item">
<a
href="/public"
class="nav-link link-body-emphasis px-2 active"
aria-current="page"
>Public Page</a
>
</li>
<li class="nav-item">
<a
href="/private"
class="nav-link link-body-emphasis px-2 active"
aria-current="page"
>Private Page</a
>
</li>
</ul>
<ul class="nav">
<li class="nav-item">
<a
v-if="isAuthenticated"
class="nav-link link-body-emphasis px-2"
href="/auth/logout"
>Log out ({{ user?.name }})</a
>

<a
v-if="!isAuthenticated"
class="nav-link link-body-emphasis px-2"
href="/auth/login"
>Log in</a
>
</li>
</ul>
</div>
</nav>
<div class="container py-4">
<NuxtPage />
</div>
</template>
8 changes: 8 additions & 0 deletions examples/example-nuxt-web/auth0/composables/use-auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const useAuth = () => {
const session = useSession();

return {
user: session.value ? session.value.user : null,
isAuthenticated: !!session.value?.user,
};
};
18 changes: 18 additions & 0 deletions examples/example-nuxt-web/auth0/composables/use-auth0-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { StartInteractiveLoginOptions } from '@auth0/auth0-server-js';

export const useAuth0Client = () => {
const nuxtApp = useNuxtApp();
const h3Event = nuxtApp.ssrContext!.event;
const auth0Client = h3Event?.context.auth0Client;

// TODO: Expose all methods here without the StoreOptions argument
// Doing so, we keep the complexity away from the user and simplify interacting with the SDK in a Nuxt application
return {
startInteractiveLogin: (options?: StartInteractiveLoginOptions) =>
auth0Client?.startInteractiveLogin(options, { event: h3Event }),
completeInteractiveLogin: (url: URL) =>
auth0Client?.completeInteractiveLogin(url, { event: h3Event }),
getUser: () => auth0Client?.getUser({ event: h3Event }),
getAccessToken: () => auth0Client?.getAccessToken({ event: h3Event }),
};
};
3 changes: 3 additions & 0 deletions examples/example-nuxt-web/auth0/composables/use-session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { SessionData } from '@auth0/auth0-server-js';

export const useSession = () => useState<SessionData | undefined>('auth0_session', () => undefined);
49 changes: 49 additions & 0 deletions examples/example-nuxt-web/auth0/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
defineNuxtModule,
createResolver,
addServerHandler,
addServerPlugin,
addRouteMiddleware
} from '@nuxt/kit';

export default defineNuxtModule({
meta: {
name: 'auth0-nuxt',
configKey: 'auth0',
},
async setup(options, nuxt) {
const resolver = createResolver(import.meta.url);

addServerPlugin(resolver.resolve('./server/plugins/auth.server'));

addRouteMiddleware({ name: 'auth0', path: resolver.resolve('./middleware/auth.server'), global: true });

addServerHandler({
handler: resolver.resolve('./server/api/auth/login.get'),
route: '/auth/login',
method: 'get',
});

addServerHandler({
handler: resolver.resolve('./server/api/auth/callback.get'),
route: '/auth/callback',
method: 'get',
});

addServerHandler({
handler: resolver.resolve('./server/api/auth/logout.get'),
route: '/auth/logout',
method: 'get',
});

addServerHandler({
handler: resolver.resolve('./server/api/auth/profile.get'),
route: '/auth/profile',
method: 'get',
});

nuxt.hook('imports:dirs', (dirs) => {
dirs.push(resolver.resolve('./composables'))
})
},
});
11 changes: 11 additions & 0 deletions examples/example-nuxt-web/auth0/middleware/auth.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default defineNuxtRouteMiddleware(async (to, from) => {
if (import.meta.server) {
const app = useNuxtApp();
const h3Event = app.ssrContext!.event;
const auth0Client = h3Event.context.auth0Client;

const session = await auth0Client.getSession({ event: h3Event });

useSession().value = session;
}
});
12 changes: 12 additions & 0 deletions examples/example-nuxt-web/auth0/server/api/auth/callback.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineEventHandler, sendRedirect } from 'h3';

export default defineEventHandler(async (event) => {
// TODO: See if there are alternative / better ways to access auth0Client and auth0ClientOptions
const auth0Client = event.context.auth0Client;
const auth0ClientOptions = event.context.auth0ClientOptions;
const { appState } = await auth0Client.completeInteractiveLogin<
{ returnTo: string } | undefined
>(new URL(event.node.req.url as string, auth0ClientOptions.appBaseUrl), { event });

sendRedirect(event, appState?.returnTo ?? auth0ClientOptions.appBaseUrl);
});
18 changes: 18 additions & 0 deletions examples/example-nuxt-web/auth0/server/api/auth/login.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineEventHandler, sendRedirect } from 'h3';

export default defineEventHandler(async (event) => {
// TODO: See if there are alternative / better ways to access auth0Client and auth0ClientOptions
const auth0Client = event.context.auth0Client;
const auth0ClientOptions = event.context.auth0ClientOptions;
const query = getQuery(event);
const returnTo = query.returnTo ?? auth0ClientOptions.appBaseUrl;

const authorizationUrl = await auth0Client.startInteractiveLogin(
{
appState: { returnTo },
},
{ event }
);

sendRedirect(event, authorizationUrl.href);
});
15 changes: 15 additions & 0 deletions examples/example-nuxt-web/auth0/server/api/auth/logout.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineEventHandler, sendRedirect } from 'h3';

export default defineEventHandler(async (event) => {
// TODO: See if there are alternative / better ways to access auth0Client and auth0ClientOptions
const auth0Client = event.context.auth0Client;
const auth0ClientOptions = event.context.auth0ClientOptions;

const returnTo = auth0ClientOptions.appBaseUrl;
const logoutUrl = await auth0Client.logout(
{ returnTo: returnTo.toString() },
{ event }
);

sendRedirect(event, logoutUrl.href);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineEventHandler } from 'h3';

export default defineEventHandler(async (event) => {
// TODO: See if there are alternative / better ways to access auth0Client
const auth0Client = event.context.auth0Client ;
const session = await auth0Client.getSession({ event });

return session?.user;
});
39 changes: 39 additions & 0 deletions examples/example-nuxt-web/auth0/server/plugins/auth.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ServerClient } from '@auth0/auth0-server-js';
import { defineNitroPlugin } from 'nitropack/dist/runtime/plugin';
import { CookieTransactionStore } from '~/store/cookie-transaction-store';
import { StatelessStateStore } from '~/store/stateless-state-store';

declare module 'h3' {
interface H3EventContext {
auth0Client: ServerClient<{ event: H3Event }>;
}
}

export default defineNitroPlugin((nitroApp) => {
const config = useRuntimeConfig();
const options: any = config.auth0;

const callbackPath = '/auth/callback';
const redirectUri = new URL(callbackPath, options.appBaseUrl);

const auth0Client = new ServerClient({
domain: options.domain,
clientId: options.clientId,
clientSecret: options.clientSecret,
authorizationParams: {
redirect_uri: redirectUri.toString(),
},
transactionStore: new CookieTransactionStore(),
stateStore: new StatelessStateStore({
secret: options.sessionSecret,
}),
});

nitroApp.hooks.hook('request', async (event) => {
// TODO: See if there are alternative / better ways to set auth0Client, auth0ClientOptions and user
event.context.auth0Client = auth0Client;
event.context.auth0ClientOptions = {
appBaseUrl: options.appBaseUrl,
};
});
});
36 changes: 36 additions & 0 deletions examples/example-nuxt-web/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2024-11-01',
devtools: { enabled: true },
modules: ['./auth0'],
runtimeConfig: {
auth0: {
domain: '', // isoverridden by NUXT_AUTH0_DOMAIN environment variable
clientId: '', // isoverridden by NUXT_AUTH0_CLIENT_ID environment variable
clientSecret: '', // isoverridden by NUXT_AUTH0_CLIENT_SECRET environment variable
sessionSecret: '', // isoverridden by NUXT_AUTH0_SESSION_SECRET environment variable
appBaseUrl: '', // isoverridden by NUXT_AUTH0_APP_BASE_URL environment variable
},
},
app: {
head: {
script: [
{
src: 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js',
integrity:
'sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz',
crossorigin: 'anonymous',
},
],
link: [
{
rel: 'stylesheet',
href: 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css',
integrity:
'sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH',
crossorigin: 'anonymous',
},
],
},
},
});
18 changes: 18 additions & 0 deletions examples/example-nuxt-web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "example-nuxt-web",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"start": "npm run dev"
},
"dependencies": {
"nuxt": "^3.16.2",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
}
}
Loading