@ricardoqmd/auth-vue 0.1.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/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/index.cjs +72 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +43 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ricardoqmd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# @ricardoqmd/auth-vue
|
|
2
|
+
|
|
3
|
+
> Vue 3 client-side bindings for `@ricardoqmd/auth-core`. A `createAuth` plugin and a reactive `useAuth()` composable with RBAC helpers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @ricardoqmd/auth-core @ricardoqmd/auth-keycloak @ricardoqmd/auth-vue vue
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> Scope is SPA / client-only (ADR-012). The plugin eagerly initializes the auth
|
|
12
|
+
> flow on install, which assumes a browser. It is SSR-ready by construction (one
|
|
13
|
+
> actor per app instance, never a module-level singleton) but SSR is not a
|
|
14
|
+
> supported target in v0.x.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
### 1. Create the provider
|
|
19
|
+
|
|
20
|
+
Create the Keycloak provider **outside** the plugin call so the instance is
|
|
21
|
+
created once. The binding is IDP-agnostic — pass any `AuthProvider`.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// src/auth.ts
|
|
25
|
+
import { createKeycloakProvider } from "@ricardoqmd/auth-keycloak";
|
|
26
|
+
|
|
27
|
+
export const provider = createKeycloakProvider({
|
|
28
|
+
config: {
|
|
29
|
+
url: import.meta.env.VITE_KC_URL,
|
|
30
|
+
realm: import.meta.env.VITE_KC_REALM,
|
|
31
|
+
clientId: import.meta.env.VITE_KC_CLIENT_ID,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Install the plugin
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// src/main.ts
|
|
40
|
+
import { createApp } from "vue";
|
|
41
|
+
import { createAuth } from "@ricardoqmd/auth-vue";
|
|
42
|
+
import App from "./App.vue";
|
|
43
|
+
import { provider } from "./auth";
|
|
44
|
+
|
|
45
|
+
createApp(App).use(createAuth({ provider })).mount("#app");
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 3. Use auth state in any component
|
|
49
|
+
|
|
50
|
+
`useAuth()` returns reactive state. The state values are `ComputedRef`s — read
|
|
51
|
+
them with `.value` in `<script setup>`; in the template they are auto-unwrapped,
|
|
52
|
+
so you can write `isAuthenticated` directly without `.value`.
|
|
53
|
+
|
|
54
|
+
```vue
|
|
55
|
+
<script setup lang="ts">
|
|
56
|
+
import { useAuth } from "@ricardoqmd/auth-vue";
|
|
57
|
+
import type { KeycloakIdpClaims } from "@ricardoqmd/auth-keycloak";
|
|
58
|
+
|
|
59
|
+
const { user, isAuthenticated, logout, hasRole, hasAnyRole } =
|
|
60
|
+
useAuth<KeycloakIdpClaims>();
|
|
61
|
+
|
|
62
|
+
// In script context, ComputedRefs need .value:
|
|
63
|
+
function reportAdmin() {
|
|
64
|
+
console.log("is admin?", hasRole("admin"));
|
|
65
|
+
console.log("authenticated?", isAuthenticated.value);
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<template>
|
|
70
|
+
<!-- In templates, refs are auto-unwrapped (no .value): -->
|
|
71
|
+
<p>Welcome, {{ user?.preferred_username }}</p>
|
|
72
|
+
<button v-if="hasRole('admin')">Admin panel</button>
|
|
73
|
+
<button v-if="hasAnyRole(['editor', 'admin'])">Edit</button>
|
|
74
|
+
<button @click="logout">Sign out</button>
|
|
75
|
+
</template>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`useAuth<TIdpClaims>()` is generic over the IDP claims shape. Pass your adapter's
|
|
79
|
+
claims interface (e.g. `KeycloakIdpClaims`) for typed access to `idpClaims`. For
|
|
80
|
+
the universal role checks, use `hasRole()` / `hasAnyRole()`; for provider-specific
|
|
81
|
+
checks (e.g. Keycloak resource roles), import utilities from your adapter package.
|
|
82
|
+
|
|
83
|
+
### Sign-in on demand (`check-sso` flows)
|
|
84
|
+
|
|
85
|
+
When the provider is configured for `check-sso`, the app starts unauthenticated.
|
|
86
|
+
Call `login()` from the composable to start the redirect:
|
|
87
|
+
|
|
88
|
+
```vue
|
|
89
|
+
<script setup lang="ts">
|
|
90
|
+
import { useAuth } from "@ricardoqmd/auth-vue";
|
|
91
|
+
|
|
92
|
+
const { isAuthenticated, login, logout } = useAuth();
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<template>
|
|
96
|
+
<button v-if="isAuthenticated" @click="logout">Sign out</button>
|
|
97
|
+
<button v-else @click="login">Sign in</button>
|
|
98
|
+
</template>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Gating the app while auth settles
|
|
102
|
+
|
|
103
|
+
There is no built-in `AuthGate` component in v0.x. Gate at the root with `v-if`
|
|
104
|
+
on `isLoading` and `error`, then render your app once auth is settled:
|
|
105
|
+
|
|
106
|
+
```vue
|
|
107
|
+
<script setup lang="ts">
|
|
108
|
+
import { useAuth } from "@ricardoqmd/auth-vue";
|
|
109
|
+
|
|
110
|
+
const { isLoading, error, isAuthenticated } = useAuth();
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<template>
|
|
114
|
+
<p v-if="isLoading">Signing in…</p>
|
|
115
|
+
<p v-else-if="error">Authentication failed: {{ error.message }}</p>
|
|
116
|
+
<RouterView v-else-if="isAuthenticated" />
|
|
117
|
+
<LoginScreen v-else />
|
|
118
|
+
</template>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## API
|
|
122
|
+
|
|
123
|
+
### `createAuth(options)`
|
|
124
|
+
|
|
125
|
+
Returns a Vue `Plugin`. Install with `app.use(createAuth({ provider }))`. Creates
|
|
126
|
+
one auth actor per app instance, starts it, sends `INIT`, and provides it app-wide.
|
|
127
|
+
|
|
128
|
+
| Option | Type | Description |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| `provider` | `AuthProvider<TIdpClaims>` | Adapter instance from `createKeycloakProvider()` (or any IDP adapter). Create it once, outside the plugin call. |
|
|
131
|
+
|
|
132
|
+
### `useAuth<TIdpClaims>()`
|
|
133
|
+
|
|
134
|
+
Must be called in `setup()` of a component whose app installed `createAuth()`.
|
|
135
|
+
Throws otherwise. Returns an `AuthState<TIdpClaims>`:
|
|
136
|
+
|
|
137
|
+
| Field | Type | Description |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| `isLoading` | `ComputedRef<boolean>` | True during `initializing` or `loggingOut` |
|
|
140
|
+
| `isAuthenticated` | `ComputedRef<boolean>` | True when the machine is in the `authenticated` state |
|
|
141
|
+
| `token` | `ComputedRef<string \| null>` | Raw JWT access token |
|
|
142
|
+
| `user` | `ComputedRef<AuthUserClaims \| null>` | Decoded standard OIDC claims (`preferred_username`, `email`, `name`, `sub`, `roles`, …) |
|
|
143
|
+
| `idpClaims` | `ComputedRef<TIdpClaims \| null>` | IDP-specific token claims; pass your IDP's claims interface to `useAuth<T>()` |
|
|
144
|
+
| `error` | `ComputedRef<AuthError \| null>` | Structured error set in the `error` state; branch on `error.code` |
|
|
145
|
+
| `login` | `() => void` | Starts the login redirect (useful with `check-sso`) |
|
|
146
|
+
| `logout` | `() => void` | Triggers the logout flow |
|
|
147
|
+
| `hasRole` | `(role: string) => boolean` | True if `user.roles` includes `role` |
|
|
148
|
+
| `hasAnyRole` | `(roles: string[]) => boolean` | True if the user has at least one of the given roles |
|
|
149
|
+
|
|
150
|
+
## Handling errors
|
|
151
|
+
|
|
152
|
+
`error` is a structured `AuthError` from `@ricardoqmd/auth-core`, not a plain
|
|
153
|
+
`Error`. Branch on `error.code` to drive UX. `code` is one of `INIT_FAILED`,
|
|
154
|
+
`REFRESH_FAILED`, `TOKEN_EXPIRED`, or `NETWORK_ERROR`. New codes may be added
|
|
155
|
+
over time, so always handle `default`. (`TOKEN_EXPIRED` is reserved and not
|
|
156
|
+
currently emitted by `auth-keycloak` — a dead refresh token rejects as
|
|
157
|
+
`REFRESH_FAILED`; see ADR-009.)
|
|
158
|
+
|
|
159
|
+
## Status
|
|
160
|
+
|
|
161
|
+
**Pre-1.0.** The public API mirrors `@ricardoqmd/auth-nextjs` and shares the
|
|
162
|
+
`@ricardoqmd/auth-core` contract.
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT © [ricardoqmd](https://github.com/ricardoqmd)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var xstate = require('xstate');
|
|
4
|
+
var authCore = require('@ricardoqmd/auth-core');
|
|
5
|
+
var vue = require('vue');
|
|
6
|
+
var vue$1 = require('@xstate/vue');
|
|
7
|
+
|
|
8
|
+
// src/plugin.ts
|
|
9
|
+
|
|
10
|
+
// src/injection-key.ts
|
|
11
|
+
var AUTH_INJECTION_KEY = /* @__PURE__ */ Symbol("ricardoqmd-auth");
|
|
12
|
+
|
|
13
|
+
// src/plugin.ts
|
|
14
|
+
function createAuth(options) {
|
|
15
|
+
return {
|
|
16
|
+
install(app) {
|
|
17
|
+
const machine = authCore.createAuthMachine(options.provider);
|
|
18
|
+
const actor = xstate.createActor(machine);
|
|
19
|
+
actor.start();
|
|
20
|
+
actor.send({ type: "INIT" });
|
|
21
|
+
app.provide(AUTH_INJECTION_KEY, actor);
|
|
22
|
+
const unmountable = app;
|
|
23
|
+
if (typeof unmountable.onUnmount === "function") {
|
|
24
|
+
unmountable.onUnmount(() => actor.stop());
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function useAuth() {
|
|
30
|
+
const actor = vue.inject(AUTH_INJECTION_KEY);
|
|
31
|
+
if (!actor) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
"useAuth() must be called within an app that installed the auth plugin. Did you forget app.use(createAuth({ provider }))?"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const stateValue = vue$1.useSelector(actor, (s) => s.value);
|
|
37
|
+
const context = vue$1.useSelector(actor, (s) => s.context);
|
|
38
|
+
const isAuthenticated = vue.computed(
|
|
39
|
+
() => typeof stateValue.value === "object" && stateValue.value !== null && "authenticated" in stateValue.value
|
|
40
|
+
);
|
|
41
|
+
const topState = vue.computed(
|
|
42
|
+
() => typeof stateValue.value === "string" ? stateValue.value : Object.keys(stateValue.value)[0]
|
|
43
|
+
);
|
|
44
|
+
const isLoading = vue.computed(
|
|
45
|
+
() => topState.value === "initializing" || topState.value === "loggingOut"
|
|
46
|
+
);
|
|
47
|
+
const token = vue.computed(() => context.value.token);
|
|
48
|
+
const user = vue.computed(() => context.value.user);
|
|
49
|
+
const idpClaims = vue.computed(() => context.value.idpClaims);
|
|
50
|
+
const error = vue.computed(() => context.value.error);
|
|
51
|
+
const login = () => actor.send({ type: "LOGIN" });
|
|
52
|
+
const logout = () => actor.send({ type: "LOGOUT" });
|
|
53
|
+
const hasRole = (role) => user.value?.roles?.includes(role) ?? false;
|
|
54
|
+
const hasAnyRole = (roles) => roles.some((r) => user.value?.roles?.includes(r) ?? false);
|
|
55
|
+
return {
|
|
56
|
+
isLoading,
|
|
57
|
+
isAuthenticated,
|
|
58
|
+
token,
|
|
59
|
+
user,
|
|
60
|
+
idpClaims,
|
|
61
|
+
error,
|
|
62
|
+
login,
|
|
63
|
+
logout,
|
|
64
|
+
hasRole,
|
|
65
|
+
hasAnyRole
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
exports.createAuth = createAuth;
|
|
70
|
+
exports.useAuth = useAuth;
|
|
71
|
+
//# sourceMappingURL=index.cjs.map
|
|
72
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/injection-key.ts","../src/plugin.ts","../src/composable.ts"],"names":["createAuthMachine","createActor","inject","useSelector","computed"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { InjectionKey } from \"vue\";\nimport type { Actor } from \"xstate\";\nimport type { createAuthMachine } from \"@ricardoqmd/auth-core\";\n\n/** The started auth actor type, derived from the core machine factory. */\nexport type AuthActor<TIdpClaims = unknown> = Actor<\n ReturnType<typeof createAuthMachine<TIdpClaims>>\n>;\n\n/** App-wide injection key for the auth actor created by the createAuth plugin. */\nexport const AUTH_INJECTION_KEY: InjectionKey<AuthActor> = Symbol(\"ricardoqmd-auth\");\n","import type { App, Plugin } from \"vue\";\nimport { createActor } from \"xstate\";\nimport { createAuthMachine, type AuthProvider } from \"@ricardoqmd/auth-core\";\nimport { AUTH_INJECTION_KEY, type AuthActor } from \"./injection-key.js\";\n\nexport interface CreateAuthOptions<TIdpClaims = unknown> {\n /** Adapter instance (e.g. createKeycloakProvider()). IDP-agnostic. */\n provider: AuthProvider<TIdpClaims>;\n}\n\n/**\n * Vue plugin wiring @ricardoqmd/auth-core into a Vue app.\n *\n * Creates ONE auth actor per app instance (SSR-safe: never a module-level\n * singleton), starts it, kicks off initialization, and provides it app-wide\n * for useAuth(). Scope is SPA/client-only (ADR-012): the eager INIT below\n * assumes a browser. SSR-ready by construction (per-app instance) but not\n * SSR-supported.\n */\nexport function createAuth<TIdpClaims = unknown>(\n options: CreateAuthOptions<TIdpClaims>,\n): Plugin {\n return {\n install(app: App) {\n const machine = createAuthMachine<TIdpClaims>(options.provider);\n const actor = createActor(machine);\n actor.start();\n actor.send({ type: \"INIT\" });\n app.provide(AUTH_INJECTION_KEY, actor as AuthActor);\n // Stop the actor when the app unmounts (avoids leaks in tests/HMR).\n // app.onUnmount requires Vue 3.5+; guard so older runtimes still install.\n const unmountable = app as App & { onUnmount?: (cb: () => void) => void };\n if (typeof unmountable.onUnmount === \"function\") {\n unmountable.onUnmount(() => actor.stop());\n }\n },\n };\n}\n","import { computed, inject, type ComputedRef } from \"vue\";\nimport { useSelector } from \"@xstate/vue\";\nimport type { AuthError, AuthUserClaims } from \"@ricardoqmd/auth-core\";\nimport { AUTH_INJECTION_KEY } from \"./injection-key.js\";\n\n/** Reactive auth state + helpers returned by useAuth(). */\nexport interface AuthState<TIdpClaims = unknown> {\n isLoading: ComputedRef<boolean>;\n isAuthenticated: ComputedRef<boolean>;\n token: ComputedRef<string | null>;\n user: ComputedRef<AuthUserClaims | null>;\n idpClaims: ComputedRef<TIdpClaims | null>;\n error: ComputedRef<AuthError | null>;\n login: () => void;\n logout: () => void;\n hasRole: (role: string) => boolean;\n hasAnyRole: (roles: string[]) => boolean;\n}\n\n/**\n * Reactive authentication state for the current app.\n * Must be called inside a component of an app that installed createAuth().\n */\nexport function useAuth<TIdpClaims = unknown>(): AuthState<TIdpClaims> {\n const actor = inject(AUTH_INJECTION_KEY);\n if (!actor) {\n throw new Error(\n \"useAuth() must be called within an app that installed the auth plugin. \" +\n \"Did you forget app.use(createAuth({ provider }))?\",\n );\n }\n\n const stateValue = useSelector(actor, (s) => s.value);\n const context = useSelector(actor, (s) => s.context);\n\n const isAuthenticated = computed(\n () =>\n typeof stateValue.value === \"object\" &&\n stateValue.value !== null &&\n \"authenticated\" in stateValue.value,\n );\n\n const topState = computed(() =>\n typeof stateValue.value === \"string\"\n ? stateValue.value\n : Object.keys(stateValue.value)[0],\n );\n\n const isLoading = computed(\n () => topState.value === \"initializing\" || topState.value === \"loggingOut\",\n );\n\n const token = computed(() => context.value.token);\n const user = computed(() => context.value.user);\n const idpClaims = computed(() => context.value.idpClaims as TIdpClaims | null);\n const error = computed(() => context.value.error);\n\n const login = () => actor.send({ type: \"LOGIN\" });\n const logout = () => actor.send({ type: \"LOGOUT\" });\n\n const hasRole = (role: string) =>\n user.value?.roles?.includes(role) ?? false;\n const hasAnyRole = (roles: string[]) =>\n roles.some((r) => user.value?.roles?.includes(r) ?? false);\n\n return {\n isLoading,\n isAuthenticated,\n token,\n user,\n idpClaims,\n error,\n login,\n logout,\n hasRole,\n hasAnyRole,\n };\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Plugin, ComputedRef } from 'vue';
|
|
2
|
+
import { AuthProvider, AuthUserClaims, AuthError, createAuthMachine } from '@ricardoqmd/auth-core';
|
|
3
|
+
export { AuthError, AuthInitResult, AuthProvider, AuthTokens, AuthUserClaims, LogoutOptions } from '@ricardoqmd/auth-core';
|
|
4
|
+
import { Actor } from 'xstate';
|
|
5
|
+
|
|
6
|
+
interface CreateAuthOptions<TIdpClaims = unknown> {
|
|
7
|
+
/** Adapter instance (e.g. createKeycloakProvider()). IDP-agnostic. */
|
|
8
|
+
provider: AuthProvider<TIdpClaims>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Vue plugin wiring @ricardoqmd/auth-core into a Vue app.
|
|
12
|
+
*
|
|
13
|
+
* Creates ONE auth actor per app instance (SSR-safe: never a module-level
|
|
14
|
+
* singleton), starts it, kicks off initialization, and provides it app-wide
|
|
15
|
+
* for useAuth(). Scope is SPA/client-only (ADR-012): the eager INIT below
|
|
16
|
+
* assumes a browser. SSR-ready by construction (per-app instance) but not
|
|
17
|
+
* SSR-supported.
|
|
18
|
+
*/
|
|
19
|
+
declare function createAuth<TIdpClaims = unknown>(options: CreateAuthOptions<TIdpClaims>): Plugin;
|
|
20
|
+
|
|
21
|
+
/** Reactive auth state + helpers returned by useAuth(). */
|
|
22
|
+
interface AuthState<TIdpClaims = unknown> {
|
|
23
|
+
isLoading: ComputedRef<boolean>;
|
|
24
|
+
isAuthenticated: ComputedRef<boolean>;
|
|
25
|
+
token: ComputedRef<string | null>;
|
|
26
|
+
user: ComputedRef<AuthUserClaims | null>;
|
|
27
|
+
idpClaims: ComputedRef<TIdpClaims | null>;
|
|
28
|
+
error: ComputedRef<AuthError | null>;
|
|
29
|
+
login: () => void;
|
|
30
|
+
logout: () => void;
|
|
31
|
+
hasRole: (role: string) => boolean;
|
|
32
|
+
hasAnyRole: (roles: string[]) => boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Reactive authentication state for the current app.
|
|
36
|
+
* Must be called inside a component of an app that installed createAuth().
|
|
37
|
+
*/
|
|
38
|
+
declare function useAuth<TIdpClaims = unknown>(): AuthState<TIdpClaims>;
|
|
39
|
+
|
|
40
|
+
/** The started auth actor type, derived from the core machine factory. */
|
|
41
|
+
type AuthActor<TIdpClaims = unknown> = Actor<ReturnType<typeof createAuthMachine<TIdpClaims>>>;
|
|
42
|
+
|
|
43
|
+
export { type AuthActor, type AuthState, type CreateAuthOptions, createAuth, useAuth };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Plugin, ComputedRef } from 'vue';
|
|
2
|
+
import { AuthProvider, AuthUserClaims, AuthError, createAuthMachine } from '@ricardoqmd/auth-core';
|
|
3
|
+
export { AuthError, AuthInitResult, AuthProvider, AuthTokens, AuthUserClaims, LogoutOptions } from '@ricardoqmd/auth-core';
|
|
4
|
+
import { Actor } from 'xstate';
|
|
5
|
+
|
|
6
|
+
interface CreateAuthOptions<TIdpClaims = unknown> {
|
|
7
|
+
/** Adapter instance (e.g. createKeycloakProvider()). IDP-agnostic. */
|
|
8
|
+
provider: AuthProvider<TIdpClaims>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Vue plugin wiring @ricardoqmd/auth-core into a Vue app.
|
|
12
|
+
*
|
|
13
|
+
* Creates ONE auth actor per app instance (SSR-safe: never a module-level
|
|
14
|
+
* singleton), starts it, kicks off initialization, and provides it app-wide
|
|
15
|
+
* for useAuth(). Scope is SPA/client-only (ADR-012): the eager INIT below
|
|
16
|
+
* assumes a browser. SSR-ready by construction (per-app instance) but not
|
|
17
|
+
* SSR-supported.
|
|
18
|
+
*/
|
|
19
|
+
declare function createAuth<TIdpClaims = unknown>(options: CreateAuthOptions<TIdpClaims>): Plugin;
|
|
20
|
+
|
|
21
|
+
/** Reactive auth state + helpers returned by useAuth(). */
|
|
22
|
+
interface AuthState<TIdpClaims = unknown> {
|
|
23
|
+
isLoading: ComputedRef<boolean>;
|
|
24
|
+
isAuthenticated: ComputedRef<boolean>;
|
|
25
|
+
token: ComputedRef<string | null>;
|
|
26
|
+
user: ComputedRef<AuthUserClaims | null>;
|
|
27
|
+
idpClaims: ComputedRef<TIdpClaims | null>;
|
|
28
|
+
error: ComputedRef<AuthError | null>;
|
|
29
|
+
login: () => void;
|
|
30
|
+
logout: () => void;
|
|
31
|
+
hasRole: (role: string) => boolean;
|
|
32
|
+
hasAnyRole: (roles: string[]) => boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Reactive authentication state for the current app.
|
|
36
|
+
* Must be called inside a component of an app that installed createAuth().
|
|
37
|
+
*/
|
|
38
|
+
declare function useAuth<TIdpClaims = unknown>(): AuthState<TIdpClaims>;
|
|
39
|
+
|
|
40
|
+
/** The started auth actor type, derived from the core machine factory. */
|
|
41
|
+
type AuthActor<TIdpClaims = unknown> = Actor<ReturnType<typeof createAuthMachine<TIdpClaims>>>;
|
|
42
|
+
|
|
43
|
+
export { type AuthActor, type AuthState, type CreateAuthOptions, createAuth, useAuth };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createActor } from 'xstate';
|
|
2
|
+
import { createAuthMachine } from '@ricardoqmd/auth-core';
|
|
3
|
+
import { inject, computed } from 'vue';
|
|
4
|
+
import { useSelector } from '@xstate/vue';
|
|
5
|
+
|
|
6
|
+
// src/plugin.ts
|
|
7
|
+
|
|
8
|
+
// src/injection-key.ts
|
|
9
|
+
var AUTH_INJECTION_KEY = /* @__PURE__ */ Symbol("ricardoqmd-auth");
|
|
10
|
+
|
|
11
|
+
// src/plugin.ts
|
|
12
|
+
function createAuth(options) {
|
|
13
|
+
return {
|
|
14
|
+
install(app) {
|
|
15
|
+
const machine = createAuthMachine(options.provider);
|
|
16
|
+
const actor = createActor(machine);
|
|
17
|
+
actor.start();
|
|
18
|
+
actor.send({ type: "INIT" });
|
|
19
|
+
app.provide(AUTH_INJECTION_KEY, actor);
|
|
20
|
+
const unmountable = app;
|
|
21
|
+
if (typeof unmountable.onUnmount === "function") {
|
|
22
|
+
unmountable.onUnmount(() => actor.stop());
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function useAuth() {
|
|
28
|
+
const actor = inject(AUTH_INJECTION_KEY);
|
|
29
|
+
if (!actor) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
"useAuth() must be called within an app that installed the auth plugin. Did you forget app.use(createAuth({ provider }))?"
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const stateValue = useSelector(actor, (s) => s.value);
|
|
35
|
+
const context = useSelector(actor, (s) => s.context);
|
|
36
|
+
const isAuthenticated = computed(
|
|
37
|
+
() => typeof stateValue.value === "object" && stateValue.value !== null && "authenticated" in stateValue.value
|
|
38
|
+
);
|
|
39
|
+
const topState = computed(
|
|
40
|
+
() => typeof stateValue.value === "string" ? stateValue.value : Object.keys(stateValue.value)[0]
|
|
41
|
+
);
|
|
42
|
+
const isLoading = computed(
|
|
43
|
+
() => topState.value === "initializing" || topState.value === "loggingOut"
|
|
44
|
+
);
|
|
45
|
+
const token = computed(() => context.value.token);
|
|
46
|
+
const user = computed(() => context.value.user);
|
|
47
|
+
const idpClaims = computed(() => context.value.idpClaims);
|
|
48
|
+
const error = computed(() => context.value.error);
|
|
49
|
+
const login = () => actor.send({ type: "LOGIN" });
|
|
50
|
+
const logout = () => actor.send({ type: "LOGOUT" });
|
|
51
|
+
const hasRole = (role) => user.value?.roles?.includes(role) ?? false;
|
|
52
|
+
const hasAnyRole = (roles) => roles.some((r) => user.value?.roles?.includes(r) ?? false);
|
|
53
|
+
return {
|
|
54
|
+
isLoading,
|
|
55
|
+
isAuthenticated,
|
|
56
|
+
token,
|
|
57
|
+
user,
|
|
58
|
+
idpClaims,
|
|
59
|
+
error,
|
|
60
|
+
login,
|
|
61
|
+
logout,
|
|
62
|
+
hasRole,
|
|
63
|
+
hasAnyRole
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { createAuth, useAuth };
|
|
68
|
+
//# sourceMappingURL=index.js.map
|
|
69
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/injection-key.ts","../src/plugin.ts","../src/composable.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { InjectionKey } from \"vue\";\nimport type { Actor } from \"xstate\";\nimport type { createAuthMachine } from \"@ricardoqmd/auth-core\";\n\n/** The started auth actor type, derived from the core machine factory. */\nexport type AuthActor<TIdpClaims = unknown> = Actor<\n ReturnType<typeof createAuthMachine<TIdpClaims>>\n>;\n\n/** App-wide injection key for the auth actor created by the createAuth plugin. */\nexport const AUTH_INJECTION_KEY: InjectionKey<AuthActor> = Symbol(\"ricardoqmd-auth\");\n","import type { App, Plugin } from \"vue\";\nimport { createActor } from \"xstate\";\nimport { createAuthMachine, type AuthProvider } from \"@ricardoqmd/auth-core\";\nimport { AUTH_INJECTION_KEY, type AuthActor } from \"./injection-key.js\";\n\nexport interface CreateAuthOptions<TIdpClaims = unknown> {\n /** Adapter instance (e.g. createKeycloakProvider()). IDP-agnostic. */\n provider: AuthProvider<TIdpClaims>;\n}\n\n/**\n * Vue plugin wiring @ricardoqmd/auth-core into a Vue app.\n *\n * Creates ONE auth actor per app instance (SSR-safe: never a module-level\n * singleton), starts it, kicks off initialization, and provides it app-wide\n * for useAuth(). Scope is SPA/client-only (ADR-012): the eager INIT below\n * assumes a browser. SSR-ready by construction (per-app instance) but not\n * SSR-supported.\n */\nexport function createAuth<TIdpClaims = unknown>(\n options: CreateAuthOptions<TIdpClaims>,\n): Plugin {\n return {\n install(app: App) {\n const machine = createAuthMachine<TIdpClaims>(options.provider);\n const actor = createActor(machine);\n actor.start();\n actor.send({ type: \"INIT\" });\n app.provide(AUTH_INJECTION_KEY, actor as AuthActor);\n // Stop the actor when the app unmounts (avoids leaks in tests/HMR).\n // app.onUnmount requires Vue 3.5+; guard so older runtimes still install.\n const unmountable = app as App & { onUnmount?: (cb: () => void) => void };\n if (typeof unmountable.onUnmount === \"function\") {\n unmountable.onUnmount(() => actor.stop());\n }\n },\n };\n}\n","import { computed, inject, type ComputedRef } from \"vue\";\nimport { useSelector } from \"@xstate/vue\";\nimport type { AuthError, AuthUserClaims } from \"@ricardoqmd/auth-core\";\nimport { AUTH_INJECTION_KEY } from \"./injection-key.js\";\n\n/** Reactive auth state + helpers returned by useAuth(). */\nexport interface AuthState<TIdpClaims = unknown> {\n isLoading: ComputedRef<boolean>;\n isAuthenticated: ComputedRef<boolean>;\n token: ComputedRef<string | null>;\n user: ComputedRef<AuthUserClaims | null>;\n idpClaims: ComputedRef<TIdpClaims | null>;\n error: ComputedRef<AuthError | null>;\n login: () => void;\n logout: () => void;\n hasRole: (role: string) => boolean;\n hasAnyRole: (roles: string[]) => boolean;\n}\n\n/**\n * Reactive authentication state for the current app.\n * Must be called inside a component of an app that installed createAuth().\n */\nexport function useAuth<TIdpClaims = unknown>(): AuthState<TIdpClaims> {\n const actor = inject(AUTH_INJECTION_KEY);\n if (!actor) {\n throw new Error(\n \"useAuth() must be called within an app that installed the auth plugin. \" +\n \"Did you forget app.use(createAuth({ provider }))?\",\n );\n }\n\n const stateValue = useSelector(actor, (s) => s.value);\n const context = useSelector(actor, (s) => s.context);\n\n const isAuthenticated = computed(\n () =>\n typeof stateValue.value === \"object\" &&\n stateValue.value !== null &&\n \"authenticated\" in stateValue.value,\n );\n\n const topState = computed(() =>\n typeof stateValue.value === \"string\"\n ? stateValue.value\n : Object.keys(stateValue.value)[0],\n );\n\n const isLoading = computed(\n () => topState.value === \"initializing\" || topState.value === \"loggingOut\",\n );\n\n const token = computed(() => context.value.token);\n const user = computed(() => context.value.user);\n const idpClaims = computed(() => context.value.idpClaims as TIdpClaims | null);\n const error = computed(() => context.value.error);\n\n const login = () => actor.send({ type: \"LOGIN\" });\n const logout = () => actor.send({ type: \"LOGOUT\" });\n\n const hasRole = (role: string) =>\n user.value?.roles?.includes(role) ?? false;\n const hasAnyRole = (roles: string[]) =>\n roles.some((r) => user.value?.roles?.includes(r) ?? false);\n\n return {\n isLoading,\n isAuthenticated,\n token,\n user,\n idpClaims,\n error,\n login,\n logout,\n hasRole,\n hasAnyRole,\n };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ricardoqmd/auth-vue",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vue 3 client-side bindings for @ricardoqmd/auth-core — createAuth plugin, useAuth composable, RBAC helpers",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"auth",
|
|
7
|
+
"vue",
|
|
8
|
+
"vue3",
|
|
9
|
+
"keycloak",
|
|
10
|
+
"composable",
|
|
11
|
+
"rbac"
|
|
12
|
+
],
|
|
13
|
+
"author": "ricardoqmd",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/ricardoqmd/auth.git",
|
|
18
|
+
"directory": "packages/auth-vue"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/ricardoqmd/auth/tree/main/packages/auth-vue",
|
|
21
|
+
"bugs": "https://github.com/ricardoqmd/auth/issues",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./dist/index.cjs",
|
|
24
|
+
"module": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js",
|
|
30
|
+
"require": "./dist/index.cjs"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"sideEffects": false,
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@xstate/vue": "^5.0.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@ricardoqmd/auth-core": "^1.0.0",
|
|
44
|
+
"vue": "^3.5.0",
|
|
45
|
+
"xstate": "^5.20.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@vue/test-utils": "^2.4.6",
|
|
49
|
+
"vue": "^3.5.13",
|
|
50
|
+
"xstate": "^5.20.0",
|
|
51
|
+
"jsdom": "^29.1.1",
|
|
52
|
+
"tsup": "^8.3.5",
|
|
53
|
+
"typescript": "^5.6.3",
|
|
54
|
+
"vitest": "^4.1.5",
|
|
55
|
+
"@ricardoqmd/auth-core": "1.0.0"
|
|
56
|
+
},
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "tsup",
|
|
62
|
+
"dev": "tsup --watch",
|
|
63
|
+
"test": "vitest run",
|
|
64
|
+
"test:watch": "vitest",
|
|
65
|
+
"test:ui": "vitest --ui",
|
|
66
|
+
"test:coverage": "vitest run --coverage",
|
|
67
|
+
"typecheck": "tsc --noEmit",
|
|
68
|
+
"clean": "rm -rf dist .tsbuildinfo"
|
|
69
|
+
}
|
|
70
|
+
}
|