{"_id":"@cas-system/vue-cas-client","name":"@cas-system/vue-cas-client","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@cas-system/vue-cas-client","version":"1.0.0","description":"Vue 3 CAS (Central Authentication System) Client SDK — composables, plugin, router guards, and Pinia store for seamless SSO integration.","type":"module","main":"./src/index.ts","module":"./src/index.ts","types":"./src/index.ts","exports":{".":{"import":"./src/index.ts","types":"./src/index.ts"}},"sideEffects":false,"scripts":{"typecheck":"vue-tsc --noEmit"},"peerDependencies":{"vue":">=3.4"},"peerDependenciesMeta":{"vue-router":{"optional":true},"pinia":{"optional":true}},"optionalDependencies":{"vue-router":">=4.4","pinia":">=2.2"},"devDependencies":{"typescript":"^5.8.3","vue-tsc":"^2.2.10"},"keywords":["vue","vue3","cas","sso","authentication","central-authentication","oauth","jwt"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/InSol-2021/one-system.git","directory":"packages/vue-cas-client"},"publishConfig":{"access":"public"},"_id":"@cas-system/vue-cas-client@1.0.0","gitHead":"80ec5e5285cd1d810e8222df1dae82376d7da0dc","bugs":{"url":"https://github.com/InSol-2021/one-system/issues"},"homepage":"https://github.com/InSol-2021/one-system#readme","_nodeVersion":"18.19.1","_npmVersion":"10.2.4","dist":{"integrity":"sha512-KfvY+Vz+ZS5UCcU3ebvV8FcCD03yoMwlGpXkB46xPqMbAKAFYVN1n0Ge796DhaeZAu4t2+woQJm+a7xUgKSVJQ==","shasum":"696ea2b264721d89c0081f82474694ec7d3deefb","tarball":"https://registry.npmjs.org/@cas-system/vue-cas-client/-/vue-cas-client-1.0.0.tgz","fileCount":12,"unpackedSize":57715,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCB2WIuIOPt146TGnSALFMt8ZPoIy0qNET7NlsFYtlYywIgKtYSZssTpvkq7W967Pd59RmIIdJFFNk3H/vTSzX++uk="}]},"_npmUser":{"name":"insol_user","email":"insoldev000@gmail.com"},"directories":{},"maintainers":[{"name":"insol_user","email":"insoldev000@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/vue-cas-client_1.0.0_1783485313832_0.25519236405505463"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-08T04:35:13.652Z","1.0.0":"2026-07-08T04:35:13.965Z","modified":"2026-07-08T04:35:14.223Z"},"maintainers":[{"name":"insol_user","email":"insoldev000@gmail.com"}],"description":"Vue 3 CAS (Central Authentication System) Client SDK — composables, plugin, router guards, and Pinia store for seamless SSO integration.","homepage":"https://github.com/InSol-2021/one-system#readme","keywords":["vue","vue3","cas","sso","authentication","central-authentication","oauth","jwt"],"repository":{"type":"git","url":"git+https://github.com/InSol-2021/one-system.git","directory":"packages/vue-cas-client"},"bugs":{"url":"https://github.com/InSol-2021/one-system/issues"},"license":"MIT","readme":"# @cas-system/vue-cas-client\n\n> Vue 3 CAS (Central Authentication System) Client SDK — composables, plugin, router guards, Pinia store, and components for seamless SSO integration.\n\n---\n\n## Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n- [Auth Flow](#auth-flow)\n- [Quick Start](#quick-start)\n- [Plugin Setup](#plugin-setup)\n- [Composables](#composables)\n  - [useCasAuth](#usecasauth)\n  - [useCasUser](#usecasuser)\n- [Vue Router Integration](#vue-router-integration)\n- [Pinia Store](#pinia-store)\n- [CasProtectedView Component](#casprotectedview-component)\n- [Backend Setup](#backend-setup)\n- [API Reference](#api-reference)\n- [Examples](#examples)\n\n---\n\n## Features\n\n- 🔐 **SSO Login / Logout** — redirect-based CAS authentication flow\n- 🧩 **Vue 3 Plugin** — `app.use(CasPlugin, config)` for app-level setup\n- ⚡ **Composables** — `useCasAuth()` and `useCasUser()` with full reactivity\n- 🛡️ **Router Guards** — factory-based `beforeEach` guard with role support\n- 📦 **Pinia Store** — optional store for Pinia-based state management\n- 🎰 **Slot Component** — `<CasProtectedView>` for declarative access control\n- 🌳 **Tree-Shakeable** — named exports only, import what you need\n- 🔒 **Secure by Design** — tokens are validated server-side only\n- 📝 **TypeScript First** — complete type definitions and JSDoc\n\n---\n\n## Installation\n\n```bash\n# npm\nnpm install @cas-system/vue-cas-client\n\n# yarn\nyarn add @cas-system/vue-cas-client\n\n# pnpm\npnpm add @cas-system/vue-cas-client\n```\n\n### Peer Dependencies\n\n| Package      | Version | Required |\n| ------------ | ------- | -------- |\n| `vue`        | ≥ 3.4   | ✅ Yes   |\n| `vue-router` | ≥ 4.4   | Optional |\n| `pinia`      | ≥ 2.2   | Optional |\n\n---\n\n## Auth Flow\n\n```\n┌──────────┐     1. login()      ┌────────────┐\n│  Vue App │ ──────────────────▶ │ CAS Server │\n│          │                     │ /sso/login  │\n└──────────┘                     └─────┬──────┘\n                                       │\n                         2. User authenticates\n                                       │\n                                       ▼\n┌──────────┐   3. Redirect with   ┌────────────┐\n│  Vue App │ ◀───── ?token=JWT ── │ CAS Server │\n│ /callback│                      └────────────┘\n└────┬─────┘\n     │\n     │  4. POST token to backend\n     ▼\n┌──────────┐   5. Validate via         ┌────────────┐\n│ Your     │ ─ POST /api/validate-token ▶│ CAS Server │\n│ Backend  │   { token, client_id,     │            │\n│          │     client_secret }       │            │\n│          │ ◀ { valid, user,          │            │\n│          │     expires_at } ─────────│            │\n└────┬─────┘                           └────────────┘\n     │\n     │  6. Return validated user\n     ▼\n┌──────────┐\n│  Vue App │  ← session stored in sessionStorage\n│          │\n└──────────┘\n```\n\n> **Security**: The JWT is **never** validated in the browser. Your backend\n> receives the token, combines it with `client_id` + `client_secret`, and\n> forwards it to the CAS server. This prevents exposing the `client_secret`.\n\n---\n\n## Quick Start\n\n### 1. Install the plugin\n\n```ts\n// main.ts\nimport { createApp } from 'vue';\nimport { CasPlugin } from '@cas-system/vue-cas-client';\nimport App from './App.vue';\n\nconst app = createApp(App);\n\napp.use(CasPlugin, {\n  serverUrl: 'https://cas.example.com',\n  clientId: 'my-vue-app',\n  callbackUrl: 'https://my-app.com/auth/callback',\n  backendValidateUrl: '/api/auth/validate',\n});\n\napp.mount('#app');\n```\n\n### 2. Use in components\n\n```vue\n<script setup lang=\"ts\">\nimport { useCasAuth } from '@cas-system/vue-cas-client';\n\nconst { user, isAuthenticated, login, logout } = useCasAuth();\n</script>\n\n<template>\n  <div v-if=\"isAuthenticated\">\n    <p>Welcome, {{ user?.username }}!</p>\n    <button @click=\"logout()\">Logout</button>\n  </div>\n  <div v-else>\n    <button @click=\"login()\">Login with SSO</button>\n  </div>\n</template>\n```\n\n### 3. Create a callback page\n\n```vue\n<!-- views/AuthCallback.vue -->\n<script setup lang=\"ts\">\nimport { onMounted } from 'vue';\nimport { useRouter } from 'vue-router';\nimport { useCasAuth } from '@cas-system/vue-cas-client';\n\nconst router = useRouter();\nconst { handleCallback, error } = useCasAuth();\n\nonMounted(async () => {\n  try {\n    await handleCallback();\n    router.push('/dashboard');\n  } catch (e) {\n    console.error('Auth failed:', e);\n  }\n});\n</script>\n\n<template>\n  <div v-if=\"error\">{{ error }}</div>\n  <div v-else>Authenticating…</div>\n</template>\n```\n\n---\n\n## Plugin Setup\n\n### CasPlugin Options\n\n```ts\ninterface CasPluginOptions {\n  /** Base URL of the CAS server (no trailing slash). */\n  serverUrl: string;\n\n  /** The client_id registered on the CAS server. */\n  clientId: string;\n\n  /** Post-login redirect URL. Defaults to `{origin}/auth/callback`. */\n  callbackUrl?: string;\n\n  /** Your backend's token validation endpoint. */\n  backendValidateUrl?: string;\n\n  /**\n   * Auto-handle callback if a `?token=` is present in the URL.\n   * @default true\n   */\n  autoHandleCallback?: boolean;\n}\n```\n\n### Auto Callback Handling\n\nBy default, the plugin checks the URL for a `?token=` query parameter on\ninstall and automatically validates it. Set `autoHandleCallback: false` to\ndisable this and handle callbacks manually.\n\n---\n\n## Composables\n\n### `useCasAuth()`\n\nPrimary composable for authentication state and actions.\n\n```ts\nconst {\n  user,            // ComputedRef<CasUser | null>\n  isAuthenticated, // ComputedRef<boolean>\n  isLoading,       // ComputedRef<boolean>\n  error,           // ComputedRef<string | null>\n  login,           // (returnUrl?: string) => void\n  logout,          // (redirectUrl?: string) => Promise<void>\n  handleCallback,  // () => Promise<CasUser>\n} = useCasAuth();\n```\n\n### `useCasUser()`\n\nUser-focused composable with reactive role checks.\n\n```ts\nconst {\n  user,        // ComputedRef<CasUser | null>\n  roles,       // ComputedRef<string[]>\n  hasRole,     // (role: string) => ComputedRef<boolean>\n  hasAnyRole,  // (roles: string[]) => ComputedRef<boolean>\n  hasAllRoles, // (roles: string[]) => ComputedRef<boolean>\n} = useCasUser();\n\n// Usage in templates:\nconst isAdmin = hasRole('admin');       // ComputedRef<boolean>\nconst canEdit = hasAnyRole(['editor', 'admin']);\n```\n\n---\n\n## Vue Router Integration\n\n### 1. Define routes with `meta`\n\n```ts\n// router/index.ts\nimport { createRouter, createWebHistory } from 'vue-router';\n\nconst routes = [\n  {\n    path: '/',\n    component: () => import('./views/Home.vue'),\n  },\n  {\n    path: '/auth/callback',\n    component: () => import('./views/AuthCallback.vue'),\n  },\n  {\n    path: '/dashboard',\n    component: () => import('./views/Dashboard.vue'),\n    meta: { requiresAuth: true },\n  },\n  {\n    path: '/admin',\n    component: () => import('./views/Admin.vue'),\n    meta: { requiresAuth: true, roles: ['admin'] },\n  },\n];\n\nexport const router = createRouter({\n  history: createWebHistory(),\n  routes,\n});\n```\n\n### 2. Attach the guard\n\n```ts\n// main.ts\nimport { createApp } from 'vue';\nimport { CasPlugin, createCasAuthGuard, CAS_AUTH_KEY } from '@cas-system/vue-cas-client';\nimport { router } from './router';\nimport App from './App.vue';\n\nconst app = createApp(App);\n\napp.use(CasPlugin, {\n  serverUrl: 'https://cas.example.com',\n  clientId: 'my-vue-app',\n  backendValidateUrl: '/api/auth/validate',\n});\n\n// Access the CAS context from the app's provides\nconst casContext = app._context.provides[CAS_AUTH_KEY as symbol];\n\nrouter.beforeEach(createCasAuthGuard(casContext, {\n  redirectToLogin: true,    // Redirect to CAS if not authenticated\n  // roles: ['user'],       // Optional global role requirement\n}));\n\napp.use(router);\napp.mount('#app');\n```\n\n### Guard Options\n\n```ts\ninterface CasGuardOptions {\n  /** Override the CAS login URL. */\n  loginUrl?: string;\n\n  /** Global roles required for all guarded routes. */\n  roles?: string[];\n\n  /**\n   * Redirect to CAS login when unauthenticated.\n   * @default true\n   */\n  redirectToLogin?: boolean;\n}\n```\n\n---\n\n## Pinia Store\n\nThe `useCasStore()` Pinia store is an **optional** alternative for apps\nalready using Pinia for state management.\n\n### Setup\n\n```ts\n// main.ts\nimport { createApp } from 'vue';\nimport { createPinia } from 'pinia';\nimport App from './App.vue';\n\nconst app = createApp(App);\napp.use(createPinia());\napp.mount('#app');\n```\n\n### Usage\n\n```vue\n<script setup lang=\"ts\">\nimport { onMounted } from 'vue';\nimport { useCasStore } from '@cas-system/vue-cas-client';\n\nconst auth = useCasStore();\n\nonMounted(() => {\n  auth.init({\n    serverUrl: 'https://cas.example.com',\n    clientId: 'my-vue-app',\n    backendValidateUrl: '/api/auth/validate',\n  });\n});\n</script>\n\n<template>\n  <div v-if=\"auth.isAuthenticated\">\n    <p>Hello, {{ auth.currentUser?.username }}</p>\n    <p v-if=\"auth.hasRole('admin')\">You are an admin!</p>\n    <button @click=\"auth.logout()\">Logout</button>\n  </div>\n  <div v-else>\n    <button @click=\"auth.login()\">Login</button>\n  </div>\n</template>\n```\n\n### Store API\n\n| Type     | Name              | Description                          |\n| -------- | ----------------- | ------------------------------------ |\n| State    | `user`            | `CasUser \\| null`                    |\n| State    | `token`           | `string \\| null`                     |\n| State    | `isAuthenticated` | `boolean`                            |\n| State    | `isLoading`       | `boolean`                            |\n| State    | `error`           | `string \\| null`                     |\n| Getter   | `currentUser`     | Alias for `user`                     |\n| Getter   | `hasRole`         | `(role: string) => boolean`          |\n| Getter   | `hasAnyRole`      | `(roles: string[]) => boolean`       |\n| Action   | `init(config)`    | Initialise with CAS config           |\n| Action   | `login(url?)`     | Redirect to CAS login                |\n| Action   | `logout(url?)`    | Logout and clear session             |\n| Action   | `handleCallback`  | Validate callback token              |\n| Action   | `checkAuth`       | Re-check auth from sessionStorage    |\n\n---\n\n## CasProtectedView Component\n\nA declarative, slot-based component for conditional rendering based on\nauthentication and role state.\n\n### Basic Usage\n\n```vue\n<template>\n  <CasProtectedView>\n    <p>This is visible only to authenticated users.</p>\n\n    <template #fallback>\n      <p>Please log in to continue.</p>\n    </template>\n  </CasProtectedView>\n</template>\n```\n\n### With Role Check\n\n```vue\n<template>\n  <CasProtectedView :roles=\"['admin']\">\n    <AdminPanel />\n\n    <template #fallback>\n      <p>You do not have permission to view this.</p>\n    </template>\n\n    <template #loading>\n      <LoadingSpinner />\n    </template>\n  </CasProtectedView>\n</template>\n```\n\n### Auto-Redirect\n\n```vue\n<!-- Automatically redirects unauthenticated users to CAS login -->\n<CasProtectedView redirect>\n  <Dashboard />\n</CasProtectedView>\n```\n\n### Props\n\n| Prop       | Type       | Default | Description                                    |\n| ---------- | ---------- | ------- | ---------------------------------------------- |\n| `roles`    | `string[]` | `[]`    | Required roles (user must have **all**)         |\n| `redirect` | `boolean`  | `false` | Auto-redirect to CAS login if unauthenticated  |\n\n### Slots\n\n| Slot       | Description                                |\n| ---------- | ------------------------------------------ |\n| `default`  | Rendered when authenticated & authorized   |\n| `fallback` | Rendered when NOT authenticated/authorized |\n| `loading`  | Rendered during auth operations            |\n\n---\n\n## Backend Setup\n\nYour backend must expose a validation endpoint that the SDK calls. The\nbackend keeps the `client_secret` safe and forwards the token to CAS:\n\n### Example (Node.js / Express)\n\n```ts\n// POST /api/auth/validate\napp.post('/api/auth/validate', async (req, res) => {\n  const { token } = req.body;\n\n  try {\n    const response = await fetch(`${CAS_SERVER_URL}/api/validate-token`, {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({\n        token,\n        client_id: process.env.CAS_CLIENT_ID,\n        client_secret: process.env.CAS_CLIENT_SECRET,\n      }),\n    });\n\n    if (!response.ok) {\n      return res.status(401).json({ error: 'Invalid token' });\n    }\n\n    // CAS replies: { valid: true, user: { id, username, email }, expires_at }\n    // The token is single-use — validate it exactly once here.\n    const data = await response.json();\n    return res.json({ user: data.user });\n  } catch (err) {\n    return res.status(500).json({ error: 'Validation failed' });\n  }\n});\n```\n\n### Example (Laravel)\n\n```php\n// routes/api.php\nRoute::post('/auth/validate', function (Request $request) {\n    $response = Http::post(config('cas.server_url') . '/api/validate-token', [\n        'token'         => $request->input('token'),\n        'client_id'     => config('cas.client_id'),\n        'client_secret' => config('cas.client_secret'),\n    ]);\n\n    if ($response->failed()) {\n        return response()->json(['error' => 'Invalid token'], 401);\n    }\n\n    // CAS replies: { valid, user: { id, username, email }, expires_at }\n    return response()->json(['user' => $response->json('user')]);\n});\n```\n\n---\n\n## API Reference\n\n### `CasClient`\n\n| Method                             | Returns         | Description                          |\n| ---------------------------------- | --------------- | ------------------------------------ |\n| `getLoginUrl(returnUrl?)`          | `string`        | Build the CAS login URL              |\n| `login(returnUrl?)`                | `void`          | Redirect to CAS login                |\n| `extractTokenFromUrl()`            | `string \\| null`| Read token from URL query string     |\n| `validateTokenViaBackend(token)`   | `Promise<CasUser>` | Validate via your backend        |\n| `handleCallback()`                 | `Promise<CasUser>` | Full callback flow                |\n| `getUser()`                        | `CasUser \\| null`| Get user from sessionStorage        |\n| `getToken()`                       | `string \\| null`| Get JWT from sessionStorage          |\n| `isAuthenticated()`                | `boolean`       | Check if session exists              |\n| `logout(redirectUrl?)`             | `Promise<void>` | Logout and redirect                  |\n| `userHasRole(role)`                | `boolean`       | Check single role                    |\n| `userHasAnyRole(roles)`            | `boolean`       | Check any of roles                   |\n| `userHasAllRoles(roles)`           | `boolean`       | Check all roles                      |\n| `clearSession()`                   | `void`          | Clear sessionStorage                 |\n\n### Interfaces\n\n```ts\ninterface CasConfig {\n  serverUrl: string;\n  clientId: string;\n  callbackUrl?: string;\n  backendValidateUrl?: string;\n}\n\ninterface CasUser {\n  id: string | number;\n  username: string;\n  email: string;\n  roles?: string[];\n}\n\ninterface CasAuthState {\n  user: CasUser | null;\n  isAuthenticated: boolean;\n  isLoading: boolean;\n  error: string | null;\n}\n```\n\n---\n\n## Examples\n\n### Protecting Routes with Roles\n\n```ts\nconst routes = [\n  {\n    path: '/settings',\n    component: Settings,\n    meta: { requiresAuth: true },\n  },\n  {\n    path: '/admin/users',\n    component: UserManagement,\n    meta: { requiresAuth: true, roles: ['admin'] },\n  },\n  {\n    path: '/editor',\n    component: Editor,\n    meta: { requiresAuth: true, roles: ['editor'] },\n  },\n];\n```\n\n### Role-Based UI Rendering\n\n```vue\n<script setup lang=\"ts\">\nimport { useCasUser } from '@cas-system/vue-cas-client';\n\nconst { user, hasRole, hasAnyRole } = useCasUser();\n\nconst isAdmin = hasRole('admin');\nconst canManageContent = hasAnyRole(['admin', 'editor']);\n</script>\n\n<template>\n  <nav>\n    <RouterLink to=\"/\">Home</RouterLink>\n    <RouterLink v-if=\"isAdmin\" to=\"/admin\">Admin</RouterLink>\n    <RouterLink v-if=\"canManageContent\" to=\"/editor\">Editor</RouterLink>\n  </nav>\n</template>\n```\n\n### Using with Axios Interceptors\n\n```ts\nimport axios from 'axios';\n\nconst api = axios.create({ baseURL: '/api' });\n\napi.interceptors.request.use((config) => {\n  const token = sessionStorage.getItem('cas_token');\n  if (token) {\n    config.headers.Authorization = `Bearer ${token}`;\n  }\n  return config;\n});\n```\n\n### Combining Plugin + Pinia Store\n\nYou can use both approaches in the same app. The plugin handles the\ninjection-based composables, and the Pinia store provides a global\nreactive store.\n\n```ts\n// main.ts\nimport { createApp } from 'vue';\nimport { createPinia } from 'pinia';\nimport { CasPlugin } from '@cas-system/vue-cas-client';\n\nconst app = createApp(App);\nconst pinia = createPinia();\n\napp.use(pinia);\napp.use(CasPlugin, {\n  serverUrl: 'https://cas.example.com',\n  clientId: 'my-app',\n  backendValidateUrl: '/api/auth/validate',\n});\n\napp.mount('#app');\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-0e4c0194a9d7b80b1c5cfb78995f2655"}