@phila/sso-vue 0.0.9 → 0.2.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/dist/index.d.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  import { AccountInfo } from '@azure/msal-browser';
2
2
  import { App } from 'vue';
3
+ import { BrokerClientConfig } from '@phila/sso-core';
4
+ import { BrokerClientState } from '@phila/sso-core';
5
+ import { BrokerSignInOptions } from '@phila/sso-core';
6
+ import { BrokerSignOutOptions } from '@phila/sso-core';
7
+ import { BrokerTokenOptions } from '@phila/sso-core';
8
+ import { BrokerUser } from '@phila/sso-core';
3
9
  import { ComputedRef } from 'vue';
4
10
  import { Ref } from 'vue';
5
11
  import { SignInOptions } from '@phila/sso-core';
@@ -17,6 +23,17 @@ export declare interface B2CPluginOptions {
17
23
  debug?: boolean;
18
24
  }
19
25
 
26
+ export declare const BROKER_CLIENT_KEY: unique symbol;
27
+
28
+ export declare interface BrokerPluginOptions {
29
+ clientConfig: BrokerClientConfig;
30
+ /**
31
+ * Set false to skip the automatic initialize() call during install.
32
+ * Useful when you want to call initialize() yourself at a specific lifecycle point.
33
+ */
34
+ autoInitialize?: boolean;
35
+ }
36
+
20
37
  /**
21
38
  * Creates a ready-to-use Vue plugin for Azure AD B2C authentication.
22
39
  * Reads connection details from VITE_SSO_* environment variables:
@@ -26,6 +43,10 @@ export declare function createB2CPlugin(options?: B2CPluginOptions): {
26
43
  install(app: App): void;
27
44
  };
28
45
 
46
+ export declare function createBrokerPlugin(options: BrokerPluginOptions): {
47
+ install(app: App): void;
48
+ };
49
+
29
50
  export declare function createSSOPlugin(options: SSOPluginOptions): {
30
51
  install(app: App): void;
31
52
  };
@@ -167,6 +188,125 @@ export declare function useAuth(): {
167
188
  hasRole: (role: string) => boolean;
168
189
  };
169
190
 
191
+ /**
192
+ * Primary composable for broker-authenticated Vue 3 apps.
193
+ * Drop-in replacement for useAuth() — same returned shape, simpler internals.
194
+ *
195
+ * @example
196
+ * const { isAuthenticated, user, signIn, signOut } = useBrokerAuth();
197
+ */
198
+ export declare function useBrokerAuth(): {
199
+ isAuthenticated: Ref<boolean, boolean>;
200
+ isLoading: Ref<boolean, boolean>;
201
+ user: Ref< {
202
+ sub: string;
203
+ email: string;
204
+ given_name?: string | undefined;
205
+ family_name?: string | undefined;
206
+ name?: string | undefined;
207
+ idp?: string | undefined;
208
+ idp_sub?: string | undefined;
209
+ } | null, BrokerUser | {
210
+ sub: string;
211
+ email: string;
212
+ given_name?: string | undefined;
213
+ family_name?: string | undefined;
214
+ name?: string | undefined;
215
+ idp?: string | undefined;
216
+ idp_sub?: string | undefined;
217
+ } | null>;
218
+ token: Ref<string | null, string | null>;
219
+ setupPasskeyToken: Ref<string | null, string | null>;
220
+ error: Ref<Error | null, Error | null>;
221
+ authReady: Ref<boolean, boolean>;
222
+ userName: ComputedRef<string | null>;
223
+ signIn: (options?: BrokerSignInOptions) => Promise<void>;
224
+ signOut: (options?: BrokerSignOutOptions) => Promise<void>;
225
+ acquireToken: (options?: BrokerTokenOptions) => Promise<string | null>;
226
+ hasRole: (role: string) => boolean;
227
+ };
228
+
229
+ /**
230
+ * Same interface as useSSOStore() but with BrokerUser and no activePolicy.
231
+ * Components can use this directly or go through useAuth() (which wraps it).
232
+ */
233
+ export declare const useBrokerStore: StoreDefinition<"broker-sso", Pick<{
234
+ isAuthenticated: Ref<boolean, boolean>;
235
+ isLoading: Ref<boolean, boolean>;
236
+ user: Ref< {
237
+ sub: string;
238
+ email: string;
239
+ given_name?: string | undefined;
240
+ family_name?: string | undefined;
241
+ name?: string | undefined;
242
+ idp?: string | undefined;
243
+ idp_sub?: string | undefined;
244
+ } | null, BrokerUser | {
245
+ sub: string;
246
+ email: string;
247
+ given_name?: string | undefined;
248
+ family_name?: string | undefined;
249
+ name?: string | undefined;
250
+ idp?: string | undefined;
251
+ idp_sub?: string | undefined;
252
+ } | null>;
253
+ token: Ref<string | null, string | null>;
254
+ setupPasskeyToken: Ref<string | null, string | null>;
255
+ error: Ref<Error | null, Error | null>;
256
+ authReady: Ref<boolean, boolean>;
257
+ syncState: (state: BrokerClientState) => void;
258
+ }, "user" | "isAuthenticated" | "isLoading" | "token" | "error" | "authReady" | "setupPasskeyToken">, Pick<{
259
+ isAuthenticated: Ref<boolean, boolean>;
260
+ isLoading: Ref<boolean, boolean>;
261
+ user: Ref< {
262
+ sub: string;
263
+ email: string;
264
+ given_name?: string | undefined;
265
+ family_name?: string | undefined;
266
+ name?: string | undefined;
267
+ idp?: string | undefined;
268
+ idp_sub?: string | undefined;
269
+ } | null, BrokerUser | {
270
+ sub: string;
271
+ email: string;
272
+ given_name?: string | undefined;
273
+ family_name?: string | undefined;
274
+ name?: string | undefined;
275
+ idp?: string | undefined;
276
+ idp_sub?: string | undefined;
277
+ } | null>;
278
+ token: Ref<string | null, string | null>;
279
+ setupPasskeyToken: Ref<string | null, string | null>;
280
+ error: Ref<Error | null, Error | null>;
281
+ authReady: Ref<boolean, boolean>;
282
+ syncState: (state: BrokerClientState) => void;
283
+ }, never>, Pick<{
284
+ isAuthenticated: Ref<boolean, boolean>;
285
+ isLoading: Ref<boolean, boolean>;
286
+ user: Ref< {
287
+ sub: string;
288
+ email: string;
289
+ given_name?: string | undefined;
290
+ family_name?: string | undefined;
291
+ name?: string | undefined;
292
+ idp?: string | undefined;
293
+ idp_sub?: string | undefined;
294
+ } | null, BrokerUser | {
295
+ sub: string;
296
+ email: string;
297
+ given_name?: string | undefined;
298
+ family_name?: string | undefined;
299
+ name?: string | undefined;
300
+ idp?: string | undefined;
301
+ idp_sub?: string | undefined;
302
+ } | null>;
303
+ token: Ref<string | null, string | null>;
304
+ setupPasskeyToken: Ref<string | null, string | null>;
305
+ error: Ref<Error | null, Error | null>;
306
+ authReady: Ref<boolean, boolean>;
307
+ syncState: (state: BrokerClientState) => void;
308
+ }, "syncState">>;
309
+
170
310
  /**
171
311
  * Access the raw SSOClient instance.
172
312
  * Must be called within a component that is a descendant of createSSOPlugin.
package/dist/index.js CHANGED
@@ -124,10 +124,104 @@ function createB2CPlugin(options = {}) {
124
124
  }
125
125
  });
126
126
  }
127
+ const BROKER_CLIENT_KEY = /* @__PURE__ */ Symbol("broker-client");
128
+ const useBrokerStore = pinia.defineStore("broker-sso", () => {
129
+ const isAuthenticated = vue.ref(false);
130
+ const isLoading = vue.ref(false);
131
+ const user = vue.ref(null);
132
+ const token = vue.ref(null);
133
+ const setupPasskeyToken = vue.ref(null);
134
+ const error = vue.ref(null);
135
+ const authReady = vue.ref(false);
136
+ function syncState(state) {
137
+ isAuthenticated.value = state.isAuthenticated;
138
+ isLoading.value = state.isLoading;
139
+ user.value = state.user;
140
+ token.value = state.token;
141
+ setupPasskeyToken.value = state.setupPasskeyToken;
142
+ error.value = state.error;
143
+ authReady.value = state.authReady;
144
+ }
145
+ return { isAuthenticated, isLoading, user, token, setupPasskeyToken, error, authReady, syncState };
146
+ });
147
+ function createBrokerPlugin(options) {
148
+ return {
149
+ install(app) {
150
+ const client = new ssoCore.BrokerClient(options.clientConfig);
151
+ app.provide(BROKER_CLIENT_KEY, client);
152
+ client.events.on("auth:stateChanged", (state) => {
153
+ const store = useBrokerStore();
154
+ store.syncState(state);
155
+ });
156
+ if (options.autoInitialize !== false) {
157
+ client.initialize().then((user) => {
158
+ if (user && options.clientConfig.debug) {
159
+ console.log("[broker-plugin] Initialized, user:", user.sub);
160
+ }
161
+ });
162
+ }
163
+ }
164
+ };
165
+ }
166
+ function useBrokerClient() {
167
+ const client = vue.inject(BROKER_CLIENT_KEY);
168
+ if (!client) {
169
+ throw new Error(
170
+ "[useBrokerAuth] BrokerClient not found. Did you install createBrokerPlugin() before calling useBrokerAuth()?"
171
+ );
172
+ }
173
+ return client;
174
+ }
175
+ function useBrokerAuth() {
176
+ const store = useBrokerStore();
177
+ const client = useBrokerClient();
178
+ const { isAuthenticated, isLoading, user, token, setupPasskeyToken, error, authReady } = pinia.storeToRefs(store);
179
+ const userName = vue.computed(() => {
180
+ if (!user.value) return null;
181
+ const joined = [user.value.given_name, user.value.family_name].filter(Boolean).join(" ");
182
+ return user.value.name || joined || null;
183
+ });
184
+ async function signIn(options) {
185
+ await client.signIn(options);
186
+ }
187
+ async function signOut(options) {
188
+ await client.signOut(options);
189
+ }
190
+ async function acquireToken(options) {
191
+ return client.acquireToken(options);
192
+ }
193
+ function hasRole(role) {
194
+ if (!user.value) return false;
195
+ const claims = user.value;
196
+ const roles = claims.roles ?? [];
197
+ return Array.isArray(roles) && roles.includes(role);
198
+ }
199
+ return {
200
+ // State (readonly refs)
201
+ isAuthenticated,
202
+ isLoading,
203
+ user,
204
+ token,
205
+ setupPasskeyToken,
206
+ error,
207
+ authReady,
208
+ userName,
209
+ // Actions
210
+ signIn,
211
+ signOut,
212
+ acquireToken,
213
+ // Utilities
214
+ hasRole
215
+ };
216
+ }
217
+ exports.BROKER_CLIENT_KEY = BROKER_CLIENT_KEY;
127
218
  exports.SSO_CLIENT_KEY = SSO_CLIENT_KEY;
