@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 +140 -0
- package/dist/index.js +94 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +95 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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":"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"}
|
|
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
|
};
|
package/dist/index.mjs.map
CHANGED
|
@@ -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
|
|
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
|
|
29
|
+
"@phila/sso-core": "0.2.0"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"vue": "^3.0.0",
|