npm package: @phila/sso-nuxt
Description: Nuxt 3 module for Azure AD B2C authentication. Installs Pinia, registers a client plugin, auto-imports `useAuth` and `useSSOStore`, and optionally guards all routes.
Latest version: 0.0.9

# @phila/sso-nuxt

Nuxt 3 module for Azure AD B2C authentication. Installs Pinia, registers a client plugin, auto-imports `useAuth` and `useSSOStore`, and optionally guards all routes.

## Installation

```bash
pnpm add @phila/sso-nuxt @phila/sso-core @phila/sso-vue @azure/msal-browser
```

## Setup

Add the module to your `nuxt.config.ts`:

```ts
export default defineNuxtConfig({
  modules: ["@phila/sso-nuxt"],

  sso: {
    globalAuth: true,
    loginPath: "/login",
    excludePaths: ["/logout"],
  },
});
```

## Module Options

| Option                | Type       | Default                   | Description                                                             |
| --------------------- | ---------- | ------------------------- | ----------------------------------------------------------------------- |
| `globalAuth`          | `boolean`  | `true`                    | Register a global route middleware that redirects unauthenticated users |
| `loginPath`           | `string`   | `"/login"`                | Path to redirect unauthenticated users to                               |
| `excludePaths`        | `string[]` | `["/logout"]`             | Paths that bypass the auth middleware (matched with `startsWith`)       |
| `signInPolicy`        | `string`   | `"B2C_1A_SIGNUP_SIGNIN"`  | B2C sign-up/sign-in user flow                                           |
| `signInOnlyPolicy`    | `string`   | `"B2C_1A_AD_SIGNIN_ONLY"` | B2C sign-in-only user flow (for city employees)                         |
| `resetPasswordPolicy` | `string`   | `"B2C_1A_PASSWORDRESET"`  | B2C password reset user flow                                            |

## Runtime Configuration

The module exposes all SSO settings via Nuxt runtime config. Set them in `nuxt.config.ts` or override with environment variables:

| Runtime Config Key       | Environment Variable                    | Default                      |
| ------------------------ | --------------------------------------- | ---------------------------- |
| `ssoClientId`            | `NUXT_PUBLIC_SSO_CLIENT_ID`             | `""`                         |
| `ssoRedirectUri`         | `NUXT_PUBLIC_SSO_REDIRECT_URI`          | `"http://localhost:3000"`    |
| `ssoTenant`              | `NUXT_PUBLIC_SSO_TENANT`                | `"PhilaB2CDev"`              |
| `ssoAuthorityDomain`     | `NUXT_PUBLIC_SSO_AUTHORITY_DOMAIN`      | `"PhilaB2CDev.b2clogin.com"` |
| `ssoApiScope`            | `NUXT_PUBLIC_SSO_API_SCOPE`             | `""`                         |
| `ssoSignInPolicy`        | `NUXT_PUBLIC_SSO_SIGN_IN_POLICY`        | from module options          |
| `ssoSignInOnlyPolicy`    | `NUXT_PUBLIC_SSO_SIGN_IN_ONLY_POLICY`   | from module options          |
| `ssoResetPasswordPolicy` | `NUXT_PUBLIC_SSO_RESET_PASSWORD_POLICY` | from module options          |
| `ssoLoginPath`           | `NUXT_PUBLIC_SSO_LOGIN_PATH`            | from module options          |
| `ssoExcludePaths`        | `NUXT_PUBLIC_SSO_EXCLUDE_PATHS`         | from module options          |

Example `.env`:

```env
NUXT_PUBLIC_SSO_CLIENT_ID=your-client-id
NUXT_PUBLIC_SSO_TENANT=YourTenant
NUXT_PUBLIC_SSO_AUTHORITY_DOMAIN=YourTenant.b2clogin.com
NUXT_PUBLIC_SSO_REDIRECT_URI=http://localhost:3000
NUXT_PUBLIC_SSO_API_SCOPE=https://YourTenant.onmicrosoft.com/api/read
```

### API Scopes

`ssoApiScope` accepts a comma-separated string. The module splits it into an array at runtime:

```env
NUXT_PUBLIC_SSO_API_SCOPE=https://tenant.onmicrosoft.com/api/read,https://tenant.onmicrosoft.com/api/write
```

## Auto-Imports

The module auto-imports these composables globally:

- `useAuth()` — primary auth composable (state, actions, utilities)
- `useSSOStore()` — direct Pinia store access

No `import` statements needed in your components:

```vue
<script setup>
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>
```

## Route Middleware

When `globalAuth: true` (default), the module registers a global route middleware that:

1. Waits for auth initialization (`authReady`) before making decisions
2. Allows navigation to `loginPath` and all `excludePaths`
3. Redirects unauthenticated users to `loginPath`

### Excluding Paths

Paths in `excludePaths` are matched using `startsWith`, so `/public` also matches `/public/about`:

```ts
export default defineNuxtConfig({
  sso: {
    excludePaths: ["/logout", "/public", "/callback"],
  },
});
```

The `loginPath` is always excluded automatically.

### Disabling the Middleware

Set `globalAuth: false` to disable the global middleware and handle auth checks manually:

```ts
export default defineNuxtConfig({
  sso: {
    globalAuth: false,
  },
});
```

## What the Module Does

During setup, the module automatically:

1. Installs `@pinia/nuxt` (apps don't need to add it separately)
2. Adds `@phila/sso-core` and `@phila/sso-vue` to the Vite transpile list
3. Configures Vite dev server to serve files from the monorepo root
4. Declares all SSO keys in `runtimeConfig.public`
5. Registers a client-only plugin that initializes the B2C auth client
6. Auto-imports `useAuth` and `useSSOStore`
7. Registers the global auth route middleware (when `globalAuth: true`)

## License

MIT