128
219
  exports.createB2CPlugin = createB2CPlugin;
220
+ exports.createBrokerPlugin = createBrokerPlugin;
129
221
  exports.createSSOPlugin = createSSOPlugin;
130
222
  exports.useAuth = useAuth;
223
+ exports.useBrokerAuth = useBrokerAuth;
224
+ exports.useBrokerStore = useBrokerStore;
131
225
  exports.useSSOClient = useSSOClient;
132
226
  exports.useSSOStore = useSSOStore;
133
227
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/plugin.ts","../src/composables/useSSOClient.ts","../src/composables/useAuth.ts","../src/createB2CPlugin.ts"],"sourcesContent":["import { ref } from \"vue\";\nimport { defineStore } from \"pinia\";\nimport { SSOClient } from \"@phila/sso-core\";\nimport type { App } from \"vue\";\nimport type { SSOClientConfig, SSOClientState, AuthResponse } from \"@phila/sso-core\";\n\nexport const SSO_CLIENT_KEY = Symbol(\"sso-client\");\n\nexport interface SSOPluginOptions {\n clientConfig: SSOClientConfig;\n autoInitialize?: boolean;\n}\n\nexport const useSSOStore = defineStore(\"sso\", () => {\n const isAuthenticated = ref(false);\n const isLoading = ref(false);\n const user = ref<SSOClientState[\"user\"]>(null);\n const token = ref<string | null>(null);\n const error = ref<Error | null>(null);\n const activePolicy = ref<string | null>(null);\n const authReady = ref(false);\n\n function syncState(state: SSOClientState) {\n isAuthenticated.value = state.isAuthenticated;\n isLoading.value = state.isLoading;\n user.value = state.user;\n token.value = state.token;\n error.value = state.error;\n activePolicy.value = state.activePolicy;\n authReady.value = state.authReady;\n }\n\n return { isAuthenticated, isLoading, user, token, error, activePolicy, authReady, syncState };\n});\n\nexport function createSSOPlugin(options: SSOPluginOptions) {\n return {\n install(app: App) {\n const client = new SSOClient(options.clientConfig);\n\n // Provide the client for useSSOClient()\n app.provide(SSO_CLIENT_KEY, client);\n\n // Subscribe to state changes and sync to Pinia store\n client.events.on(\"auth:stateChanged\", (state: SSOClientState) => {\n const store = useSSOStore();\n store.syncState(state);\n });\n\n // Auto-initialize unless explicitly disabled\n if (options.autoInitialize !== false) {\n client.initialize().then((response: AuthResponse | null) => {\n if (response && options.clientConfig.debug) {\n console.log(\"[sso-vue] Auto-initialized with response:\", response);\n }\n });\n }\n },\n };\n}\n","import { inject } from \"vue\";\nimport { SSOClient } from \"@phila/sso-core\";\nimport { SSO_CLIENT_KEY } from \"../plugin\";\n\n/**\n * Access the raw SSOClient instance.\n * Must be called within a component that is a descendant of createSSOPlugin.\n */\nexport function useSSOClient(): SSOClient {\n const client = inject<SSOClient>(SSO_CLIENT_KEY);\n if (!client) {\n throw new Error(\"SSOClient not found. Did you install createSSOPlugin?\");\n }\n return client;\n}\n","import { computed } from \"vue\";\nimport { storeToRefs } from \"pinia\";\nimport { useSSOStore } from \"../plugin\";\nimport { useSSOClient } from \"./useSSOClient\";\nimport type { SignInOptions, SignOutOptions, TokenOptions } from \"@phila/sso-core\";\n\n/**\n * Primary composable for SSO authentication in Vue 3 apps.\n * API shape is intentionally similar to service-cat's existing useAuth()\n * to minimize migration friction.\n */\nexport function useAuth() {\n const store = useSSOStore();\n const client = useSSOClient();\n const { isAuthenticated, isLoading, user, token, error, activePolicy, authReady } = storeToRefs(store);\n\n // Computed helpers\n const userName = computed(() => {\n const claims = user.value?.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return null;\n const given = (claims.given_name ?? claims.name ?? \"\") as string;\n const family = (claims.family_name ?? \"\") as string;\n return family ? `${given} ${family}`.trim() : given;\n });\n\n // Actions\n async function signIn(options?: SignInOptions): Promise<void> {\n await client.signIn(options);\n }\n\n async function signInCityEmployee(options?: SignInOptions): Promise<void> {\n await client.signInCityEmployee(options);\n }\n\n async function signOut(options?: SignOutOptions): Promise<void> {\n await client.signOut(options);\n }\n\n async function forgotPassword(): Promise<void> {\n await client.forgotPassword();\n }\n\n async function acquireToken(options?: TokenOptions): Promise<string | null> {\n return client.acquireToken(options);\n }\n\n // Utilities\n function hasRole(role: string): boolean {\n const claims = user.value?.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return false;\n\n const roles = (claims.roles ?? claims.extension_Roles ?? []) as string[];\n return Array.isArray(roles) && roles.includes(role);\n }\n\n return {\n // State (readonly refs)\n isAuthenticated,\n isLoading,\n user,\n token,\n error,\n activePolicy,\n authReady,\n userName,\n\n // Actions\n signIn,\n signInCityEmployee,\n signOut,\n forgotPassword,\n acquireToken,\n\n // Utilities\n hasRole,\n };\n}\n","// ABOUTME: Convenience factory for vanilla Vue/Vite apps using Azure AD B2C\n// ABOUTME: Reads VITE_SSO_* env vars by convention so apps need zero B2CProvider boilerplate\nimport { B2CProvider } from \"@phila/sso-core\";\nimport { createSSOPlugin } from \"./plugin\";\n\nexport interface B2CPluginOptions {\n signInPolicy?: string;\n resetPasswordPolicy?: string;\n debug?: boolean;\n}\n\n/**\n * Creates a ready-to-use Vue plugin for Azure AD B2C authentication.\n * Reads connection details from VITE_SSO_* environment variables:\n * VITE_SSO_CLIENT_ID, VITE_SSO_TENANT, VITE_SSO_AUTHORITY_DOMAIN, VITE_SSO_REDIRECT_URI\n */\nexport function createB2CPlugin(options: B2CPluginOptions = {}) {\n const {\n signInPolicy = \"B2C_1A_AD_SIGNIN_ONLY\",\n resetPasswordPolicy = \"B2C_1A_PASSWORDRESET\",\n debug = import.meta.env.DEV,\n } = options;\n\n return createSSOPlugin({\n clientConfig: {\n provider: new B2CProvider({\n clientId: import.meta.env.VITE_SSO_CLIENT_ID,\n b2cEnvironment: import.meta.env.VITE_SSO_TENANT,\n authorityDomain: import.meta.env.VITE_SSO_AUTHORITY_DOMAIN,\n redirectUri: import.meta.env.VITE_SSO_REDIRECT_URI,\n policies: {\n signUpSignIn: signInPolicy,\n signInOnly: signInPolicy,\n resetPassword: resetPasswordPolicy,\n },\n }),\n debug,\n },\n });\n}\n"],"names":["defineStore","ref","SSOClient","inject","storeToRefs","computed","B2CProvider"],"mappings":";;;;;AAMO,MAAM,wCAAwB,YAAY;AAO1C,MAAM,cAAcA,MAAAA,YAAY,OAAO,MAAM;AAClD,QAAM,kBAAkBC,IAAAA,IAAI,KAAK;AACjC,QAAM,YAAYA,IAAAA,IAAI,KAAK;AAC3B,QAAM,OAAOA,IAAAA,IAA4B,IAAI;AAC7C,QAAM,QAAQA,IAAAA,IAAmB,IAAI;AACrC,QAAM,QAAQA,IAAAA,IAAkB,IAAI;AACpC,QAAM,eAAeA,IAAAA,IAAmB,IAAI;AAC5C,QAAM,YAAYA,IAAAA,IAAI,KAAK;AAE3B,WAAS,UAAU,OAAuB;AACxC,oBAAgB,QAAQ,MAAM;AAC9B,cAAU,QAAQ,MAAM;AACxB,SAAK,QAAQ,MAAM;AACnB,UAAM,QAAQ,MAAM;AACpB,UAAM,QAAQ,MAAM;AACpB,iBAAa,QAAQ,MAAM;AAC3B,cAAU,QAAQ,MAAM;AAAA,EAC1B;AAEA,SAAO,EAAE,iBAAiB,WAAW,MAAM,OAAO,OAAO,cAAc,WAAW,UAAA;AACpF,CAAC;AAEM,SAAS,gBAAgB,SAA2B;AACzD,SAAO;AAAA,IACL,QAAQ,KAAU;AAChB,YAAM,SAAS,IAAIC,kBAAU,QAAQ,YAAY;AAGjD,UAAI,QAAQ,gBAAgB,MAAM;AAGlC,aAAO,OAAO,GAAG,qBAAqB,CAAC,UAA0B;AAC/D,cAAM,QAAQ,YAAA;AACd,cAAM,UAAU,KAAK;AAAA,MACvB,CAAC;AAGD,UAAI,QAAQ,mBAAmB,OAAO;AACpC,eAAO,WAAA,EAAa,KAAK,CAAC,aAAkC;AAC1D,cAAI,YAAY,QAAQ,aAAa,OAAO;AAC1C,oBAAQ,IAAI,6CAA6C,QAAQ;AAAA,UACnE;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EAAA;AAEJ;ACnDO,SAAS,eAA0B;AACxC,QAAM,SAASC,IAAAA,OAAkB,cAAc;AAC/C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AACA,SAAO;AACT;ACHO,SAAS,UAAU;AACxB,QAAM,QAAQ,YAAA;AACd,QAAM,SAAS,aAAA;AACf,QAAM,EAAE,iBAAiB,WAAW,MAAM,OAAO,OAAO,cAAc,UAAA,IAAcC,MAAAA,YAAY,KAAK;AAGrG,QAAM,WAAWC,IAAAA,SAAS,MAAM;AAC9B,UAAM,SAAS,KAAK,OAAO;AAC3B,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,QAAS,OAAO,cAAc,OAAO,QAAQ;AACnD,UAAM,SAAU,OAAO,eAAe;AACtC,WAAO,SAAS,GAAG,KAAK,IAAI,MAAM,GAAG,SAAS;AAAA,EAChD,CAAC;AAGD,iBAAe,OAAO,SAAwC;AAC5D,UAAM,OAAO,OAAO,OAAO;AAAA,EAC7B;AAEA,iBAAe,mBAAmB,SAAwC;AACxE,UAAM,OAAO,mBAAmB,OAAO;AAAA,EACzC;AAEA,iBAAe,QAAQ,SAAyC;AAC9D,UAAM,OAAO,QAAQ,OAAO;AAAA,EAC9B;AAEA,iBAAe,iBAAgC;AAC7C,UAAM,OAAO,eAAA;AAAA,EACf;AAEA,iBAAe,aAAa,SAAgD;AAC1E,WAAO,OAAO,aAAa,OAAO;AAAA,EACpC;AAGA,WAAS,QAAQ,MAAuB;AACtC,UAAM,SAAS,KAAK,OAAO;AAC3B,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,QAAS,OAAO,SAAS,OAAO,mBAAmB,CAAA;AACzD,WAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,IAAI;AAAA,EACpD;AAEA,SAAO;AAAA;AAAA,IAEL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAGA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAGA;AAAA,EAAA;AAEJ;AC5DO,SAAS,gBAAgB,UAA4B,IAAI;AAC9D,QAAM;AAAA,IACJ,eAAe;AAAA,IACf,sBAAsB;AAAA,IACtB,QAAQ;AAAA,EAAA,IACN;AAEJ,SAAO,gBAAgB;AAAA,IACrB,cAAc;AAAA,MACZ,UAAU,IAAIC,QAAAA,YAAY;AAAA,QACxB,UAAU;AAAA,QACV,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,aAAa;AAAA,QACb,UAAU;AAAA,UACR,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,eAAe;AAAA,QAAA;AAAA,MACjB,CACD;AAAA,MACD;AAAA,IAAA;AAAA,EACF,CACD;AACH;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../src/plugin.ts","../src/composables/useSSOClient.ts","../src/composables/useAuth.ts","../src/createB2CPlugin.ts","../src/broker-plugin.ts","../src/composables/useBrokerAuth.ts"],"sourcesContent":["import { ref } from \"vue\";\nimport { defineStore } from \"pinia\";\nimport { SSOClient } from \"@phila/sso-core\";\nimport type { App } from \"vue\";\nimport type { SSOClientConfig, SSOClientState, AuthResponse } from \"@phila/sso-core\";\n\nexport const SSO_CLIENT_KEY = Symbol(\"sso-client\");\n\nexport interface SSOPluginOptions {\n clientConfig: SSOClientConfig;\n autoInitialize?: boolean;\n}\n\nexport const useSSOStore = defineStore(\"sso\", () => {\n const isAuthenticated = ref(false);\n const isLoading = ref(false);\n const user = ref<SSOClientState[\"user\"]>(null);\n const token = ref<string | null>(null);\n const error = ref<Error | null>(null);\n const activePolicy = ref<string | null>(null);\n const authReady = ref(false);\n\n function syncState(state: SSOClientState) {\n isAuthenticated.value = state.isAuthenticated;\n isLoading.value = state.isLoading;\n user.value = state.user;\n token.value = state.token;\n error.value = state.error;\n activePolicy.value = state.activePolicy;\n authReady.value = state.authReady;\n }\n\n return { isAuthenticated, isLoading, user, token, error, activePolicy, authReady, syncState };\n});\n\nexport function createSSOPlugin(options: SSOPluginOptions) {\n return {\n install(app: App) {\n const client = new SSOClient(options.clientConfig);\n\n // Provide the client for useSSOClient()\n app.provide(SSO_CLIENT_KEY, client);\n\n // Subscribe to state changes and sync to Pinia store\n client.events.on(\"auth:stateChanged\", (state: SSOClientState) => {\n const store = useSSOStore();\n store.syncState(state);\n });\n\n // Auto-initialize unless explicitly disabled\n if (options.autoInitialize !== false) {\n client.initialize().then((response: AuthResponse | null) => {\n if (response && options.clientConfig.debug) {\n console.log(\"[sso-vue] Auto-initialized with response:\", response);\n }\n });\n }\n },\n };\n}\n","import { inject } from \"vue\";\nimport { SSOClient } from \"@phila/sso-core\";\nimport { SSO_CLIENT_KEY } from \"../plugin\";\n\n/**\n * Access the raw SSOClient instance.\n * Must be called within a component that is a descendant of createSSOPlugin.\n */\nexport function useSSOClient(): SSOClient {\n const client = inject<SSOClient>(SSO_CLIENT_KEY);\n if (!client) {\n throw new Error(\"SSOClient not found. Did you install createSSOPlugin?\");\n }\n return client;\n}\n","import { computed } from \"vue\";\nimport { storeToRefs } from \"pinia\";\nimport { useSSOStore } from \"../plugin\";\nimport { useSSOClient } from \"./useSSOClient\";\nimport type { SignInOptions, SignOutOptions, TokenOptions } from \"@phila/sso-core\";\n\n/**\n * Primary composable for SSO authentication in Vue 3 apps.\n * API shape is intentionally similar to service-cat's existing useAuth()\n * to minimize migration friction.\n */\nexport function useAuth() {\n const store = useSSOStore();\n const client = useSSOClient();\n const { isAuthenticated, isLoading, user, token, error, activePolicy, authReady } = storeToRefs(store);\n\n // Computed helpers\n const userName = computed(() => {\n const claims = user.value?.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return null;\n const given = (claims.given_name ?? claims.name ?? \"\") as string;\n const family = (claims.family_name ?? \"\") as string;\n return family ? `${given} ${family}`.trim() : given;\n });\n\n // Actions\n async function signIn(options?: SignInOptions): Promise<void> {\n await client.signIn(options);\n }\n\n async function signInCityEmployee(options?: SignInOptions): Promise<void> {\n await client.signInCityEmployee(options);\n }\n\n async function signOut(options?: SignOutOptions): Promise<void> {\n await client.signOut(options);\n }\n\n async function forgotPassword(): Promise<void> {\n await client.forgotPassword();\n }\n\n async function acquireToken(options?: TokenOptions): Promise<string | null> {\n return client.acquireToken(options);\n }\n\n // Utilities\n function hasRole(role: string): boolean {\n const claims = user.value?.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return false;\n\n const roles = (claims.roles ?? claims.extension_Roles ?? []) as string[];\n return Array.isArray(roles) && roles.includes(role);\n }\n\n return {\n // State (readonly refs)\n isAuthenticated,\n isLoading,\n user,\n token,\n error,\n activePolicy,\n authReady,\n userName,\n\n // Actions\n signIn,\n signInCityEmployee,\n signOut,\n forgotPassword,\n acquireToken,\n\n // Utilities\n hasRole,\n };\n}\n","// ABOUTME: Convenience factory for vanilla Vue/Vite apps using Azure AD B2C\n// ABOUTME: Reads VITE_SSO_* env vars by convention so apps need zero B2CProvider boilerplate\nimport { B2CProvider } from \"@phila/sso-core\";\nimport { createSSOPlugin } from \"./plugin\";\n\nexport interface B2CPluginOptions {\n signInPolicy?: string;\n resetPasswordPolicy?: string;\n debug?: boolean;\n}\n\n/**\n * Creates a ready-to-use Vue plugin for Azure AD B2C authentication.\n * Reads connection details from VITE_SSO_* environment variables:\n * VITE_SSO_CLIENT_ID, VITE_SSO_TENANT, VITE_SSO_AUTHORITY_DOMAIN, VITE_SSO_REDIRECT_URI\n */\nexport function createB2CPlugin(options: B2CPluginOptions = {}) {\n const {\n signInPolicy = \"B2C_1A_AD_SIGNIN_ONLY\",\n resetPasswordPolicy = \"B2C_1A_PASSWORDRESET\",\n debug = import.meta.env.DEV,\n } = options;\n\n return createSSOPlugin({\n clientConfig: {\n provider: new B2CProvider({\n clientId: import.meta.env.VITE_SSO_CLIENT_ID,\n b2cEnvironment: import.meta.env.VITE_SSO_TENANT,\n authorityDomain: import.meta.env.VITE_SSO_AUTHORITY_DOMAIN,\n redirectUri: import.meta.env.VITE_SSO_REDIRECT_URI,\n policies: {\n signUpSignIn: signInPolicy,\n signInOnly: signInPolicy,\n resetPassword: resetPasswordPolicy,\n },\n }),\n debug,\n },\n });\n}\n","// ABOUTME: Vue 3 plugin for the phila-identity broker — replaces createSSOPlugin() / useSSOStore().\n// ABOUTME: Uses BrokerClient instead of SSOClient, so there's no MSAL dependency.\n// ABOUTME:\n// ABOUTME: Store shape differences from the B2C plugin:\n// ABOUTME: - user is BrokerUser (not MSAL AccountInfo) — direct named properties, no idTokenClaims map\n// ABOUTME: - activePolicy is gone — the broker is policy-agnostic from the client's perspective\n// ABOUTME:\n// ABOUTME: Everything else (provide/inject, Pinia store, event wiring) is identical.\n\nimport { ref } from \"vue\";\nimport { defineStore } from \"pinia\";\nimport { BrokerClient } from \"@phila/sso-core\";\nimport type { App } from \"vue\";\nimport type { BrokerClientConfig, BrokerClientState } from \"@phila/sso-core\";\n\nexport const BROKER_CLIENT_KEY = Symbol(\"broker-client\");\n\n// ── Plugin options ─────────────────────────────────────────────────────────────\n\nexport interface BrokerPluginOptions {\n clientConfig: BrokerClientConfig;\n /**\n * Set false to skip the automatic initialize() call during install.\n * Useful when you want to call initialize() yourself at a specific lifecycle point.\n */\n autoInitialize?: boolean;\n}\n\n// ── Pinia store ────────────────────────────────────────────────────────────────\n\n/**\n * Same interface as useSSOStore() but with BrokerUser and no activePolicy.\n * Components can use this directly or go through useAuth() (which wraps it).\n */\nexport const useBrokerStore = defineStore(\"broker-sso\", () => {\n const isAuthenticated = ref(false);\n const isLoading = ref(false);\n const user = ref<BrokerClientState[\"user\"]>(null);\n const token = ref<string | null>(null);\n const setupPasskeyToken = ref<string | null>(null);\n const error = ref<Error | null>(null);\n const authReady = ref(false);\n\n function syncState(state: BrokerClientState) {\n isAuthenticated.value = state.isAuthenticated;\n isLoading.value = state.isLoading;\n user.value = state.user;\n token.value = state.token;\n setupPasskeyToken.value = state.setupPasskeyToken;\n error.value = state.error;\n authReady.value = state.authReady;\n }\n\n return { isAuthenticated, isLoading, user, token, setupPasskeyToken, error, authReady, syncState };\n});\n\n// ── Plugin factory ─────────────────────────────────────────────────────────────\n\nexport function createBrokerPlugin(options: BrokerPluginOptions) {\n return {\n install(app: App) {\n const client = new BrokerClient(options.clientConfig);\n\n app.provide(BROKER_CLIENT_KEY, client);\n\n client.events.on(\"auth:stateChanged\", (state: BrokerClientState) => {\n const store = useBrokerStore();\n store.syncState(state);\n });\n\n if (options.autoInitialize !== false) {\n client.initialize().then(user => {\n if (user && options.clientConfig.debug) {\n console.log(\"[broker-plugin] Initialized, user:\", user.sub);\n }\n });\n }\n },\n };\n}\n","// ABOUTME: useAuth() equivalent for broker-authenticated Vue 3 apps.\n// ABOUTME: API surface matches useAuth() from useAuth.ts so migration is find-and-replace.\n// ABOUTME:\n// ABOUTME: What's simpler compared to the B2C useAuth():\n// ABOUTME: - userName reads direct BrokerUser properties (no idTokenClaims map)\n// ABOUTME: - no forgotPassword() — handled entirely on the B2C side behind the broker\n// ABOUTME: - no signInCityEmployee() / policy-based sign-in — one signIn() covers all flows\n// ABOUTME: - hasRole() reads roles claim directly (not extension_Roles fallback)\n\nimport { computed, inject } from \"vue\";\nimport { storeToRefs } from \"pinia\";\nimport { useBrokerStore, BROKER_CLIENT_KEY } from \"../broker-plugin\";\nimport type { BrokerClient, BrokerSignInOptions, BrokerSignOutOptions, BrokerTokenOptions } from \"@phila/sso-core\";\n\nfunction useBrokerClient(): BrokerClient {\n const client = inject<BrokerClient>(BROKER_CLIENT_KEY);\n if (!client) {\n throw new Error(\n \"[useBrokerAuth] BrokerClient not found. \" +\n \"Did you install createBrokerPlugin() before calling useBrokerAuth()?\",\n );\n }\n return client;\n}\n\n/**\n * Primary composable for broker-authenticated Vue 3 apps.\n * Drop-in replacement for useAuth() — same returned shape, simpler internals.\n *\n * @example\n * const { isAuthenticated, user, signIn, signOut } = useBrokerAuth();\n */\nexport function useBrokerAuth() {\n const store = useBrokerStore();\n const client = useBrokerClient();\n const { isAuthenticated, isLoading, user, token, setupPasskeyToken, error, authReady } = storeToRefs(store);\n\n // ── Computed helpers ─────────────────────────────────────────────────────\n\n const userName = computed(() => {\n if (!user.value) return null;\n // BrokerUser has direct properties — no idTokenClaims indirection.\n const joined = [user.value.given_name, user.value.family_name].filter(Boolean).join(\" \");\n return user.value.name || joined || null;\n });\n\n // ── Actions ──────────────────────────────────────────────────────────────\n\n async function signIn(options?: BrokerSignInOptions): Promise<void> {\n await client.signIn(options);\n }\n\n async function signOut(options?: BrokerSignOutOptions): Promise<void> {\n await client.signOut(options);\n }\n\n async function acquireToken(options?: BrokerTokenOptions): Promise<string | null> {\n return client.acquireToken(options);\n }\n\n // ── Utilities ────────────────────────────────────────────────────────────\n\n /**\n * Check if the authenticated user has a given role.\n * The broker includes roles in the access token `scope` field by convention;\n * adjust the claim name here if your broker config uses a custom roles claim.\n */\n function hasRole(role: string): boolean {\n if (!user.value) return false;\n // Roles aren't yet standardized in the broker's token — placeholder for\n // when the broker adds a `roles` claim to the JWT payload.\n const claims = user.value as unknown as Record<string, unknown>;\n const roles = (claims.roles ?? []) as string[];\n return Array.isArray(roles) && roles.includes(role);\n }\n\n return {\n // State (readonly refs)\n isAuthenticated,\n isLoading,\n user,\n token,\n setupPasskeyToken,\n error,\n authReady,\n userName,\n\n // Actions\n signIn,\n signOut,\n acquireToken,\n\n // Utilities\n hasRole,\n };\n}\n"],"names":["defineStore","ref","SSOClient","inject","storeToRefs","computed","B2CProvider","BrokerClient"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ref, inject, computed } from "vue";
2
2
  import { defineStore, storeToRefs } from "pinia";
