@moreillon/vue-oidc 0.0.14 → 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/README.md CHANGED
@@ -1,41 +1,85 @@
1
1
  # Vue OIDC
2
2
 
3
- ## Usage example
3
+ Vue 3 plugin and composable for OpenID Connect authentication, built on [@moreillon/oidc-auth](https://www.npmjs.com/package/@moreillon/oidc-auth).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @moreillon/vue-oidc
9
+ ```
10
+
11
+ `vue` (^3.5) is a peer dependency.
12
+
13
+ ## Setup
4
14
 
5
15
  ```ts
6
16
  import { createApp } from "vue";
7
- import { createPinia } from "pinia";
8
17
  import { auth } from "@moreillon/vue-oidc";
9
18
  import App from "./App.vue";
10
- import router from "./router";
11
-
12
- const app = createApp(App);
13
19
 
14
20
  const { VITE_OIDC_AUTHORITY, VITE_OIDC_CLIENT_ID } = import.meta.env;
15
21
 
16
- app.use(auth, {
17
- authority: VITE_OIDC_AUTHORITY,
18
- client_id: VITE_OIDC_CLIENT_ID,
19
- });
20
- app.use(createPinia());
21
- app.use(router);
22
-
23
- app.mount("#app");
22
+ createApp(App)
23
+ .use(auth, {
24
+ authority: VITE_OIDC_AUTHORITY,
25
+ client_id: VITE_OIDC_CLIENT_ID,
26
+ })
27
+ .mount("#app");
24
28
  ```
25
29
 
26
- Then in your components:
30
+ ### Options
31
+
32
+ | Option | Type | Description |
33
+ | ------------------ | ------------------------ | -------------------------------------------------------------------------------- |
34
+ | `authority` | `string` | URL of the OIDC provider (required) |
35
+ | `client_id` | `string` | OIDC client ID (required) |
36
+ | `redirect_uri` | `string` | Where the provider redirects after login. Defaults to the current page |
37
+ | `extraQueryParams` | `Record<string, string>` | Additional query parameters sent to the authorization endpoint |
38
+ | `enforce` | `boolean` | Redirect unauthenticated users to the provider immediately instead of letting them in anonymously |
39
+
40
+ ## Usage
41
+
42
+ Initialization is asynchronous and the app mounts before it finishes, so wait for `ready` before rendering anything that depends on authentication:
27
43
 
28
44
  ```vue
29
45
  <script setup lang="ts">
30
46
  import { useAuth } from "@moreillon/vue-oidc";
31
47
 
32
- const { user } = useAuth();
48
+ const { ready, error, user, login, logout } = useAuth();
33
49
  </script>
34
50
 
35
51
  <template>
36
- <h1>User</h1>
37
- <p>
38
- {{ user }}
39
- </p>
52
+ <p v-if="!ready">Loading...</p>
53
+ <p v-else-if="error">Authentication failed: {{ error }}</p>
54
+ <template v-else-if="user">
55
+ <p>Hello {{ user.name }}</p>
56
+ <button @click="logout">Log out</button>
57
+ </template>
58
+ <button v-else @click="login">Log in</button>
40
59
  </template>
41
60
  ```
61
+
62
+ ### `useAuth()`
63
+
64
+ All refs are read-only.
65
+
66
+ | Property | Type | Description |
67
+ | -------------- | --------------------------------- | --------------------------------------------------------------------------- |
68
+ | `ready` | `Ref<boolean>` | `true` once initialization has finished (successfully or not) |
69
+ | `error` | `Ref<unknown>` | Error thrown during initialization, if any |
70
+ | `user` | `Ref<User \| null>` | User info of the authenticated user |
71
+ | `access_token` | `Ref<string \| null>` | Current access token, kept up to date on refresh |
72
+ | `client` | `Ref<OidcClient \| null>` | Underlying `@moreillon/oidc-auth` client |
73
+ | `login()` | `() => void` | Redirect to the provider's login page |
74
+ | `logout()` | `() => void` | Log out at the provider |
75
+
76
+ `useAuth` accepts a type parameter for custom claims: `useAuth<MyUser>()`.
77
+
78
+ The state is shared at module level, so `useAuth()` also works outside components, e.g. in router guards or API clients:
79
+
80
+ ```ts
81
+ const { access_token } = useAuth();
82
+ fetch("/api/items", {
83
+ headers: { Authorization: `Bearer ${access_token.value}` },
84
+ });
85
+ ```
@@ -1,81 +1,33 @@
1
1
  import type OidcClient from "@moreillon/oidc-auth";
2
- declare function login(): void;
3
- declare function logout(): void;
4
- export default function (): {
5
- ready: import("vue").Ref<boolean, boolean>;
6
- client: import("vue").Ref<{
7
- cookieName: string;
8
- options: {
9
- redirect_uri: string;
10
- authority: string;
11
- client_id: string;
12
- extraQueryParams?: {
13
- [key: string]: string;
14
- } | undefined;
15
- };
16
- openidConfig?: {
17
- authorization_endpoint: string;
18
- token_endpoint: string;
19
- userinfo_endpoint: string;
20
- end_session_endpoint: string;
21
- } | undefined;
22
- refreshEventHandlers: Function[];
23
- init: (enforce?: boolean) => Promise<import("@moreillon/oidc-auth").InitOutput | undefined>;
24
- sendUserToAuthUrl: (saveHref: Boolean) => Promise<void>;
25
- fetchOidcConfig: () => Promise<any>;
26
- generateAuthUrl: () => Promise<string>;
27
- createTimeoutForTokenExpiry: (oidConfig: import("@moreillon/oidc-auth").OidcConfig) => void;
28
- makeCookieOptions: () => string;
29
- makeExpiryDate: (expires_in: number) => Date;
30
- isExpired: (expires_at: string) => boolean;
31
- saveAuthDataInCookies: (data: {
32
- expires_in: number;
33
- }) => void;
34
- exchangeCodeForToken: (code: string) => Promise<void>;
35
- getUser: () => Promise<any>;
36
- refreshAccessToken: (oidcConfig: import("@moreillon/oidc-auth").OidcConfig) => Promise<void>;
37
- onTokenRefreshed: (handler: (oidcData: import("@moreillon/oidc-auth").Tokens) => void) => void;
38
- runRefreshEventHandlers: () => void;
39
- login: () => void;
40
- logout: () => void;
41
- } | null, OidcClient | {
42
- cookieName: string;
43
- options: {
44
- redirect_uri: string;
45
- authority: string;
46
- client_id: string;
47
- extraQueryParams?: {
48
- [key: string]: string;
49
- } | undefined;
50
- };
51
- openidConfig?: {
52
- authorization_endpoint: string;
53
- token_endpoint: string;
54
- userinfo_endpoint: string;
55
- end_session_endpoint: string;
56
- } | undefined;
57
- refreshEventHandlers: Function[];
58
- init: (enforce?: boolean) => Promise<import("@moreillon/oidc-auth").InitOutput | undefined>;
59
- sendUserToAuthUrl: (saveHref: Boolean) => Promise<void>;
60
- fetchOidcConfig: () => Promise<any>;
61
- generateAuthUrl: () => Promise<string>;
62
- createTimeoutForTokenExpiry: (oidConfig: import("@moreillon/oidc-auth").OidcConfig) => void;
63
- makeCookieOptions: () => string;
64
- makeExpiryDate: (expires_in: number) => Date;
65
- isExpired: (expires_at: string) => boolean;
66
- saveAuthDataInCookies: (data: {
67
- expires_in: number;
68
- }) => void;
69
- exchangeCodeForToken: (code: string) => Promise<void>;
70
- getUser: () => Promise<any>;
71
- refreshAccessToken: (oidcConfig: import("@moreillon/oidc-auth").OidcConfig) => Promise<void>;
72
- onTokenRefreshed: (handler: (oidcData: import("@moreillon/oidc-auth").Tokens) => void) => void;
73
- runRefreshEventHandlers: () => void;
74
- login: () => void;
75
- logout: () => void;
2
+ import type { User } from "@moreillon/oidc-auth";
3
+ export declare const state: {
4
+ client: import("vue").ShallowRef<OidcClient | null, OidcClient | null>;
5
+ user: import("vue").Ref<{
6
+ [x: string]: unknown;
7
+ name: string;
8
+ email: string;
9
+ family_name: string;
10
+ given_name: string;
11
+ } | null, User | {
12
+ [x: string]: unknown;
13
+ name: string;
14
+ email: string;
15
+ family_name: string;
16
+ given_name: string;
76
17
  } | null>;
77
- user: import("vue").Ref<any, any>;
78
18
  access_token: import("vue").Ref<string | null, string | null>;
19
+ ready: import("vue").Ref<boolean, boolean>;
20
+ error: import("vue").ShallowRef<unknown, unknown>;
21
+ };
22
+ export declare function resetState(): void;
23
+ declare function login(): void;
24
+ declare function logout(): void;
25
+ export default function useAuth<T extends User = User>(): {
26
+ ready: Readonly<import("vue").Ref<boolean, boolean>>;
27
+ error: import("vue").ComputedRef<unknown>;
28
+ client: import("vue").ComputedRef<OidcClient | null>;
29
+ user: import("vue").ComputedRef<T | null>;
30
+ access_token: Readonly<import("vue").Ref<string | null, string | null>>;
79
31
  login: typeof login;
80
32
  logout: typeof logout;
81
33
  };
@@ -1,24 +1,38 @@
1
- import { ref } from "vue";
2
- const client = ref(null);
3
- const user = ref(null);
4
- const access_token = ref(null);
5
- const ready = ref(false);
1
+ import { computed, readonly, ref, shallowRef } from "vue";
2
+ // Module-level state so that useAuth() also works outside of components
3
+ // (e.g. in router navigation guards). Only the plugin may write to it.
4
+ export const state = {
5
+ client: shallowRef(null),
6
+ user: ref(null),
7
+ access_token: ref(null),
8
+ ready: ref(false),
9
+ error: shallowRef(null),
10
+ };
11
+ export function resetState() {
12
+ state.client.value = null;
13
+ state.user.value = null;
14
+ state.access_token.value = null;
15
+ state.ready.value = false;
16
+ state.error.value = null;
17
+ }
18
+ function getClient() {
19
+ if (!state.client.value)
20
+ throw new Error("OIDC Client not instantiated");
21
+ return state.client.value;
22
+ }
6
23
  function login() {
7
- if (!client.value)
8
- throw new Error("OIDC Client not instanciated");
9
- client.value.login();
24
+ getClient().login();
10
25
  }
11
26
  function logout() {
12
- if (!client.value)
13
- throw new Error("OIDC Client not instanciated");
14
- client.value.logout();
27
+ getClient().logout();
15
28
  }
16
- export default function () {
29
+ export default function useAuth() {
17
30
  return {
18
- ready,
19
- client,
20
- user,
21
- access_token,
31
+ ready: readonly(state.ready),
32
+ error: computed(() => state.error.value),
33
+ client: computed(() => state.client.value),
34
+ user: computed(() => state.user.value),
35
+ access_token: readonly(state.access_token),
22
36
  login,
23
37
  logout,
24
38
  };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
- import auth from "./plugins/auth";
2
- import useAuth from "./composables/useAuth";
1
+ import auth from "./plugins/auth.js";
2
+ import useAuth from "./composables/useAuth.js";
3
3
  export { auth, useAuth };
4
+ export type { Options } from "./plugins/auth.js";
5
+ export type { User, Tokens } from "@moreillon/oidc-auth";
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import auth from "./plugins/auth";
2
- import useAuth from "./composables/useAuth";
1
+ import auth from "./plugins/auth.js";
2
+ import useAuth from "./composables/useAuth.js";
3
3
  export { auth, useAuth };
@@ -1,14 +1,9 @@
1
1
  import type { App } from "vue";
2
- export type Options = {
3
- authority: string;
4
- client_id: string;
5
- extraQueryParams?: {
6
- [key: string]: string;
7
- };
8
- redirect_uri?: string;
2
+ import type { UserProvidedOptions } from "@moreillon/oidc-auth";
3
+ export type Options = UserProvidedOptions & {
9
4
  enforce?: boolean;
10
5
  };
11
6
  declare const _default: {
12
- install(app: App, options: Options): Promise<void>;
7
+ install(_app: App, options: Options): Promise<void>;
13
8
  };
14
9
  export default _default;
@@ -1,20 +1,32 @@
1
- import useAuth from "../composables/useAuth";
2
1
  import OidcClient from "@moreillon/oidc-auth";
3
- export default {
4
- async install(app, options) {
5
- const { enforce, ...oidcOptions } = options;
6
- const auth = useAuth();
7
- auth.client.value = new OidcClient(oidcOptions);
8
- const oidcData = await auth.client.value.init(enforce);
2
+ import { resetState, state } from "../composables/useAuth.js";
3
+ async function init(options) {
4
+ const { enforce, ...oidcOptions } = options;
5
+ resetState();
6
+ try {
7
+ const client = new OidcClient(oidcOptions);
8
+ state.client.value = client;
9
+ const oidcData = await client.init(enforce);
10
+ if (oidcData) {
11
+ state.access_token.value = oidcData.tokens.access_token;
12
+ state.user.value = oidcData.user;
13
+ client.onTokenRefreshed(({ access_token }) => {
14
+ state.access_token.value = access_token;
15
+ });
16
+ }
17
+ // When enforced without data, the user is being redirected to the provider
9
18
  if (!enforce || oidcData)
10
- auth.ready.value = true;
11
- if (!oidcData)
12
- return;
13
- const { user, tokens: { access_token }, } = oidcData;
14
- auth.user.value = user;
15
- auth.access_token.value = access_token;
16
- auth.client.value.onTokenRefreshed(({ access_token }) => {
17
- auth.access_token.value = access_token;
18
- });
19
+ state.ready.value = true;
20
+ }
21
+ catch (error) {
22
+ state.error.value = error;
23
+ state.ready.value = true;
24
+ }
25
+ }
26
+ export default {
27
+ // Vue does not await plugin installation: returned promise is for tests / callers
28
+ // who want to wait. Apps should rely on the `ready` ref from useAuth().
29
+ install(_app, options) {
30
+ return init(options);
19
31
  },
20
32
  };
package/package.json CHANGED
@@ -1,28 +1,31 @@
1
1
  {
2
2
  "name": "@moreillon/vue-oidc",
3
- "version": "0.0.14",
3
+ "version": "0.1.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
7
7
  ],
8
8
  "main": "dist/index.js",
9
9
  "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ }
15
+ },
10
16
  "scripts": {
11
- "build": "tsc"
17
+ "build": "tsc",
18
+ "test": "vitest run"
19
+ },
20
+ "peerDependencies": {
21
+ "vue": "^3.5.0"
12
22
  },
13
23
  "dependencies": {
14
- "@moreillon/oidc-auth": "^3.0.5",
15
- "vue": "^3.5.17"
24
+ "@moreillon/oidc-auth": "^3.0.6"
16
25
  },
17
26
  "devDependencies": {
18
- "@tsconfig/node22": "^22.0.2",
19
- "@types/node": "^22.15.32",
20
- "@vitejs/plugin-vue": "^6.0.0",
21
- "@vue/tsconfig": "^0.7.0",
22
- "npm-run-all2": "^8.0.4",
23
27
  "typescript": "~5.8.0",
24
- "vite": "^7.0.0",
25
- "vite-plugin-vue-devtools": "^7.7.7",
26
- "vue-tsc": "^2.2.10"
28
+ "vitest": "^4.1.12",
29
+ "vue": "^3.5.44"
27
30
  }
28
31
  }