@phila/sso-vue
v0.3.0
Published
Vue 3 adapter for @phila/sso-core
Downloads
267
Readme
@phila/sso-vue
Vue 3 adapter for @phila/sso-core. Provides a Vue plugin, Pinia store, and composables for Azure AD B2C authentication.
Installation
pnpm add @phila/sso-vue @phila/sso-core @azure/msal-browser piniaQuick Start (Vite + B2C)
The createB2CPlugin factory reads VITE_SSO_* keys off the import.meta.env you pass it:
VITE_SSO_CLIENT_ID=your-client-id
VITE_SSO_TENANT=YourTenant
VITE_SSO_AUTHORITY_DOMAIN=YourTenant.b2clogin.com
VITE_SSO_REDIRECT_URI=http://localhost:3000// main.ts
import { createApp } from "vue";
import { createPinia } from "pinia";
import { createB2CPlugin } from "@phila/sso-vue";
import App from "./App.vue";
const app = createApp(App);
app.use(createPinia());
app.use(createB2CPlugin({ env: import.meta.env }));
app.mount("#app");
envmust be passed from your own app's source (i.e. literally writeimport.meta.envat the call site). This package ships pre-built, so it can't read your app's env vars itself —import.meta.env.VITE_SSO_*only resolves when Vite processes it as part of your build.
<!-- App.vue -->
<script setup lang="ts">
import { useAuth } from "@phila/sso-vue";
const { isAuthenticated, userName, authReady, signIn, signOut } = useAuth();
</script>
<template>
<div v-if="!authReady">Loading...</div>
<div v-else-if="isAuthenticated">
<p>Welcome, {{ userName }}</p>
<button @click="signOut()">Sign out</button>
</div>
<div v-else>
<button @click="signIn()">Sign in</button>
</div>
</template>Advanced Setup
For full control over the provider configuration:
import { createSSOPlugin } from "@phila/sso-vue";
import { B2CProvider } from "@phila/sso-core";
app.use(
createSSOPlugin({
clientConfig: {
provider: new B2CProvider({
clientId: "your-client-id",
b2cEnvironment: "YourTenant",
authorityDomain: "YourTenant.b2clogin.com",
redirectUri: "http://localhost:3000",
apiScopes: ["https://YourTenant.onmicrosoft.com/api/read"],
policies: {
signUpSignIn: "B2C_1A_SIGNUP_SIGNIN",
signInOnly: "B2C_1A_AD_SIGNIN_ONLY",
resetPassword: "B2C_1A_PASSWORDRESET",
},
}),
debug: true,
},
}),
);Plugin Options
interface SSOPluginOptions {
clientConfig: SSOClientConfig;
autoInitialize?: boolean; // default: true
}Composables
useAuth()
Primary composable for authentication. Must be called after the plugin is installed.
Reactive state:
| Property | Type | Description |
| ----------------- | ----------------------------- | ------------------------------ |
| isAuthenticated | Ref<boolean> | User is signed in |
| isLoading | Ref<boolean> | Auth operation in progress |
| user | Ref<AccountInfo \| null> | MSAL account info |
| token | Ref<string \| null> | Current access token |
| error | Ref<Error \| null> | Last auth error |
| activePolicy | Ref<string \| null> | Active B2C policy |
| authReady | Ref<boolean> | Initialization complete |
| userName | ComputedRef<string \| null> | Display name from token claims |
Actions:
| Method | Returns | Description |
| ------------------------------ | ------------------------- | -------------------------------- |
| signIn(options?) | Promise<void> | Start sign-in flow |
| signInCityEmployee(options?) | Promise<void> | Sign in with sign-in-only policy |
| signOut(options?) | Promise<void> | Sign out |
| forgotPassword() | Promise<void> | Start password reset |
| acquireToken(options?) | Promise<string \| null> | Get access token |
Utilities:
| Method | Returns | Description |
| --------------- | --------- | ------------------------------------------------------- |
| hasRole(role) | boolean | Check user role from roles or extension_Roles claim |
useSSOClient()
Returns the raw SSOClient instance for advanced use cases (direct event subscription, etc.).
const client = useSSOClient();
client.events.on("auth:tokenAcquired", token => {
/* ... */
});useSSOStore()
Direct access to the Pinia store. Useful when you need store-level reactivity outside of components.
import { useSSOStore } from "@phila/sso-vue";
const store = useSSOStore();
watch(
() => store.isAuthenticated,
val => {
/* ... */
},
);createB2CPlugin Options
interface B2CPluginOptions {
env: ImportMetaEnv; // required — pass your app's import.meta.env
signInPolicy?: string; // default: "B2C_1A_AD_SIGNIN_ONLY"
resetPasswordPolicy?: string; // default: "B2C_1A_PASSWORDRESET"
debug?: boolean; // default: env.DEV
}Broker Plugin
createBrokerPlugin is the Vue 3 plugin for apps that integrate with the phila-identity OAuth broker directly. It installs a BrokerClient (from @phila/sso-core) instead of MSAL, so there is no @azure/msal-browser dependency and no B2C policy configuration needed. The Pinia store and composable shapes are intentionally close to the B2C equivalents so switching is mostly find-and-replace.
Use this when your app is registered with the phila-identity broker. Use createSSOPlugin / createB2CPlugin when your app talks to B2C, Entra, or CIAM directly.
Quick Start
// main.ts
import { createApp } from "vue";
import { createPinia } from "pinia";
import { createBrokerPlugin } from "@phila/sso-vue";
import App from "./App.vue";
const app = createApp(App);
app.use(createPinia());
app.use(
createBrokerPlugin({
clientConfig: {
brokerUrl: import.meta.env.VITE_BROKER_URL, // e.g. "https://identity.phila.gov"
clientId: import.meta.env.VITE_CLIENT_ID,
redirectUri: window.location.origin + "/",
scopes: ["openid", "email"],
},
}),
);
app.mount("#app");<!-- App.vue -->
<script setup lang="ts">
import { useBrokerAuth } from "@phila/sso-vue";
const { isAuthenticated, userName, authReady, signIn, signOut } = useBrokerAuth();
</script>
<template>
<div v-if="!authReady">Loading...</div>
<div v-else-if="isAuthenticated">
<p>Welcome, {{ userName }}</p>
<button @click="signOut()">Sign out</button>
</div>
<div v-else>
<button @click="signIn()">Sign in</button>
</div>
</template>Plugin Options
interface BrokerPluginOptions {
clientConfig: BrokerClientConfig; // Passed directly to BrokerClient — see @phila/sso-core.
autoInitialize?: boolean; // Default: true. Set false to call initialize() yourself.
}useBrokerAuth()
Primary composable for broker-authenticated apps. Must be called after createBrokerPlugin() is installed.
Reactive state:
| Property | Type | Description |
| ------------------- | ----------------------------- | -------------------------------------------------------------------------------------- |
| isAuthenticated | Ref<boolean> | User is signed in |
| isLoading | Ref<boolean> | Auth operation in progress |
| user | Ref<BrokerUser \| null> | User object from /v1/auth/userinfo |
| token | Ref<string \| null> | Current access token |
| setupPasskeyToken | Ref<string \| null> | setup_passkey token from the broker; non-null when passkey:setup scope was granted |
| error | Ref<Error \| null> | Last auth error |
| authReady | Ref<boolean> | True once initialize() has completed |
| userName | ComputedRef<string \| null> | Display name derived from given_name + family_name, falling back to name |
Actions:
| Method | Returns | Description |
| ------------------------ | ------------------------- | ---------------------------------------------------------- |
| signIn(options?) | Promise<void> | Start the PKCE flow |
| signOut(options?) | Promise<void> | Clear the local session and redirect |
| acquireToken(options?) | Promise<string \| null> | Return the current access token, refreshing if near expiry |
Utilities:
| Method | Returns | Description |
| --------------- | --------- | -------------------------------------------- |
| hasRole(role) | boolean | Check for a role in the user's roles claim |
useBrokerStore()
Direct access to the Pinia store. Useful when you need store-level reactivity outside of components.
import { useBrokerStore } from "@phila/sso-vue";
const store = useBrokerStore();
watch(
() => store.isAuthenticated,
val => {
/* ... */
},
);Resource-bound tokens
To get a token scoped to a downstream API, pass resource either at plugin config time (all sign-ins) or per sign-in:
// Per sign-in override:
await signIn({ scopes: ["openid", "api:access"], resource: "https://my-api.example.com" });
// Then call the API with the access_token:
const { token } = useBrokerAuth();
fetch("https://my-api.example.com/data", {
headers: { Authorization: `Bearer ${token.value}` },
});License
MIT