3
- import { SSOClient, B2CProvider } from "@phila/sso-core";
3
+ import { SSOClient, B2CProvider, BrokerClient } from "@phila/sso-core";
4
4
  const SSO_CLIENT_KEY = /* @__PURE__ */ Symbol("sso-client");
5
5
  const useSSOStore = defineStore("sso", () => {
6
6
  const isAuthenticated = ref(false);
@@ -122,11 +122,105 @@ function createB2CPlugin(options = {}) {
122
122
  }
123
123
  });
124
124
  }
125
+ const BROKER_CLIENT_KEY = /* @__PURE__ */ Symbol("broker-client");
126
+ const useBrokerStore = defineStore("broker-sso", () => {
127
+ const isAuthenticated = ref(false);
128
+ const isLoading = ref(false);
129
+ const user = ref(null);
130
+ const token = ref(null);
131
+ const setupPasskeyToken = ref(null);
132
+ const error = ref(null);
133
+ const authReady = ref(false);
134
+ function syncState(state) {
135
+ isAuthenticated.value = state.isAuthenticated;
136
+ isLoading.value = state.isLoading;
137
+ user.value = state.user;
138
+ token.value = state.token;
139
+ setupPasskeyToken.value = state.setupPasskeyToken;
140
+ error.value = state.error;
141
+ authReady.value = state.authReady;
142
+ }
143
+ return { isAuthenticated, isLoading, user, token, setupPasskeyToken, error, authReady, syncState };
144
+ });
145
+ function createBrokerPlugin(options) {
146
+ return {
147
+ install(app) {
148
+ const client = new BrokerClient(options.clientConfig);
149
+ app.provide(BROKER_CLIENT_KEY, client);
150
+ client.events.on("auth:stateChanged", (state) => {
151
+ const store = useBrokerStore();
152
+ store.syncState(state);
153
+ });
154
+ if (options.autoInitialize !== false) {
155
+ client.initialize().then((user) => {
156
+ if (user && options.clientConfig.debug) {
157
+ console.log("[broker-plugin] Initialized, user:", user.sub);
158
+ }
159
+ });
160
+ }
161
+ }
162
+ };
163
+ }
164
+ function useBrokerClient() {
165
+ const client = inject(BROKER_CLIENT_KEY);
166
+ if (!client) {
167
+ throw new Error(
168
+ "[useBrokerAuth] BrokerClient not found. Did you install createBrokerPlugin() before calling useBrokerAuth()?"
169
+ );
170
+ }
171
+ return client;
172
+ }
173
+ function useBrokerAuth() {
174
+ const store = useBrokerStore();
175
+ const client = useBrokerClient();
176
+ const { isAuthenticated, isLoading, user, token, setupPasskeyToken, error, authReady } = storeToRefs(store);
177
+ const userName = computed(() => {
178
+ if (!user.value) return null;
179
+ const joined = [user.value.given_name, user.value.family_name].filter(Boolean).join(" ");
180
+ return user.value.name || joined || null;
181
+ });
182
+ async function signIn(options) {
183
+ await client.signIn(options);
184
+ }
185
+ async function signOut(options) {
186
+ await client.signOut(options);
187
+ }
188
+ async function acquireToken(options) {
189
+ return client.acquireToken(options);
190
+ }
191
+ function hasRole(role) {
192
+ if (!user.value) return false;
193
+ const claims = user.value;
194
+ const roles = claims.roles ?? [];
195
+ return Array.isArray(roles) && roles.includes(role);
196
+ }
197
+ return {
198
+ // State (readonly refs)
199
+ isAuthenticated,
200
+ isLoading,
201
+ user,
202
+ token,
203
+ setupPasskeyToken,
204
+ error,
205
+ authReady,
206
+ userName,
207
+ // Actions
208
+ signIn,
209
+ signOut,
210
+ acquireToken,
211
+ // Utilities
212
+ hasRole
213
+ };
214
+ }
125
215
  export {
216
+ BROKER_CLIENT_KEY,
126
217
  SSO_CLIENT_KEY,
127
218
  createB2CPlugin,
219
+ createBrokerPlugin,
128
220
  createSSOPlugin,
129
221
  useAuth,
222
+ useBrokerAuth,
223
+ useBrokerStore,
130
224
  useSSOClient,
131
225
  useSSOStore
132
226
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/plugin.ts","../src/composables/useSSOClient.ts","../src/composables/useAuth.ts","../src/createB2CPlugin.ts"],"sourcesContent":["import { ref } from \"vue\";\nimport { defineStore } from \"pinia\";\nimport { SSOClient } from \"@phila/sso-core\";\nimport type { App } from \"vue\";\nimport type { SSOClientConfig, SSOClientState, AuthResponse } from \"@phila/sso-core\";\n\nexport const SSO_CLIENT_KEY = Symbol(\"sso-client\");\n\nexport interface SSOPluginOptions {\n clientConfig: SSOClientConfig;\n autoInitialize?: boolean;\n}\n\nexport const useSSOStore = defineStore(\"sso\", () => {\n const isAuthenticated = ref(false);\n const isLoading = ref(false);\n const user = ref<SSOClientState[\"user\"]>(null);\n const token = ref<string | null>(null);\n const error = ref<Error | null>(null);\n const activePolicy = ref<string | null>(null);\n const authReady = ref(false);\n\n function syncState(state: SSOClientState) {\n isAuthenticated.value = state.isAuthenticated;\n isLoading.value = state.isLoading;\n user.value = state.user;\n token.value = state.token;\n error.value = state.error;\n activePolicy.value = state.activePolicy;\n authReady.value = state.authReady;\n }\n\n return { isAuthenticated, isLoading, user, token, error, activePolicy, authReady, syncState };\n});\n\nexport function createSSOPlugin(options: SSOPluginOptions) {\n return {\n install(app: App) {\n const client = new SSOClient(options.clientConfig);\n\n // Provide the client for useSSOClient()\n app.provide(SSO_CLIENT_KEY, client);\n\n // Subscribe to state changes and sync to Pinia store\n client.events.on(\"auth:stateChanged\", (state: SSOClientState) => {\n const store = useSSOStore();\n store.syncState(state);\n });\n\n // Auto-initialize unless explicitly disabled\n if (options.autoInitialize !== false) {\n client.initialize().then((response: AuthResponse | null) => {\n if (response && options.clientConfig.debug) {\n console.log(\"[sso-vue] Auto-initialized with response:\", response);\n }\n });\n }\n },\n };\n}\n","import { inject } from \"vue\";\nimport { SSOClient } from \"@phila/sso-core\";\nimport { SSO_CLIENT_KEY } from \"../plugin\";\n\n/**\n * Access the raw SSOClient instance.\n * Must be called within a component that is a descendant of createSSOPlugin.\n */\nexport function useSSOClient(): SSOClient {\n const client = inject<SSOClient>(SSO_CLIENT_KEY);\n if (!client) {\n throw new Error(\"SSOClient not found. Did you install createSSOPlugin?\");\n }\n return client;\n}\n","import { computed } from \"vue\";\nimport { storeToRefs } from \"pinia\";\nimport { useSSOStore } from \"../plugin\";\nimport { useSSOClient } from \"./useSSOClient\";\nimport type { SignInOptions, SignOutOptions, TokenOptions } from \"@phila/sso-core\";\n\n/**\n * Primary composable for SSO authentication in Vue 3 apps.\n * API shape is intentionally similar to service-cat's existing useAuth()\n * to minimize migration friction.\n */\nexport function useAuth() {\n const store = useSSOStore();\n const client = useSSOClient();\n const { isAuthenticated, isLoading, user, token, error, activePolicy, authReady } = storeToRefs(store);\n\n // Computed helpers\n const userName = computed(() => {\n const claims = user.value?.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return null;\n const given = (claims.given_name ?? claims.name ?? \"\") as string;\n const family = (claims.family_name ?? \"\") as string;\n return family ? `${given} ${family}`.trim() : given;\n });\n\n // Actions\n async function signIn(options?: SignInOptions): Promise<void> {\n await client.signIn(options);\n }\n\n async function signInCityEmployee(options?: SignInOptions): Promise<void> {\n await client.signInCityEmployee(options);\n }\n\n async function signOut(options?: SignOutOptions): Promise<void> {\n await client.signOut(options);\n }\n\n async function forgotPassword(): Promise<void> {\n await client.forgotPassword();\n }\n\n async function acquireToken(options?: TokenOptions): Promise<string | null> {\n return client.acquireToken(options);\n }\n\n // Utilities\n function hasRole(role: string): boolean {\n const claims = user.value?.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return false;\n\n const roles = (claims.roles ?? claims.extension_Roles ?? []) as string[];\n return Array.isArray(roles) && roles.includes(role);\n }\n\n return {\n // State (readonly refs)\n isAuthenticated,\n isLoading,\n user,\n token,\n error,\n activePolicy,\n authReady,\n userName,\n\n // Actions\n signIn,\n signInCityEmployee,\n signOut,\n forgotPassword,\n acquireToken,\n\n // Utilities\n hasRole,\n };\n}\n","// ABOUTME: Convenience factory for vanilla Vue/Vite apps using Azure AD B2C\n// ABOUTME: Reads VITE_SSO_* env vars by convention so apps need zero B2CProvider boilerplate\nimport { B2CProvider } from \"@phila/sso-core\";\nimport { createSSOPlugin } from \"./plugin\";\n\nexport interface B2CPluginOptions {\n signInPolicy?: string;\n resetPasswordPolicy?: string;\n debug?: boolean;\n}\n\n/**\n * Creates a ready-to-use Vue plugin for Azure AD B2C authentication.\n * Reads connection details from VITE_SSO_* environment variables:\n * VITE_SSO_CLIENT_ID, VITE_SSO_TENANT, VITE_SSO_AUTHORITY_DOMAIN, VITE_SSO_REDIRECT_URI\n */\nexport function createB2CPlugin(options: B2CPluginOptions = {}) {\n const {\n signInPolicy = \"B2C_1A_AD_SIGNIN_ONLY\",\n resetPasswordPolicy = \"B2C_1A_PASSWORDRESET\",\n debug = import.meta.env.DEV,\n } = options;\n\n return createSSOPlugin({\n clientConfig: {\n provider: new B2CProvider({\n clientId: import.meta.env.VITE_SSO_CLIENT_ID,\n b2cEnvironment: import.meta.env.VITE_SSO_TENANT,\n authorityDomain: import.meta.env.VITE_SSO_AUTHORITY_DOMAIN,\n redirectUri: import.meta.env.VITE_SSO_REDIRECT_URI,\n policies: {\n signUpSignIn: signInPolicy,\n signInOnly: signInPolicy,\n resetPassword: resetPasswordPolicy,\n },\n }),\n debug,\n },\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/plugin.ts","../src/composables/useSSOClient.ts","../src/composables/useAuth.ts","../src/createB2CPlugin.ts","../src/broker-plugin.ts","../src/composables/useBrokerAuth.ts"],"sourcesContent":["import { ref } from \"vue\";\nimport { defineStore } from \"pinia\";\nimport { SSOClient } from \"@phila/sso-core\";\nimport type { App } from \"vue\";\nimport type { SSOClientConfig, SSOClientState, AuthResponse } from \"@phila/sso-core\";\n\nexport const SSO_CLIENT_KEY = Symbol(\"sso-client\");\n\nexport interface SSOPluginOptions {\n clientConfig: SSOClientConfig;\n autoInitialize?: boolean;\n}\n\nexport const useSSOStore = defineStore(\"sso\", () => {\n const isAuthenticated = ref(false);\n const isLoading = ref(false);\n const user = ref<SSOClientState[\"user\"]>(null);\n const token = ref<string | null>(null);\n const error = ref<Error | null>(null);\n const activePolicy = ref<string | null>(null);\n const authReady = ref(false);\n\n function syncState(state: SSOClientState) {\n isAuthenticated.value = state.isAuthenticated;\n isLoading.value = state.isLoading;\n user.value = state.user;\n token.value = state.token;\n error.value = state.error;\n activePolicy.value = state.activePolicy;\n authReady.value = state.authReady;\n }\n\n return { isAuthenticated, isLoading, user, token, error, activePolicy, authReady, syncState };\n});\n\nexport function createSSOPlugin(options: SSOPluginOptions) {\n return {\n install(app: App) {\n const client = new SSOClient(options.clientConfig);\n\n // Provide the client for useSSOClient()\n app.provide(SSO_CLIENT_KEY, client);\n\n // Subscribe to state changes and sync to Pinia store\n client.events.on(\"auth:stateChanged\", (state: SSOClientState) => {\n const store = useSSOStore();\n store.syncState(state);\n });\n\n // Auto-initialize unless explicitly disabled\n if (options.autoInitialize !== false) {\n client.initialize().then((response: AuthResponse | null) => {\n if (response && options.clientConfig.debug) {\n console.log(\"[sso-vue] Auto-initialized with response:\", response);\n }\n });\n }\n },\n };\n}\n","import { inject } from \"vue\";\nimport { SSOClient } from \"@phila/sso-core\";\nimport { SSO_CLIENT_KEY } from \"../plugin\";\n\n/**\n * Access the raw SSOClient instance.\n * Must be called within a component that is a descendant of createSSOPlugin.\n */\nexport function useSSOClient(): SSOClient {\n const client = inject<SSOClient>(SSO_CLIENT_KEY);\n if (!client) {\n throw new Error(\"SSOClient not found. Did you install createSSOPlugin?\");\n }\n return client;\n}\n","import { computed } from \"vue\";\nimport { storeToRefs } from \"pinia\";\nimport { useSSOStore } from \"../plugin\";\nimport { useSSOClient } from \"./useSSOClient\";\nimport type { SignInOptions, SignOutOptions, TokenOptions } from \"@phila/sso-core\";\n\n/**\n * Primary composable for SSO authentication in Vue 3 apps.\n * API shape is intentionally similar to service-cat's existing useAuth()\n * to minimize migration friction.\n */\nexport function useAuth() {\n const store = useSSOStore();\n const client = useSSOClient();\n const { isAuthenticated, isLoading, user, token, error, activePolicy, authReady } = storeToRefs(store);\n\n // Computed helpers\n const userName = computed(() => {\n const claims = user.value?.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return null;\n const given = (claims.given_name ?? claims.name ?? \"\") as string;\n const family = (claims.family_name ?? \"\") as string;\n return family ? `${given} ${family}`.trim() : given;\n });\n\n // Actions\n async function signIn(options?: SignInOptions): Promise<void> {\n await client.signIn(options);\n }\n\n async function signInCityEmployee(options?: SignInOptions): Promise<void> {\n await client.signInCityEmployee(options);\n }\n\n async function signOut(options?: SignOutOptions): Promise<void> {\n await client.signOut(options);\n }\n\n async function forgotPassword(): Promise<void> {\n await client.forgotPassword();\n }\n\n async function acquireToken(options?: TokenOptions): Promise<string | null> {\n return client.acquireToken(options);\n }\n\n // Utilities\n function hasRole(role: string): boolean {\n const claims = user.value?.idTokenClaims as Record<string, unknown> | undefined;\n if (!claims) return false;\n\n const roles = (claims.roles ?? claims.extension_Roles ?? []) as string[];\n return Array.isArray(roles) && roles.includes(role);\n }\n\n return {\n // State (readonly refs)\n isAuthenticated,\n isLoading,\n user,\n token,\n error,\n activePolicy,\n authReady,\n userName,\n\n // Actions\n signIn,\n signInCityEmployee,\n signOut,\n forgotPassword,\n acquireToken,\n\n // Utilities\n hasRole,\n };\n}\n","// ABOUTME: Convenience factory for vanilla Vue/Vite apps using Azure AD B2C\n// ABOUTME: Reads VITE_SSO_* env vars by convention so apps need zero B2CProvider boilerplate\nimport { B2CProvider } from \"@phila/sso-core\";\nimport { createSSOPlugin } from \"./plugin\";\n\nexport interface B2CPluginOptions {\n signInPolicy?: string;\n resetPasswordPolicy?: string;\n debug?: boolean;\n}\n\n/**\n * Creates a ready-to-use Vue plugin for Azure AD B2C authentication.\n * Reads connection details from VITE_SSO_* environment variables:\n * VITE_SSO_CLIENT_ID, VITE_SSO_TENANT, VITE_SSO_AUTHORITY_DOMAIN, VITE_SSO_REDIRECT_URI\n */\nexport function createB2CPlugin(options: B2CPluginOptions = {}) {\n const {\n signInPolicy = \"B2C_1A_AD_SIGNIN_ONLY\",\n resetPasswordPolicy = \"B2C_1A_PASSWORDRESET\",\n debug = import.meta.env.DEV,\n } = options;\n\n return createSSOPlugin({\n clientConfig: {\n provider: new B2CProvider({\n clientId: import.meta.env.VITE_SSO_CLIENT_ID,\n b2cEnvironment: import.meta.env.VITE_SSO_TENANT,\n authorityDomain: import.meta.env.VITE_SSO_AUTHORITY_DOMAIN,\n redirectUri: import.meta.env.VITE_SSO_REDIRECT_URI,\n policies: {\n signUpSignIn: signInPolicy,\n signInOnly: signInPolicy,\n resetPassword: resetPasswordPolicy,\n },\n }),\n debug,\n },\n });\n}\n","// ABOUTME: Vue 3 plugin for the phila-identity broker — replaces createSSOPlugin() / useSSOStore().\n// ABOUTME: Uses BrokerClient instead of SSOClient, so there's no MSAL dependency.\n// ABOUTME:\n// ABOUTME: Store shape differences from the B2C plugin:\n// ABOUTME: - user is BrokerUser (not MSAL AccountInfo) — direct named properties, no idTokenClaims map\n// ABOUTME: - activePolicy is gone — the broker is policy-agnostic from the client's perspective\n// ABOUTME:\n// ABOUTME: Everything else (provide/inject, Pinia store, event wiring) is identical.\n\nimport { ref } from \"vue\";\nimport { defineStore } from \"pinia\";\nimport { BrokerClient } from \"@phila/sso-core\";\nimport type { App } from \"vue\";\nimport type { BrokerClientConfig, BrokerClientState } from \"@phila/sso-core\";\n\nexport const BROKER_CLIENT_KEY = Symbol(\"broker-client\");\n\n// ── Plugin options ─────────────────────────────────────────────────────────────\n\nexport interface BrokerPluginOptions {\n clientConfig: BrokerClientConfig;\n /**\n * Set false to skip the automatic initialize() call during install.\n * Useful when you want to call initialize() yourself at a specific lifecycle point.\n */\n autoInitialize?: boolean;\n}\n\n// ── Pinia store ────────────────────────────────────────────────────────────────\n\n/**\n * Same interface as useSSOStore() but with BrokerUser and no activePolicy.\n * Components can use this directly or go through useAuth() (which wraps it).\n */\nexport const useBrokerStore = defineStore(\"broker-sso\", () => {\n const isAuthenticated = ref(false);\n const isLoading = ref(false);\n const user = ref<BrokerClientState[\"user\"]>(null);\n const token = ref<string | null>(null);\n const setupPasskeyToken = ref<string | null>(null);\n const error = ref<Error | null>(null);\n const authReady = ref(false);\n\n function syncState(state: BrokerClientState) {\n isAuthenticated.value = state.isAuthenticated;\n isLoading.value = state.isLoading;\n user.value = state.user;\n token.value = state.token;\n setupPasskeyToken.value = state.setupPasskeyToken;\n error.value = state.error;\n authReady.value = state.authReady;\n }\n\n return { isAuthenticated, isLoading, user, token, setupPasskeyToken, error, authReady, syncState };\n});\n\n// ── Plugin factory ─────────────────────────────────────────────────────────────\n\nexport function createBrokerPlugin(options: BrokerPluginOptions) {\n return {\n install(app: App) {\n const client = new BrokerClient(options.clientConfig);\n\n app.provide(BROKER_CLIENT_KEY, client);\n\n client.events.on(\"auth:stateChanged\", (state: BrokerClientState) => {\n const store = useBrokerStore();\n store.syncState(state);\n });\n\n if (options.autoInitialize !== false) {\n client.initialize().then(user => {\n if (user && options.clientConfig.debug) {\n console.log(\"[broker-plugin] Initialized, user:\", user.sub);\n }\n });\n }\n },\n };\n}\n","// ABOUTME: useAuth() equivalent for broker-authenticated Vue 3 apps.\n// ABOUTME: API surface matches useAuth() from useAuth.ts so migration is find-and-replace.\n// ABOUTME:\n// ABOUTME: What's simpler compared to the B2C useAuth():\n// ABOUTME: - userName reads direct BrokerUser properties (no idTokenClaims map)\n// ABOUTME: - no forgotPassword() — handled entirely on the B2C side behind the broker\n// ABOUTME: - no signInCityEmployee() / policy-based sign-in — one signIn() covers all flows\n// ABOUTME: - hasRole() reads roles claim directly (not extension_Roles fallback)\n\nimport { computed, inject } from \"vue\";\nimport { storeToRefs } from \"pinia\";\nimport { useBrokerStore, BROKER_CLIENT_KEY } from \"../broker-plugin\";\nimport type { BrokerClient, BrokerSignInOptions, BrokerSignOutOptions, BrokerTokenOptions } from \"@phila/sso-core\";\n\nfunction useBrokerClient(): BrokerClient {\n const client = inject<BrokerClient>(BROKER_CLIENT_KEY);\n if (!client) {\n throw new Error(\n \"[useBrokerAuth] BrokerClient not found. \" +\n \"Did you install createBrokerPlugin() before calling useBrokerAuth()?\",\n );\n }\n return client;\n}\n\n/**\n * Primary composable for broker-authenticated Vue 3 apps.\n * Drop-in replacement for useAuth() — same returned shape, simpler internals.\n *\n * @example\n * const { isAuthenticated, user, signIn, signOut } = useBrokerAuth();\n */\nexport function useBrokerAuth() {\n const store = useBrokerStore();\n const client = useBrokerClient();\n const { isAuthenticated, isLoading, user, token, setupPasskeyToken, error, authReady } = storeToRefs(store);\n\n // ── Computed helpers ─────────────────────────────────────────────────────\n\n const userName = computed(() => {\n if (!user.value) return null;\n // BrokerUser has direct properties — no idTokenClaims indirection.\n const joined = [user.value.given_name, user.value.family_name].filter(Boolean).join(\" \");\n return user.value.name || joined || null;\n });\n\n // ── Actions ──────────────────────────────────────────────────────────────\n\n async function signIn(options?: BrokerSignInOptions): Promise<void> {\n await client.signIn(options);\n }\n\n async function signOut(options?: BrokerSignOutOptions): Promise<void> {\n await client.signOut(options);\n }\n\n async function acquireToken(options?: BrokerTokenOptions): Promise<string | null> {\n return client.acquireToken(options);\n }\n\n // ── Utilities ────────────────────────────────────────────────────────────\n\n /**\n * Check if the authenticated user has a given role.\n * The broker includes roles in the access token `scope` field by convention;\n * adjust the claim name here if your broker config uses a custom roles claim.\n */\n function hasRole(role: string): boolean {\n if (!user.value) return false;\n // Roles aren't yet standardized in the broker's token — placeholder for\n // when the broker adds a `roles` claim to the JWT payload.\n const claims = user.value as unknown as Record<string, unknown>;\n const roles = (claims.roles ?? []) as string[];\n return Array.isArray(roles) && roles.includes(role);\n }\n\n return {\n // State (readonly refs)\n isAuthenticated,\n isLoading,\n user,\n token,\n setupPasskeyToken,\n error,\n authReady,\n userName,\n\n // Actions\n signIn,\n signOut,\n acquireToken,\n\n // Utilities\n hasRole,\n };\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phila/sso-vue",
3
- "version": "0.0.9",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Vue 3 adapter for @phila/sso-core",
6
6
  "main": "./dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "author": "City of Philadelphia",
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@phila/sso-core": "0.0.9"
29
+ "@phila/sso-core": "0.2.0"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "vue": "^3.0.0",