@wocha/vue
v0.1.0
Published
Vue 3 composables and components for Wocha authentication (PKCE SPA flow)
Maintainers
Readme
@wocha/vue
Vue 3 composables and headless components for Wocha authentication in single-page applications. Implements the OAuth 2.0 authorisation code flow with PKCE as a public client — no client secret required.
Install
npm install @wocha/vue
# or: pnpm add / yarn add / bun add @wocha/vuePeer dependency: Vue 3.4+.
Quick start
Install the plugin in your app entry point and register a /callback route for the OAuth redirect:
// main.ts
import { createApp } from "vue";
import { GreetPlugin } from "@wocha/vue";
import App from "./App.vue";
const app = createApp(App);
app.use(GreetPlugin, {
config: {
issuer: "https://my-tenant.auth.wocha.ai",
clientId: "your-client-id",
redirectUri: window.location.origin + "/callback",
},
});
app.mount("#app");Register your redirect URI (https://your-app.com/callback) in the Wocha Console before testing.
Configuration reference
Pass a WochaAuthConfig object to GreetPlugin:
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| issuer | string | Yes | OIDC issuer URL (e.g. https://my-tenant.auth.wocha.ai). |
| clientId | string | Yes | OAuth client ID for your SPA application. |
| redirectUri | string | Yes | Callback URL registered with your OAuth client. |
| scope | string | No | Space-separated scopes. Default: openid profile email org_id offline_access. |
| audience | string | No | API audience claim, if your tenant requires it. |
| apiUrl | string | No | Customer API base URL for permission checks. Defaults to {tenant}.api.wocha.ai/v1 on Wocha Cloud. |
| storage | "session" \| "local" | No | Where refresh tokens are persisted. Default: session. |
| onRedirectCallback | (appState?: unknown) => void | No | Called after a successful OAuth callback. |
Composables
All composables must be used after app.use(GreetPlugin, …).
useSession()
Reactive session state with loading, authenticated, and unauthenticated statuses:
<script setup lang="ts">
import { useSession } from "@wocha/vue";
const { data, status, error, refresh } = useSession();
</script>
<template>
<p v-if="status === 'loading'">Loading…</p>
<p v-else-if="status === 'authenticated'">Hello, {{ data?.email }}</p>
<p v-else>Please sign in</p>
</template>useUser()
Returns the current user and auth status:
const { user, status } = useUser();useOrg()
Organisation context with switchOrg():
<script setup lang="ts">
import { useOrg } from "@wocha/vue";
const { orgId, orgIds, switchOrg, isSwitching } = useOrg();
</script>
<template>
<select
:value="orgId"
:disabled="isSwitching"
@change="switchOrg(($event.target as HTMLSelectElement).value)"
>
<option v-for="id in orgIds" :key="id" :value="id">{{ id }}</option>
</select>
</template>Some org switches require re-authorisation; switchOrg handles the redirect automatically when needed.
usePermission(check)
SpiceDB permission check against the Customer API:
<script setup lang="ts">
import { usePermission } from "@wocha/vue";
const { allowed, isLoading } = usePermission({
resource: { type: "document", id: "doc-123" },
permission: "edit",
});
</script>
<template>
<button v-if="allowed && !isLoading">Edit</button>
</template>useWochaAuth()
Imperative auth actions:
const { signIn, signOut, switchOrg, getAccessToken, isAuthenticated, isLoading } = useWochaAuth();
await signIn({ appState: { returnTo: "/dashboard" } });
await signIn({ signup: true });
signOut();login and logout are aliases for signIn and signOut.
Components
Headless components using slots:
<template>
<Authenticated>
<Dashboard />
</Authenticated>
<Unauthenticated>
<SignInPrompt />
</Unauthenticated>
<Protect
:permission="{ resource: { type: 'document', id: docId }, permission: 'edit' }"
>
<EditForm />
</Protect>
</template>
<script setup lang="ts">
import { Authenticated, Unauthenticated, Protect } from "@wocha/vue";
</script>Also available: SignIn, SignUp, and OrgSwitcher with optional scoped slots.
Security model
- Public client (PKCE): No client secret in the browser. Suitable for SPAs only.
- Token storage: Refresh tokens use
sessionStorageby default (cleared when the tab closes). Access tokens are kept in memory and refreshed automatically. - Multi-tab sync: Login, logout, and org switches broadcast across tabs via
BroadcastChannel. - No server-side session: Use
@wocha/sveltekitor@wocha/nextjsfor BFF patterns with httpOnly cookies.
Environment variables
Vite requires VITE_-prefixed variables (VITE_WOCHA_ISSUER, VITE_WOCHA_CLIENT_ID). Values are baked in at build time — restart the dev server after changing .env.
Related packages
| Package | Use when |
|---------|----------|
| @wocha/sveltekit | SvelteKit with server-side sessions |
| @wocha/nextjs | Next.js App Router with server-side sessions |
| @wocha/react | React SPA with the same PKCE flow |
| @wocha/sdk | Server-side user/org management via the Management API |
Troubleshooting
redirect_uri_mismatch
The redirectUri must exactly match a redirect URI registered in the Wocha Console.
CSRF / state_mismatch
Clear site storage for your origin and retry. Do not start a second login flow before the first completes.
CORS errors
Confirm the OAuth application allows your SPA origin for browser-based token requests.
