@cboxdk/id-nuxt 0.1.0 → 0.2.0

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 CHANGED
@@ -5,8 +5,11 @@ Nuxt module for [Cbox ID](https://github.com/cboxdk/laravel-id). It wires the
5
5
  drop-in **sign-in / callback / sign-out** routes, a sealed session, and a
6
6
  `useCboxUser()` composable — add authentication with one module entry.
7
7
 
8
- Pair it with [`@cboxdk/id-vue`](https://github.com/cboxdk/id-vue) for the
9
- `<CboxUserButton>` and other widgets.
8
+ It also bundles the [`@cboxdk/id-vue`](https://github.com/cboxdk/id-vue) widgets
9
+ (`<CboxUserButton>` and friends) as **auto-imported, globally-provided** components:
10
+ no import, no `<CboxIdProvider>` wrapper. The module provides their context app-wide
11
+ from the session and injects the stylesheet through `useHead`, so they render
12
+ correctly during SSR.
10
13
 
11
14
  ## Install
12
15
 
@@ -45,9 +48,32 @@ The module registers these routes for you:
45
48
  | `GET /auth/sign-in` | starts login (accepts `?redirect=/where/next`) |
46
49
  | `GET /auth/callback` | verifies the login and stores the session |
47
50
  | `GET /auth/sign-out` | clears the session and logs out |
51
+ | `GET /auth/account` | redirects to the hosted profile page (accepts `?return_to=`) |
48
52
  | `GET /api/_cbox/user` | the current user as JSON (used internally) |
49
53
 
50
- Link to them and read the user reactively anywhere:
54
+ ### Widgets
55
+
56
+ Drop the widgets in anywhere — they're auto-imported and already wired to the
57
+ session, so this is the whole integration:
58
+
59
+ ```vue
60
+ <template>
61
+ <header>
62
+ <!-- avatar + account menu when signed in, a sign-in button when not -->
63
+ <CboxUserButton />
64
+ </header>
65
+ </template>
66
+ ```
67
+
68
+ Also available: `<CboxSignInButton>`, `<CboxSignOutButton>`, `<CboxUserProfileCard>`,
69
+ `<CboxOrganizationBadge>`, and `<CboxIdProvider>` (for a scoped override). Their
70
+ "Manage account" links point at `GET /auth/account`; "Sign out" at `logoutPath`.
71
+ Theme them with the `appearance` option (below). Set `components: false` to opt out
72
+ and wire `@cboxdk/id-vue` yourself.
73
+
74
+ ### Composable
75
+
76
+ Or read the user reactively and build your own UI:
51
77
 
52
78
  ```vue
53
79
  <script setup lang="ts">
@@ -80,8 +106,11 @@ export default defineNuxtRouteMiddleware(() => {
80
106
  |---|---|---|
81
107
  | `issuer` / `clientId` / `clientSecret` / `redirectUri` | from env | the Cbox ID connection |
82
108
  | `scopes` | `openid profile email` | requested at login |
83
- | `accountPath` | `/settings` | hosted profile page path |
109
+ | `accountPath` | `/settings` | hosted profile page path on the instance |
84
110
  | `loginPath` / `callbackPath` / `logoutPath` | `/auth/*` | override the route paths |
111
+ | `profilePath` | `/auth/account` | app route that redirects to the hosted profile |
112
+ | `appearance` | `{}` | widget theming (`accent`, `accentForeground`, `radius`, `fontFamily`) |
113
+ | `components` | `true` | auto-register the `@cboxdk/id-vue` widgets globally |
85
114
 
86
115
  ## Scope
87
116
 
package/dist/module.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
+ import { CboxWidgetAppearance } from '@cboxdk/id-vue';
2
3
 
3
4
  interface ModuleOptions {
4
5
  /** Base URL of the Cbox ID instance (or set CBOX_ID_ISSUER). */
@@ -19,11 +20,30 @@ interface ModuleOptions {
19
20
  callbackPath?: string;
20
21
  /** Route that signs out. Defaults to /auth/sign-out. */
21
22
  logoutPath?: string;
23
+ /**
24
+ * Route that redirects to the instance's hosted account/profile page (what the
25
+ * widgets' "Manage account" links point at). Defaults to /auth/account.
26
+ */
27
+ profilePath?: string;
28
+ /** Theming applied to the auto-provided widgets. */
29
+ appearance?: CboxWidgetAppearance;
30
+ /**
31
+ * Auto-register the `@cboxdk/id-vue` widget components (`<CboxUserButton>` etc.)
32
+ * as global components and wire their context app-wide. Defaults to `true`.
33
+ */
34
+ components?: boolean;
22
35
  }
23
36
  /**
24
- * Nuxt module for Cbox ID. Registers the sign-in / callback / sign-out routes and a
25
- * `useCboxUser()` composable, all backed by @cboxdk/id-js. Configure it under the
26
- * `cboxId` key or via environment variables.
37
+ * Nuxt module for Cbox ID. Wires the whole embeddable-identity story into a Nuxt app:
38
+ *
39
+ * - server routes for sign-in / callback / sign-out / hosted-profile redirect, backed
40
+ * by `@cboxdk/id-js` and a sealed h3 session;
41
+ * - a `useCboxUser()` composable, SSR-hydrated from that session;
42
+ * - the `@cboxdk/id-vue` widgets (`<CboxUserButton>` and friends) as global,
43
+ * auto-imported components, provided their context app-wide so they work with no
44
+ * `<CboxIdProvider>` boilerplate — SSR-safe, stylesheet injected via `useHead`.
45
+ *
46
+ * Configure it under the `cboxId` key or via environment variables.
27
47
  */
28
48
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
29
49
 
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.0.0"
6
6
  },
7
- "version": "0.1.0",
7
+ "version": "0.2.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,6 +1,14 @@
1
- import { defineNuxtModule, createResolver, addServerHandler, addImportsDir, addPlugin } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addServerHandler, addImportsDir, addPlugin, addComponent } from '@nuxt/kit';
2
2
  import { defu } from 'defu';
3
3
 
4
+ const WIDGET_COMPONENTS = [
5
+ "CboxIdProvider",
6
+ "CboxSignInButton",
7
+ "CboxSignOutButton",
8
+ "CboxUserButton",
9
+ "CboxUserProfileCard",
10
+ "CboxOrganizationBadge"
11
+ ];
4
12
  const module$1 = defineNuxtModule({
5
13
  meta: {
6
14
  name: "@cboxdk/id-nuxt",
@@ -11,7 +19,9 @@ const module$1 = defineNuxtModule({
11
19
  accountPath: "/settings",
12
20
  loginPath: "/auth/sign-in",
13
21
  callbackPath: "/auth/callback",
14
- logoutPath: "/auth/sign-out"
22
+ logoutPath: "/auth/sign-out",
23
+ profilePath: "/auth/account",
24
+ components: true
15
25
  },
16
26
  setup(options, nuxt) {
17
27
  const resolver = createResolver(import.meta.url);
@@ -32,10 +42,11 @@ const module$1 = defineNuxtModule({
32
42
  {
33
43
  loginPath: options.loginPath,
34
44
  logoutPath: options.logoutPath,
35
- accountPath: options.accountPath
45
+ profilePath: options.profilePath,
46
+ appearance: options.appearance ?? {}
36
47
  }
37
48
  );
38
- nuxt.options.build.transpile.push(resolver.resolve("./runtime"));
49
+ nuxt.options.build.transpile.push(resolver.resolve("./runtime"), "@cboxdk/id-vue");
39
50
  addServerHandler({
40
51
  route: options.loginPath,
41
52
  method: "get",
@@ -51,6 +62,11 @@ const module$1 = defineNuxtModule({
51
62
  method: "get",
52
63
  handler: resolver.resolve("./runtime/server/routes/sign-out.get")
53
64
  });
65
+ addServerHandler({
66
+ route: options.profilePath,
67
+ method: "get",
68
+ handler: resolver.resolve("./runtime/server/routes/account.get")
69
+ });
54
70
  addServerHandler({
55
71
  route: "/api/_cbox/user",
56
72
  method: "get",
@@ -58,6 +74,11 @@ const module$1 = defineNuxtModule({
58
74
  });
59
75
  addImportsDir(resolver.resolve("./runtime/composables"));
60
76
  addPlugin(resolver.resolve("./runtime/plugin"));
77
+ if (options.components) {
78
+ for (const name of WIDGET_COMPONENTS) {
79
+ addComponent({ name, export: name, filePath: "@cboxdk/id-vue", mode: "all" });
80
+ }
81
+ }
61
82
  }
62
83
  });
63
84
 
@@ -1,3 +1,8 @@
1
- /** Hydrate the current user from the server session once, on app start. */
1
+ /**
2
+ * Hydrate the current user from the server session, then provide the widget context
3
+ * app-wide so `@cboxdk/id-vue` components work anywhere without a `<CboxIdProvider>`.
4
+ * Runs on both server and client (so SSR renders the signed-in state), and injects
5
+ * the widget stylesheet via `useHead` — SSR-safe, no `document` access at import time.
6
+ */
2
7
  declare const _default: any;
3
8
  export default _default;
@@ -1,13 +1,26 @@
1
- import { defineNuxtPlugin, useRequestFetch } from "#imports";
1
+ import { defineNuxtPlugin, useHead, useRequestFetch, useRuntimeConfig } from "#imports";
2
+ import { computed } from "vue";
3
+ import { CSS, CboxIdKey, STYLE_ID } from "@cboxdk/id-vue";
2
4
  import { useCboxUser } from "./composables/useCboxUser.js";
3
- export default defineNuxtPlugin(async () => {
5
+ export default defineNuxtPlugin(async (nuxtApp) => {
4
6
  const user = useCboxUser();
5
- if (user.value !== null) {
6
- return;
7
- }
8
- try {
9
- user.value = await useRequestFetch()("/api/_cbox/user");
10
- } catch {
11
- user.value = null;
7
+ if (user.value === null) {
8
+ try {
9
+ user.value = await useRequestFetch()("/api/_cbox/user");
10
+ } catch {
11
+ user.value = null;
12
+ }
12
13
  }
14
+ const config = useRuntimeConfig().public.cboxId;
15
+ const appearance = config.appearance ?? {};
16
+ const urls = {
17
+ signIn: config.loginPath,
18
+ signOut: config.logoutPath,
19
+ profile: config.profilePath
20
+ };
21
+ nuxtApp.vueApp.provide(
22
+ CboxIdKey,
23
+ computed(() => ({ user: user.value, urls, appearance }))
24
+ );
25
+ useHead({ style: [{ id: STYLE_ID, key: STYLE_ID, innerHTML: CSS }] });
13
26
  });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Redirect to the instance's hosted account/profile page (self-service password,
3
+ * MFA, passkeys, sessions). `return_to` is honoured so the page can link back; it
4
+ * defaults to the app's origin.
5
+ */
6
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
7
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import { defineEventHandler, getQuery, getRequestURL, sendRedirect } from "h3";
2
+ import { getCboxClient } from "../utils/client.js";
3
+ export default defineEventHandler((event) => {
4
+ const client = getCboxClient(event);
5
+ const returnTo = getQuery(event).return_to;
6
+ const origin = getRequestURL(event).origin;
7
+ return sendRedirect(event, client.profileUrl(typeof returnTo === "string" ? returnTo : origin));
8
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cboxdk/id-nuxt",
3
- "version": "0.1.0",
4
- "description": "Nuxt module for Cbox ID drop-in sign-in / callback / sign-out routes and session handling built on @cboxdk/id-js. Add authentication to a Nuxt app with one module entry.",
3
+ "version": "0.2.0",
4
+ "description": "Nuxt module for Cbox ID \u2014 drop-in sign-in / callback / sign-out routes and session handling built on @cboxdk/id-js. Add authentication to a Nuxt app with one module entry.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Sylvester Damgaard <sn@cbox.dk>",
@@ -42,17 +42,26 @@
42
42
  "scripts": {
43
43
  "prepare-types": "nuxt-module-build prepare",
44
44
  "build": "nuxt-module-build build",
45
- "check": "npm run prepare-types && npm run build"
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "check": "npm run prepare-types && npm run build && npm run test"
46
48
  },
47
49
  "dependencies": {
48
- "@cboxdk/id-js": "^0.1.0",
50
+ "@cboxdk/id-js": "^0.2.0",
51
+ "@cboxdk/id-vue": "^0.2.0",
49
52
  "@nuxt/kit": "^3.0.0 || ^4.0.0",
50
53
  "defu": "^6.1.0",
51
54
  "h3": "^1.13.0"
52
55
  },
56
+ "peerDependencies": {
57
+ "vue": "^3.4.0"
58
+ },
53
59
  "devDependencies": {
54
60
  "@nuxt/module-builder": "^1.0.0",
55
61
  "@nuxt/schema": "^3.0.0 || ^4.0.0",
56
- "typescript": "^5.9.2"
62
+ "nuxt": "^3.0.0",
63
+ "typescript": "^5.9.2",
64
+ "vitest": "^4.1.0",
65
+ "vue": "^3.5.0"
57
66
  }
58
67
  }