@bagelink/auth 1.7.92 → 1.7.96
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/api.d.ts +14 -1
- package/dist/index.cjs +86 -26
- package/dist/index.mjs +86 -26
- package/dist/types.d.ts +30 -18
- package/dist/useAuth.d.ts +85 -25
- package/package.json +1 -1
- package/src/api.ts +33 -0
- package/src/types.ts +58 -49
- package/src/useAuth.ts +69 -8
package/dist/api.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { RegisterRequest, UpdateAccountRequest, ChangePasswordRequest, ResetPasswordRequest, SendVerificationRequest, AuthenticationAccount, LoginResponse, RegisterResponse, LogoutResponse, GetMeResponse, UpdateMeResponse, DeleteMeResponse, GetAccountResponse, UpdateAccountResponse, DeleteAccountResponse, ActivateAccountResponse, DeactivateAccountResponse, ChangePasswordResponse, ForgotPasswordResponse, ResetPasswordResponse, VerifyResetTokenResponse, SendVerificationResponse, VerifyEmailResponse, RefreshSessionResponse, GetSessionsResponse, DeleteSessionResponse, DeleteAllSessionsResponse, CleanupSessionsResponse, GetMethodsResponse, SSOProvider, SSOInitiateRequest, SSOCallbackRequest, SSOLinkRequest, SSOInitiateResponse, SSOCallbackResponse, SSOLinkResponse, SSOUnlinkResponse } from './types';
|
|
1
|
+
import { RegisterRequest, UpdateAccountRequest, ChangePasswordRequest, ResetPasswordRequest, SendVerificationRequest, AuthenticationAccount, LoginResponse, RegisterResponse, LogoutResponse, GetMeResponse, UpdateMeResponse, DeleteMeResponse, GetAccountResponse, UpdateAccountResponse, DeleteAccountResponse, ActivateAccountResponse, DeactivateAccountResponse, ChangePasswordResponse, ForgotPasswordResponse, ResetPasswordResponse, VerifyResetTokenResponse, SendVerificationResponse, VerifyEmailResponse, RefreshSessionResponse, GetSessionsResponse, DeleteSessionResponse, DeleteAllSessionsResponse, CleanupSessionsResponse, GetMethodsResponse, SSOProvider, SSOInitiateRequest, SSOCallbackRequest, SSOLinkRequest, SSOInitiateResponse, SSOCallbackResponse, SSOLinkResponse, SSOUnlinkResponse, GetTenantsResponse } from './types';
|
|
2
2
|
export declare class AuthApi {
|
|
3
3
|
private api;
|
|
4
|
+
private currentTenantId;
|
|
4
5
|
constructor(baseURL?: string);
|
|
6
|
+
/**
|
|
7
|
+
* Set the current tenant ID for multi-tenant requests
|
|
8
|
+
*/
|
|
9
|
+
setTenantId(tenantId: string | null): void;
|
|
10
|
+
/**
|
|
11
|
+
* Get the current tenant ID
|
|
12
|
+
*/
|
|
13
|
+
getTenantId(): string | null;
|
|
5
14
|
private setupInterceptors;
|
|
6
15
|
/**
|
|
7
16
|
* Get available authentication methods
|
|
@@ -112,4 +121,8 @@ export declare class AuthApi {
|
|
|
112
121
|
* Cleanup expired sessions (admin)
|
|
113
122
|
*/
|
|
114
123
|
cleanupSessions(): Promise<CleanupSessionsResponse>;
|
|
124
|
+
/**
|
|
125
|
+
* Get list of tenants the authenticated user belongs to
|
|
126
|
+
*/
|
|
127
|
+
getTenants(): Promise<GetTenantsResponse>;
|
|
115
128
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -65,9 +65,22 @@ function queryParams() {
|
|
|
65
65
|
class AuthApi {
|
|
66
66
|
constructor(baseURL = "") {
|
|
67
67
|
__publicField(this, "api");
|
|
68
|
+
__publicField(this, "currentTenantId", null);
|
|
68
69
|
this.api = createAxiosInstance(baseURL);
|
|
69
70
|
this.setupInterceptors();
|
|
70
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Set the current tenant ID for multi-tenant requests
|
|
74
|
+
*/
|
|
75
|
+
setTenantId(tenantId) {
|
|
76
|
+
this.currentTenantId = tenantId;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get the current tenant ID
|
|
80
|
+
*/
|
|
81
|
+
getTenantId() {
|
|
82
|
+
return this.currentTenantId;
|
|
83
|
+
}
|
|
71
84
|
setupInterceptors() {
|
|
72
85
|
this.api.interceptors.request.use((config) => {
|
|
73
86
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -75,6 +88,9 @@ class AuthApi {
|
|
|
75
88
|
if (resetToken !== null) {
|
|
76
89
|
config.headers["X-Reset-Token"] = resetToken;
|
|
77
90
|
}
|
|
91
|
+
if (this.currentTenantId !== null) {
|
|
92
|
+
config.headers["X-Tenant-ID"] = this.currentTenantId;
|
|
93
|
+
}
|
|
78
94
|
return config;
|
|
79
95
|
});
|
|
80
96
|
}
|
|
@@ -281,6 +297,15 @@ class AuthApi {
|
|
|
281
297
|
async cleanupSessions() {
|
|
282
298
|
return this.api.post("/authentication/cleanup-sessions", {});
|
|
283
299
|
}
|
|
300
|
+
// ============================================
|
|
301
|
+
// Multi-Tenancy Methods
|
|
302
|
+
// ============================================
|
|
303
|
+
/**
|
|
304
|
+
* Get list of tenants the authenticated user belongs to
|
|
305
|
+
*/
|
|
306
|
+
async getTenants() {
|
|
307
|
+
return this.api.get("/tenants");
|
|
308
|
+
}
|
|
284
309
|
}
|
|
285
310
|
const _hoisted_1$8 = { class: "txt20 bold mb-1" };
|
|
286
311
|
const _hoisted_2$4 = { class: "txt-center opacity-7" };
|
|
@@ -1902,35 +1927,24 @@ function accountToUser(account) {
|
|
|
1902
1927
|
if (account === null) {
|
|
1903
1928
|
return null;
|
|
1904
1929
|
}
|
|
1905
|
-
|
|
1930
|
+
const hasPersonLinked = account.person !== void 0 && account.person !== null;
|
|
1931
|
+
if (hasPersonLinked) {
|
|
1906
1932
|
return {
|
|
1907
1933
|
id: account.person.id,
|
|
1908
1934
|
accountId: account.id,
|
|
1909
1935
|
name: account.person.name,
|
|
1910
|
-
email: account.person.email,
|
|
1936
|
+
email: account.person.email ?? void 0,
|
|
1911
1937
|
type: account.account_type,
|
|
1912
1938
|
roles: account.person.roles,
|
|
1913
1939
|
isActive: account.is_active,
|
|
1914
1940
|
isVerified: account.is_verified,
|
|
1915
1941
|
person: account.person,
|
|
1916
|
-
lastLogin: account.last_login
|
|
1917
|
-
|
|
1918
|
-
}
|
|
1919
|
-
if (account.entity !== void 0) {
|
|
1920
|
-
return {
|
|
1921
|
-
id: account.entity.id,
|
|
1922
|
-
accountId: account.id,
|
|
1923
|
-
name: account.entity.name,
|
|
1924
|
-
type: account.account_type,
|
|
1925
|
-
isActive: account.is_active,
|
|
1926
|
-
isVerified: account.is_verified,
|
|
1927
|
-
lastLogin: account.last_login,
|
|
1928
|
-
entityType: account.entity.type,
|
|
1929
|
-
metadata: account.entity.metadata
|
|
1942
|
+
lastLogin: account.last_login ?? void 0,
|
|
1943
|
+
hasPersonLinked: true
|
|
1930
1944
|
};
|
|
1931
1945
|
}
|
|
1932
1946
|
const emailMethod = account.authentication_methods.find(
|
|
1933
|
-
(m) => m.type === "password" || m.type === "email_token"
|
|
1947
|
+
(m) => m.type === "password" || m.type === "email_token" || m.type === "sso"
|
|
1934
1948
|
);
|
|
1935
1949
|
return {
|
|
1936
1950
|
id: account.id,
|
|
@@ -1940,7 +1954,8 @@ function accountToUser(account) {
|
|
|
1940
1954
|
type: account.account_type,
|
|
1941
1955
|
isActive: account.is_active,
|
|
1942
1956
|
isVerified: account.is_verified,
|
|
1943
|
-
lastLogin: account.last_login
|
|
1957
|
+
lastLogin: account.last_login ?? void 0,
|
|
1958
|
+
hasPersonLinked: false
|
|
1944
1959
|
};
|
|
1945
1960
|
}
|
|
1946
1961
|
const DEFAULT_REDIRECT_CONFIG = {
|
|
@@ -1965,6 +1980,8 @@ let redirectConfig = null;
|
|
|
1965
1980
|
let autoRedirectRouter = null;
|
|
1966
1981
|
let cachedAuthGuard = null;
|
|
1967
1982
|
const accountInfo = vue.ref(null);
|
|
1983
|
+
const tenants = vue.ref([]);
|
|
1984
|
+
const currentTenant = vue.ref(null);
|
|
1968
1985
|
function getRedirectConfig() {
|
|
1969
1986
|
if (!redirectConfig) {
|
|
1970
1987
|
throw new Error("Redirect config not initialized. Did you call createAuth with redirect config?");
|
|
@@ -2135,15 +2152,11 @@ function useAuth() {
|
|
|
2135
2152
|
};
|
|
2136
2153
|
const getAccountType = () => {
|
|
2137
2154
|
var _a;
|
|
2138
|
-
return ((_a = user.value) == null ? void 0 : _a.type) ?? "
|
|
2155
|
+
return ((_a = user.value) == null ? void 0 : _a.type) ?? "identity";
|
|
2139
2156
|
};
|
|
2140
2157
|
const isPersonAccount = () => {
|
|
2141
2158
|
var _a;
|
|
2142
|
-
return ((_a = user.value) == null ? void 0 : _a.
|
|
2143
|
-
};
|
|
2144
|
-
const isEntityAccount = () => {
|
|
2145
|
-
var _a;
|
|
2146
|
-
return ((_a = user.value) == null ? void 0 : _a.type) === "entity";
|
|
2159
|
+
return ((_a = user.value) == null ? void 0 : _a.hasPersonLinked) === true;
|
|
2147
2160
|
};
|
|
2148
2161
|
async function logout() {
|
|
2149
2162
|
const logoutPromise = api.logout();
|
|
@@ -2176,6 +2189,45 @@ function useAuth() {
|
|
|
2176
2189
|
return false;
|
|
2177
2190
|
}
|
|
2178
2191
|
}
|
|
2192
|
+
async function loadTenants() {
|
|
2193
|
+
try {
|
|
2194
|
+
const { data } = await api.getTenants();
|
|
2195
|
+
tenants.value = data;
|
|
2196
|
+
if (currentTenant.value === null && tenants.value.length > 0) {
|
|
2197
|
+
const firstActiveTenant = tenants.value.find((t) => t.status === "active");
|
|
2198
|
+
if (firstActiveTenant !== void 0) {
|
|
2199
|
+
setTenant(firstActiveTenant.id);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
return tenants.value;
|
|
2203
|
+
} catch {
|
|
2204
|
+
tenants.value = [];
|
|
2205
|
+
return [];
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
function setTenant(tenantId) {
|
|
2209
|
+
if (tenantId === null) {
|
|
2210
|
+
currentTenant.value = null;
|
|
2211
|
+
api.setTenantId(null);
|
|
2212
|
+
return;
|
|
2213
|
+
}
|
|
2214
|
+
const tenant = tenants.value.find((t) => t.id === tenantId);
|
|
2215
|
+
if (tenant !== void 0) {
|
|
2216
|
+
currentTenant.value = tenant;
|
|
2217
|
+
api.setTenantId(tenantId);
|
|
2218
|
+
} else {
|
|
2219
|
+
throw new Error(`Tenant with ID ${tenantId} not found`);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
function switchTenant(tenantId) {
|
|
2223
|
+
setTenant(tenantId);
|
|
2224
|
+
}
|
|
2225
|
+
function getTenants() {
|
|
2226
|
+
return tenants.value;
|
|
2227
|
+
}
|
|
2228
|
+
function getCurrentTenant() {
|
|
2229
|
+
return currentTenant.value;
|
|
2230
|
+
}
|
|
2179
2231
|
async function signup(newUser) {
|
|
2180
2232
|
const hasPassword = newUser.password !== void 0 && newUser.password.length > 0;
|
|
2181
2233
|
if (hasPassword && newUser.password !== newUser.confirmPassword) {
|
|
@@ -2290,6 +2342,9 @@ function useAuth() {
|
|
|
2290
2342
|
accountInfo,
|
|
2291
2343
|
// SSO Providers (ready to use!)
|
|
2292
2344
|
sso,
|
|
2345
|
+
// Multi-Tenancy State
|
|
2346
|
+
tenants,
|
|
2347
|
+
currentTenant,
|
|
2293
2348
|
// Getters
|
|
2294
2349
|
getFullName,
|
|
2295
2350
|
getIsLoggedIn,
|
|
@@ -2297,7 +2352,8 @@ function useAuth() {
|
|
|
2297
2352
|
getRoles,
|
|
2298
2353
|
getAccountType,
|
|
2299
2354
|
isPersonAccount,
|
|
2300
|
-
|
|
2355
|
+
getTenants,
|
|
2356
|
+
getCurrentTenant,
|
|
2301
2357
|
// Authentication Actions
|
|
2302
2358
|
login,
|
|
2303
2359
|
logout,
|
|
@@ -2327,7 +2383,11 @@ function useAuth() {
|
|
|
2327
2383
|
// Session Management
|
|
2328
2384
|
getSessions,
|
|
2329
2385
|
revokeSession,
|
|
2330
|
-
revokeAllSessions
|
|
2386
|
+
revokeAllSessions,
|
|
2387
|
+
// Multi-Tenancy Actions
|
|
2388
|
+
loadTenants,
|
|
2389
|
+
setTenant,
|
|
2390
|
+
switchTenant
|
|
2331
2391
|
};
|
|
2332
2392
|
}
|
|
2333
2393
|
const useAuth$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
package/dist/index.mjs
CHANGED
|
@@ -63,9 +63,22 @@ function queryParams() {
|
|
|
63
63
|
class AuthApi {
|
|
64
64
|
constructor(baseURL = "") {
|
|
65
65
|
__publicField(this, "api");
|
|
66
|
+
__publicField(this, "currentTenantId", null);
|
|
66
67
|
this.api = createAxiosInstance(baseURL);
|
|
67
68
|
this.setupInterceptors();
|
|
68
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Set the current tenant ID for multi-tenant requests
|
|
72
|
+
*/
|
|
73
|
+
setTenantId(tenantId) {
|
|
74
|
+
this.currentTenantId = tenantId;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get the current tenant ID
|
|
78
|
+
*/
|
|
79
|
+
getTenantId() {
|
|
80
|
+
return this.currentTenantId;
|
|
81
|
+
}
|
|
69
82
|
setupInterceptors() {
|
|
70
83
|
this.api.interceptors.request.use((config) => {
|
|
71
84
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -73,6 +86,9 @@ class AuthApi {
|
|
|
73
86
|
if (resetToken !== null) {
|
|
74
87
|
config.headers["X-Reset-Token"] = resetToken;
|
|
75
88
|
}
|
|
89
|
+
if (this.currentTenantId !== null) {
|
|
90
|
+
config.headers["X-Tenant-ID"] = this.currentTenantId;
|
|
91
|
+
}
|
|
76
92
|
return config;
|
|
77
93
|
});
|
|
78
94
|
}
|
|
@@ -279,6 +295,15 @@ class AuthApi {
|
|
|
279
295
|
async cleanupSessions() {
|
|
280
296
|
return this.api.post("/authentication/cleanup-sessions", {});
|
|
281
297
|
}
|
|
298
|
+
// ============================================
|
|
299
|
+
// Multi-Tenancy Methods
|
|
300
|
+
// ============================================
|
|
301
|
+
/**
|
|
302
|
+
* Get list of tenants the authenticated user belongs to
|
|
303
|
+
*/
|
|
304
|
+
async getTenants() {
|
|
305
|
+
return this.api.get("/tenants");
|
|
306
|
+
}
|
|
282
307
|
}
|
|
283
308
|
const _hoisted_1$8 = { class: "txt20 bold mb-1" };
|
|
284
309
|
const _hoisted_2$4 = { class: "txt-center opacity-7" };
|
|
@@ -1900,35 +1925,24 @@ function accountToUser(account) {
|
|
|
1900
1925
|
if (account === null) {
|
|
1901
1926
|
return null;
|
|
1902
1927
|
}
|
|
1903
|
-
|
|
1928
|
+
const hasPersonLinked = account.person !== void 0 && account.person !== null;
|
|
1929
|
+
if (hasPersonLinked) {
|
|
1904
1930
|
return {
|
|
1905
1931
|
id: account.person.id,
|
|
1906
1932
|
accountId: account.id,
|
|
1907
1933
|
name: account.person.name,
|
|
1908
|
-
email: account.person.email,
|
|
1934
|
+
email: account.person.email ?? void 0,
|
|
1909
1935
|
type: account.account_type,
|
|
1910
1936
|
roles: account.person.roles,
|
|
1911
1937
|
isActive: account.is_active,
|
|
1912
1938
|
isVerified: account.is_verified,
|
|
1913
1939
|
person: account.person,
|
|
1914
|
-
lastLogin: account.last_login
|
|
1915
|
-
|
|
1916
|
-
}
|
|
1917
|
-
if (account.entity !== void 0) {
|
|
1918
|
-
return {
|
|
1919
|
-
id: account.entity.id,
|
|
1920
|
-
accountId: account.id,
|
|
1921
|
-
name: account.entity.name,
|
|
1922
|
-
type: account.account_type,
|
|
1923
|
-
isActive: account.is_active,
|
|
1924
|
-
isVerified: account.is_verified,
|
|
1925
|
-
lastLogin: account.last_login,
|
|
1926
|
-
entityType: account.entity.type,
|
|
1927
|
-
metadata: account.entity.metadata
|
|
1940
|
+
lastLogin: account.last_login ?? void 0,
|
|
1941
|
+
hasPersonLinked: true
|
|
1928
1942
|
};
|
|
1929
1943
|
}
|
|
1930
1944
|
const emailMethod = account.authentication_methods.find(
|
|
1931
|
-
(m) => m.type === "password" || m.type === "email_token"
|
|
1945
|
+
(m) => m.type === "password" || m.type === "email_token" || m.type === "sso"
|
|
1932
1946
|
);
|
|
1933
1947
|
return {
|
|
1934
1948
|
id: account.id,
|
|
@@ -1938,7 +1952,8 @@ function accountToUser(account) {
|
|
|
1938
1952
|
type: account.account_type,
|
|
1939
1953
|
isActive: account.is_active,
|
|
1940
1954
|
isVerified: account.is_verified,
|
|
1941
|
-
lastLogin: account.last_login
|
|
1955
|
+
lastLogin: account.last_login ?? void 0,
|
|
1956
|
+
hasPersonLinked: false
|
|
1942
1957
|
};
|
|
1943
1958
|
}
|
|
1944
1959
|
const DEFAULT_REDIRECT_CONFIG = {
|
|
@@ -1963,6 +1978,8 @@ let redirectConfig = null;
|
|
|
1963
1978
|
let autoRedirectRouter = null;
|
|
1964
1979
|
let cachedAuthGuard = null;
|
|
1965
1980
|
const accountInfo = ref(null);
|
|
1981
|
+
const tenants = ref([]);
|
|
1982
|
+
const currentTenant = ref(null);
|
|
1966
1983
|
function getRedirectConfig() {
|
|
1967
1984
|
if (!redirectConfig) {
|
|
1968
1985
|
throw new Error("Redirect config not initialized. Did you call createAuth with redirect config?");
|
|
@@ -2133,15 +2150,11 @@ function useAuth() {
|
|
|
2133
2150
|
};
|
|
2134
2151
|
const getAccountType = () => {
|
|
2135
2152
|
var _a;
|
|
2136
|
-
return ((_a = user.value) == null ? void 0 : _a.type) ?? "
|
|
2153
|
+
return ((_a = user.value) == null ? void 0 : _a.type) ?? "identity";
|
|
2137
2154
|
};
|
|
2138
2155
|
const isPersonAccount = () => {
|
|
2139
2156
|
var _a;
|
|
2140
|
-
return ((_a = user.value) == null ? void 0 : _a.
|
|
2141
|
-
};
|
|
2142
|
-
const isEntityAccount = () => {
|
|
2143
|
-
var _a;
|
|
2144
|
-
return ((_a = user.value) == null ? void 0 : _a.type) === "entity";
|
|
2157
|
+
return ((_a = user.value) == null ? void 0 : _a.hasPersonLinked) === true;
|
|
2145
2158
|
};
|
|
2146
2159
|
async function logout() {
|
|
2147
2160
|
const logoutPromise = api.logout();
|
|
@@ -2174,6 +2187,45 @@ function useAuth() {
|
|
|
2174
2187
|
return false;
|
|
2175
2188
|
}
|
|
2176
2189
|
}
|
|
2190
|
+
async function loadTenants() {
|
|
2191
|
+
try {
|
|
2192
|
+
const { data } = await api.getTenants();
|
|
2193
|
+
tenants.value = data;
|
|
2194
|
+
if (currentTenant.value === null && tenants.value.length > 0) {
|
|
2195
|
+
const firstActiveTenant = tenants.value.find((t) => t.status === "active");
|
|
2196
|
+
if (firstActiveTenant !== void 0) {
|
|
2197
|
+
setTenant(firstActiveTenant.id);
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
return tenants.value;
|
|
2201
|
+
} catch {
|
|
2202
|
+
tenants.value = [];
|
|
2203
|
+
return [];
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
function setTenant(tenantId) {
|
|
2207
|
+
if (tenantId === null) {
|
|
2208
|
+
currentTenant.value = null;
|
|
2209
|
+
api.setTenantId(null);
|
|
2210
|
+
return;
|
|
2211
|
+
}
|
|
2212
|
+
const tenant = tenants.value.find((t) => t.id === tenantId);
|
|
2213
|
+
if (tenant !== void 0) {
|
|
2214
|
+
currentTenant.value = tenant;
|
|
2215
|
+
api.setTenantId(tenantId);
|
|
2216
|
+
} else {
|
|
2217
|
+
throw new Error(`Tenant with ID ${tenantId} not found`);
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
function switchTenant(tenantId) {
|
|
2221
|
+
setTenant(tenantId);
|
|
2222
|
+
}
|
|
2223
|
+
function getTenants() {
|
|
2224
|
+
return tenants.value;
|
|
2225
|
+
}
|
|
2226
|
+
function getCurrentTenant() {
|
|
2227
|
+
return currentTenant.value;
|
|
2228
|
+
}
|
|
2177
2229
|
async function signup(newUser) {
|
|
2178
2230
|
const hasPassword = newUser.password !== void 0 && newUser.password.length > 0;
|
|
2179
2231
|
if (hasPassword && newUser.password !== newUser.confirmPassword) {
|
|
@@ -2288,6 +2340,9 @@ function useAuth() {
|
|
|
2288
2340
|
accountInfo,
|
|
2289
2341
|
// SSO Providers (ready to use!)
|
|
2290
2342
|
sso,
|
|
2343
|
+
// Multi-Tenancy State
|
|
2344
|
+
tenants,
|
|
2345
|
+
currentTenant,
|
|
2291
2346
|
// Getters
|
|
2292
2347
|
getFullName,
|
|
2293
2348
|
getIsLoggedIn,
|
|
@@ -2295,7 +2350,8 @@ function useAuth() {
|
|
|
2295
2350
|
getRoles,
|
|
2296
2351
|
getAccountType,
|
|
2297
2352
|
isPersonAccount,
|
|
2298
|
-
|
|
2353
|
+
getTenants,
|
|
2354
|
+
getCurrentTenant,
|
|
2299
2355
|
// Authentication Actions
|
|
2300
2356
|
login,
|
|
2301
2357
|
logout,
|
|
@@ -2325,7 +2381,11 @@ function useAuth() {
|
|
|
2325
2381
|
// Session Management
|
|
2326
2382
|
getSessions,
|
|
2327
2383
|
revokeSession,
|
|
2328
|
-
revokeAllSessions
|
|
2384
|
+
revokeAllSessions,
|
|
2385
|
+
// Multi-Tenancy Actions
|
|
2386
|
+
loadTenants,
|
|
2387
|
+
setTenant,
|
|
2388
|
+
switchTenant
|
|
2329
2389
|
};
|
|
2330
2390
|
}
|
|
2331
2391
|
const useAuth$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
package/dist/types.d.ts
CHANGED
|
@@ -22,7 +22,20 @@ export interface AuthEventMap {
|
|
|
22
22
|
[AuthState.EMAIL_VERIFIED]: AuthEventHandler;
|
|
23
23
|
[AuthState.SESSION_REFRESH]: AuthEventHandler;
|
|
24
24
|
}
|
|
25
|
-
export type AuthenticationAccountType = '
|
|
25
|
+
export type AuthenticationAccountType = 'identity' | string;
|
|
26
|
+
export type TenantStatus = 'active' | 'suspended' | 'archived';
|
|
27
|
+
export interface TenantInfo {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
slug: string;
|
|
31
|
+
parent_id: string | null;
|
|
32
|
+
settings: Record<string, any> | null;
|
|
33
|
+
status: TenantStatus;
|
|
34
|
+
suspended_at: string | null;
|
|
35
|
+
suspended_reason: string | null;
|
|
36
|
+
created_at: string;
|
|
37
|
+
updated_at: string;
|
|
38
|
+
}
|
|
26
39
|
export type AuthenticationMethodType = 'password' | 'email_token' | 'sso' | 'otp';
|
|
27
40
|
export type SSOProvider = 'google' | 'microsoft' | 'github' | 'okta' | 'apple' | 'facebook';
|
|
28
41
|
export interface AuthenticationAccount {
|
|
@@ -43,11 +56,11 @@ export interface AuthenticationAccount {
|
|
|
43
56
|
export interface PersonInfo {
|
|
44
57
|
id: string;
|
|
45
58
|
name: string;
|
|
46
|
-
email?: string;
|
|
47
|
-
phone?: string;
|
|
48
|
-
roles: string[];
|
|
49
59
|
first_name: string;
|
|
50
60
|
last_name: string;
|
|
61
|
+
email?: string | null;
|
|
62
|
+
phone_number?: string | null;
|
|
63
|
+
roles: string[];
|
|
51
64
|
}
|
|
52
65
|
export interface AuthMethodInfo {
|
|
53
66
|
id: string;
|
|
@@ -65,10 +78,9 @@ export interface AccountInfo {
|
|
|
65
78
|
display_name: string;
|
|
66
79
|
is_active: boolean;
|
|
67
80
|
is_verified: boolean;
|
|
68
|
-
last_login?: string;
|
|
81
|
+
last_login?: string | null;
|
|
69
82
|
authentication_methods: AuthMethodInfo[];
|
|
70
|
-
person?: PersonInfo;
|
|
71
|
-
entity?: EntityInfo;
|
|
83
|
+
person?: PersonInfo | null;
|
|
72
84
|
}
|
|
73
85
|
export interface EntityInfo {
|
|
74
86
|
id: string;
|
|
@@ -85,21 +97,21 @@ export interface SessionInfo {
|
|
|
85
97
|
is_current?: boolean;
|
|
86
98
|
}
|
|
87
99
|
/**
|
|
88
|
-
* Unified user representation
|
|
89
|
-
*
|
|
100
|
+
* Unified user representation
|
|
101
|
+
* All accounts are "identity" accounts that may be linked to a person
|
|
90
102
|
*/
|
|
91
103
|
export interface User {
|
|
92
|
-
/** Unique identifier (person_id
|
|
104
|
+
/** Unique identifier (person_id if linked, otherwise identity_id) */
|
|
93
105
|
id: string;
|
|
94
|
-
/** Account ID */
|
|
106
|
+
/** Identity/Account ID */
|
|
95
107
|
accountId: string;
|
|
96
108
|
/** Display name */
|
|
97
109
|
name: string;
|
|
98
110
|
/** Email address (from person or authentication methods) */
|
|
99
111
|
email?: string;
|
|
100
|
-
/** Account type
|
|
112
|
+
/** Account type (always 'identity') */
|
|
101
113
|
type: AuthenticationAccountType;
|
|
102
|
-
/** User roles (only
|
|
114
|
+
/** User roles (only when linked to person) */
|
|
103
115
|
roles?: string[];
|
|
104
116
|
/** Is the account active */
|
|
105
117
|
isActive: boolean;
|
|
@@ -107,12 +119,10 @@ export interface User {
|
|
|
107
119
|
isVerified: boolean;
|
|
108
120
|
/** Last login timestamp */
|
|
109
121
|
lastLogin?: string;
|
|
110
|
-
/**
|
|
111
|
-
entityType?: string;
|
|
112
|
-
/** Additional metadata */
|
|
113
|
-
metadata?: Record<string, any>;
|
|
114
|
-
/** Person-specific info (only for person accounts) */
|
|
122
|
+
/** Person info (if identity is linked to a person) */
|
|
115
123
|
person?: PersonInfo;
|
|
124
|
+
/** Whether identity is linked to a person */
|
|
125
|
+
hasPersonLinked: boolean;
|
|
116
126
|
}
|
|
117
127
|
export interface RegisterRequest {
|
|
118
128
|
email: string;
|
|
@@ -248,7 +258,9 @@ export type SSOInitiateResponse = AxiosResponse<{
|
|
|
248
258
|
export type SSOCallbackResponse = AxiosResponse<AuthenticationResponse>;
|
|
249
259
|
export type SSOLinkResponse = AxiosResponse<MessageResponse>;
|
|
250
260
|
export type SSOUnlinkResponse = AxiosResponse<MessageResponse>;
|
|
261
|
+
export type GetTenantsResponse = AxiosResponse<TenantInfo[]>;
|
|
251
262
|
/**
|
|
252
263
|
* Extract unified user from account info
|
|
264
|
+
* All accounts are identities that may be linked to a person
|
|
253
265
|
*/
|
|
254
266
|
export declare function accountToUser(account: AccountInfo | null): User | null;
|
package/dist/useAuth.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { App, ObjectPlugin } from 'vue';
|
|
2
|
-
import { AccountInfo, User, NewUser, UpdatePasswordForm, UpdateAccountRequest, AuthEventMap, SSOProvider, SSOInitiateRequest, SSOCallbackRequest, SSOLinkRequest, AuthState } from './types';
|
|
2
|
+
import { AccountInfo, User, NewUser, UpdatePasswordForm, UpdateAccountRequest, AuthEventMap, SSOProvider, SSOInitiateRequest, SSOCallbackRequest, SSOLinkRequest, TenantInfo, AuthState } from './types';
|
|
3
3
|
import { RedirectConfig, NormalizedRedirectConfig } from './types/redirect';
|
|
4
4
|
interface InitParams {
|
|
5
5
|
baseURL: string;
|
|
@@ -37,7 +37,7 @@ export declare function useAuth(): {
|
|
|
37
37
|
display_name: string;
|
|
38
38
|
is_active: boolean;
|
|
39
39
|
is_verified: boolean;
|
|
40
|
-
last_login?: string | undefined;
|
|
40
|
+
last_login?: string | null | undefined;
|
|
41
41
|
authentication_methods: {
|
|
42
42
|
id: string;
|
|
43
43
|
type: string;
|
|
@@ -51,25 +51,19 @@ export declare function useAuth(): {
|
|
|
51
51
|
person?: {
|
|
52
52
|
id: string;
|
|
53
53
|
name: string;
|
|
54
|
-
email?: string | undefined;
|
|
55
|
-
phone?: string | undefined;
|
|
56
|
-
roles: string[];
|
|
57
54
|
first_name: string;
|
|
58
55
|
last_name: string;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
type?: string | undefined;
|
|
64
|
-
metadata?: Record<string, any> | undefined;
|
|
65
|
-
} | undefined;
|
|
56
|
+
email?: string | null | undefined;
|
|
57
|
+
phone_number?: string | null | undefined;
|
|
58
|
+
roles: string[];
|
|
59
|
+
} | null | undefined;
|
|
66
60
|
} | null, AccountInfo | {
|
|
67
61
|
id: string;
|
|
68
62
|
account_type: string;
|
|
69
63
|
display_name: string;
|
|
70
64
|
is_active: boolean;
|
|
71
65
|
is_verified: boolean;
|
|
72
|
-
last_login?: string | undefined;
|
|
66
|
+
last_login?: string | null | undefined;
|
|
73
67
|
authentication_methods: {
|
|
74
68
|
id: string;
|
|
75
69
|
type: string;
|
|
@@ -83,27 +77,90 @@ export declare function useAuth(): {
|
|
|
83
77
|
person?: {
|
|
84
78
|
id: string;
|
|
85
79
|
name: string;
|
|
86
|
-
email?: string | undefined;
|
|
87
|
-
phone?: string | undefined;
|
|
88
|
-
roles: string[];
|
|
89
80
|
first_name: string;
|
|
90
81
|
last_name: string;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
type?: string | undefined;
|
|
96
|
-
metadata?: Record<string, any> | undefined;
|
|
97
|
-
} | undefined;
|
|
82
|
+
email?: string | null | undefined;
|
|
83
|
+
phone_number?: string | null | undefined;
|
|
84
|
+
roles: string[];
|
|
85
|
+
} | null | undefined;
|
|
98
86
|
} | null>;
|
|
99
87
|
sso: import('./sso').SSOObject;
|
|
88
|
+
tenants: import('vue').Ref<{
|
|
89
|
+
id: string;
|
|
90
|
+
name: string;
|
|
91
|
+
slug: string;
|
|
92
|
+
parent_id: string | null;
|
|
93
|
+
settings: Record<string, any> | null;
|
|
94
|
+
status: import('./types').TenantStatus;
|
|
95
|
+
suspended_at: string | null;
|
|
96
|
+
suspended_reason: string | null;
|
|
97
|
+
created_at: string;
|
|
98
|
+
updated_at: string;
|
|
99
|
+
}[], TenantInfo[] | {
|
|
100
|
+
id: string;
|
|
101
|
+
name: string;
|
|
102
|
+
slug: string;
|
|
103
|
+
parent_id: string | null;
|
|
104
|
+
settings: Record<string, any> | null;
|
|
105
|
+
status: import('./types').TenantStatus;
|
|
106
|
+
suspended_at: string | null;
|
|
107
|
+
suspended_reason: string | null;
|
|
108
|
+
created_at: string;
|
|
109
|
+
updated_at: string;
|
|
110
|
+
}[]>;
|
|
111
|
+
currentTenant: import('vue').Ref<{
|
|
112
|
+
id: string;
|
|
113
|
+
name: string;
|
|
114
|
+
slug: string;
|
|
115
|
+
parent_id: string | null;
|
|
116
|
+
settings: Record<string, any> | null;
|
|
117
|
+
status: import('./types').TenantStatus;
|
|
118
|
+
suspended_at: string | null;
|
|
119
|
+
suspended_reason: string | null;
|
|
120
|
+
created_at: string;
|
|
121
|
+
updated_at: string;
|
|
122
|
+
} | null, TenantInfo | {
|
|
123
|
+
id: string;
|
|
124
|
+
name: string;
|
|
125
|
+
slug: string;
|
|
126
|
+
parent_id: string | null;
|
|
127
|
+
settings: Record<string, any> | null;
|
|
128
|
+
status: import('./types').TenantStatus;
|
|
129
|
+
suspended_at: string | null;
|
|
130
|
+
suspended_reason: string | null;
|
|
131
|
+
created_at: string;
|
|
132
|
+
updated_at: string;
|
|
133
|
+
} | null>;
|
|
100
134
|
getFullName: () => string;
|
|
101
135
|
getIsLoggedIn: () => boolean;
|
|
102
136
|
getEmail: () => string;
|
|
103
137
|
getRoles: () => string[];
|
|
104
|
-
getAccountType: () =>
|
|
138
|
+
getAccountType: () => string;
|
|
105
139
|
isPersonAccount: () => boolean;
|
|
106
|
-
|
|
140
|
+
getTenants: () => {
|
|
141
|
+
id: string;
|
|
142
|
+
name: string;
|
|
143
|
+
slug: string;
|
|
144
|
+
parent_id: string | null;
|
|
145
|
+
settings: Record<string, any> | null;
|
|
146
|
+
status: import('./types').TenantStatus;
|
|
147
|
+
suspended_at: string | null;
|
|
148
|
+
suspended_reason: string | null;
|
|
149
|
+
created_at: string;
|
|
150
|
+
updated_at: string;
|
|
151
|
+
}[];
|
|
152
|
+
getCurrentTenant: () => {
|
|
153
|
+
id: string;
|
|
154
|
+
name: string;
|
|
155
|
+
slug: string;
|
|
156
|
+
parent_id: string | null;
|
|
157
|
+
settings: Record<string, any> | null;
|
|
158
|
+
status: import('./types').TenantStatus;
|
|
159
|
+
suspended_at: string | null;
|
|
160
|
+
suspended_reason: string | null;
|
|
161
|
+
created_at: string;
|
|
162
|
+
updated_at: string;
|
|
163
|
+
} | null;
|
|
107
164
|
login: (credentials: {
|
|
108
165
|
email: string;
|
|
109
166
|
password: string;
|
|
@@ -130,5 +187,8 @@ export declare function useAuth(): {
|
|
|
130
187
|
getSessions: (accountId?: string) => Promise<import('./types').GetSessionsResponse>;
|
|
131
188
|
revokeSession: (sessionToken: string) => Promise<void>;
|
|
132
189
|
revokeAllSessions: (accountId?: string) => Promise<void>;
|
|
190
|
+
loadTenants: () => Promise<TenantInfo[]>;
|
|
191
|
+
setTenant: (tenantId: string | null) => void;
|
|
192
|
+
switchTenant: (tenantId: string) => void;
|
|
133
193
|
};
|
|
134
194
|
export {};
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -37,17 +37,33 @@ import type {
|
|
|
37
37
|
SSOCallbackResponse,
|
|
38
38
|
SSOLinkResponse,
|
|
39
39
|
SSOUnlinkResponse,
|
|
40
|
+
GetTenantsResponse,
|
|
40
41
|
} from './types'
|
|
41
42
|
import { createAxiosInstance } from './utils'
|
|
42
43
|
|
|
43
44
|
export class AuthApi {
|
|
44
45
|
private api: AxiosInstance
|
|
46
|
+
private currentTenantId: string | null = null
|
|
45
47
|
|
|
46
48
|
constructor(baseURL: string = '') {
|
|
47
49
|
this.api = createAxiosInstance(baseURL)
|
|
48
50
|
this.setupInterceptors()
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Set the current tenant ID for multi-tenant requests
|
|
55
|
+
*/
|
|
56
|
+
setTenantId(tenantId: string | null) {
|
|
57
|
+
this.currentTenantId = tenantId
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get the current tenant ID
|
|
62
|
+
*/
|
|
63
|
+
getTenantId(): string | null {
|
|
64
|
+
return this.currentTenantId
|
|
65
|
+
}
|
|
66
|
+
|
|
51
67
|
private setupInterceptors() {
|
|
52
68
|
this.api.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
|
53
69
|
// Handle password reset token from URL
|
|
@@ -56,6 +72,12 @@ export class AuthApi {
|
|
|
56
72
|
if (resetToken !== null) {
|
|
57
73
|
config.headers['X-Reset-Token'] = resetToken
|
|
58
74
|
}
|
|
75
|
+
|
|
76
|
+
// Add tenant ID header if set
|
|
77
|
+
if (this.currentTenantId !== null) {
|
|
78
|
+
config.headers['X-Tenant-ID'] = this.currentTenantId
|
|
79
|
+
}
|
|
80
|
+
|
|
59
81
|
return config
|
|
60
82
|
})
|
|
61
83
|
}
|
|
@@ -304,4 +326,15 @@ export class AuthApi {
|
|
|
304
326
|
async cleanupSessions(): Promise<CleanupSessionsResponse> {
|
|
305
327
|
return this.api.post('/authentication/cleanup-sessions', {})
|
|
306
328
|
}
|
|
329
|
+
|
|
330
|
+
// ============================================
|
|
331
|
+
// Multi-Tenancy Methods
|
|
332
|
+
// ============================================
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Get list of tenants the authenticated user belongs to
|
|
336
|
+
*/
|
|
337
|
+
async getTenants(): Promise<GetTenantsResponse> {
|
|
338
|
+
return this.api.get('/tenants')
|
|
339
|
+
}
|
|
307
340
|
}
|
package/src/types.ts
CHANGED
|
@@ -34,13 +34,33 @@ export interface AuthEventMap {
|
|
|
34
34
|
// Core Types
|
|
35
35
|
// ============================================
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
// Note: All accounts are "identity" accounts. An identity may be linked to a person or not.
|
|
38
|
+
export type AuthenticationAccountType = 'identity' | string
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
// ============================================
|
|
41
|
+
// Multi-Tenancy Types
|
|
42
|
+
// ============================================
|
|
43
|
+
|
|
44
|
+
export type TenantStatus = 'active' | 'suspended' | 'archived'
|
|
45
|
+
|
|
46
|
+
export interface TenantInfo {
|
|
47
|
+
id: string
|
|
48
|
+
name: string
|
|
49
|
+
slug: string
|
|
50
|
+
parent_id: string | null
|
|
51
|
+
settings: Record<string, any> | null
|
|
52
|
+
status: TenantStatus
|
|
53
|
+
suspended_at: string | null
|
|
54
|
+
suspended_reason: string | null
|
|
55
|
+
created_at: string
|
|
56
|
+
updated_at: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type AuthenticationMethodType
|
|
60
|
+
= | 'password'
|
|
61
|
+
| 'email_token'
|
|
62
|
+
| 'sso'
|
|
63
|
+
| 'otp'
|
|
44
64
|
|
|
45
65
|
export type SSOProvider = 'google' | 'microsoft' | 'github' | 'okta' | 'apple' | 'facebook'
|
|
46
66
|
|
|
@@ -63,11 +83,11 @@ export interface AuthenticationAccount {
|
|
|
63
83
|
export interface PersonInfo {
|
|
64
84
|
id: string
|
|
65
85
|
name: string
|
|
66
|
-
email?: string
|
|
67
|
-
phone?: string
|
|
68
|
-
roles: string[]
|
|
69
86
|
first_name: string
|
|
70
87
|
last_name: string
|
|
88
|
+
email?: string | null
|
|
89
|
+
phone_number?: string | null
|
|
90
|
+
roles: string[]
|
|
71
91
|
}
|
|
72
92
|
|
|
73
93
|
export interface AuthMethodInfo {
|
|
@@ -87,12 +107,13 @@ export interface AccountInfo {
|
|
|
87
107
|
display_name: string
|
|
88
108
|
is_active: boolean
|
|
89
109
|
is_verified: boolean
|
|
90
|
-
last_login?: string
|
|
110
|
+
last_login?: string | null
|
|
91
111
|
authentication_methods: AuthMethodInfo[]
|
|
92
|
-
person?: PersonInfo
|
|
93
|
-
entity?: EntityInfo
|
|
112
|
+
person?: PersonInfo | null
|
|
94
113
|
}
|
|
95
114
|
|
|
115
|
+
// Note: EntityInfo kept for backward compatibility
|
|
116
|
+
// Current API no longer returns entity in AccountInfo
|
|
96
117
|
export interface EntityInfo {
|
|
97
118
|
id: string
|
|
98
119
|
name: string
|
|
@@ -114,21 +135,21 @@ export interface SessionInfo {
|
|
|
114
135
|
// ============================================
|
|
115
136
|
|
|
116
137
|
/**
|
|
117
|
-
* Unified user representation
|
|
118
|
-
*
|
|
138
|
+
* Unified user representation
|
|
139
|
+
* All accounts are "identity" accounts that may be linked to a person
|
|
119
140
|
*/
|
|
120
141
|
export interface User {
|
|
121
|
-
/** Unique identifier (person_id
|
|
142
|
+
/** Unique identifier (person_id if linked, otherwise identity_id) */
|
|
122
143
|
id: string
|
|
123
|
-
/** Account ID */
|
|
144
|
+
/** Identity/Account ID */
|
|
124
145
|
accountId: string
|
|
125
146
|
/** Display name */
|
|
126
147
|
name: string
|
|
127
148
|
/** Email address (from person or authentication methods) */
|
|
128
149
|
email?: string
|
|
129
|
-
/** Account type
|
|
150
|
+
/** Account type (always 'identity') */
|
|
130
151
|
type: AuthenticationAccountType
|
|
131
|
-
/** User roles (only
|
|
152
|
+
/** User roles (only when linked to person) */
|
|
132
153
|
roles?: string[]
|
|
133
154
|
/** Is the account active */
|
|
134
155
|
isActive: boolean
|
|
@@ -136,12 +157,10 @@ export interface User {
|
|
|
136
157
|
isVerified: boolean
|
|
137
158
|
/** Last login timestamp */
|
|
138
159
|
lastLogin?: string
|
|
139
|
-
/**
|
|
140
|
-
entityType?: string
|
|
141
|
-
/** Additional metadata */
|
|
142
|
-
metadata?: Record<string, any>
|
|
143
|
-
/** Person-specific info (only for person accounts) */
|
|
160
|
+
/** Person info (if identity is linked to a person) */
|
|
144
161
|
person?: PersonInfo
|
|
162
|
+
/** Whether identity is linked to a person */
|
|
163
|
+
hasPersonLinked: boolean
|
|
145
164
|
}
|
|
146
165
|
|
|
147
166
|
// ============================================
|
|
@@ -302,6 +321,7 @@ export type SSOInitiateResponse = AxiosResponse<{ authorization_url: string }>
|
|
|
302
321
|
export type SSOCallbackResponse = AxiosResponse<AuthenticationResponse>
|
|
303
322
|
export type SSOLinkResponse = AxiosResponse<MessageResponse>
|
|
304
323
|
export type SSOUnlinkResponse = AxiosResponse<MessageResponse>
|
|
324
|
+
export type GetTenantsResponse = AxiosResponse<TenantInfo[]>
|
|
305
325
|
|
|
306
326
|
// ============================================
|
|
307
327
|
// Helper Functions (exported for convenience)
|
|
@@ -309,45 +329,33 @@ export type SSOUnlinkResponse = AxiosResponse<MessageResponse>
|
|
|
309
329
|
|
|
310
330
|
/**
|
|
311
331
|
* Extract unified user from account info
|
|
332
|
+
* All accounts are identities that may be linked to a person
|
|
312
333
|
*/
|
|
313
334
|
export function accountToUser(account: AccountInfo | null): User | null {
|
|
314
335
|
if (account === null) { return null }
|
|
315
336
|
|
|
316
|
-
|
|
317
|
-
if (account.person !== undefined) {
|
|
318
|
-
return {
|
|
319
|
-
id: account.person.id,
|
|
320
|
-
accountId: account.id,
|
|
321
|
-
name: account.person.name,
|
|
322
|
-
email: account.person.email,
|
|
323
|
-
type: account.account_type as AuthenticationAccountType,
|
|
324
|
-
roles: account.person.roles,
|
|
325
|
-
isActive: account.is_active,
|
|
326
|
-
isVerified: account.is_verified,
|
|
327
|
-
person: account.person,
|
|
328
|
-
lastLogin: account.last_login,
|
|
329
|
-
}
|
|
330
|
-
}
|
|
337
|
+
const hasPersonLinked = account.person !== undefined && account.person !== null
|
|
331
338
|
|
|
332
|
-
//
|
|
333
|
-
if (
|
|
339
|
+
// Identity linked to person - use person data
|
|
340
|
+
if (hasPersonLinked) {
|
|
334
341
|
return {
|
|
335
|
-
id: account.
|
|
342
|
+
id: account.person!.id,
|
|
336
343
|
accountId: account.id,
|
|
337
|
-
name: account.
|
|
344
|
+
name: account.person!.name,
|
|
345
|
+
email: account.person!.email ?? undefined,
|
|
338
346
|
type: account.account_type as AuthenticationAccountType,
|
|
347
|
+
roles: account.person!.roles,
|
|
339
348
|
isActive: account.is_active,
|
|
340
349
|
isVerified: account.is_verified,
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
350
|
+
person: account.person!,
|
|
351
|
+
lastLogin: account.last_login ?? undefined,
|
|
352
|
+
hasPersonLinked: true,
|
|
344
353
|
}
|
|
345
354
|
}
|
|
346
355
|
|
|
347
|
-
//
|
|
348
|
-
// Extract email from authentication methods
|
|
356
|
+
// Identity without person link - extract data from authentication methods
|
|
349
357
|
const emailMethod = account.authentication_methods.find(
|
|
350
|
-
m => m.type === 'password' || m.type === 'email_token',
|
|
358
|
+
m => m.type === 'password' || m.type === 'email_token' || m.type === 'sso',
|
|
351
359
|
)
|
|
352
360
|
|
|
353
361
|
return {
|
|
@@ -358,6 +366,7 @@ export function accountToUser(account: AccountInfo | null): User | null {
|
|
|
358
366
|
type: account.account_type as AuthenticationAccountType,
|
|
359
367
|
isActive: account.is_active,
|
|
360
368
|
isVerified: account.is_verified,
|
|
361
|
-
lastLogin: account.last_login,
|
|
369
|
+
lastLogin: account.last_login ?? undefined,
|
|
370
|
+
hasPersonLinked: false,
|
|
362
371
|
}
|
|
363
372
|
}
|
package/src/useAuth.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
SSOInitiateRequest,
|
|
11
11
|
SSOCallbackRequest,
|
|
12
12
|
SSOLinkRequest,
|
|
13
|
+
TenantInfo,
|
|
13
14
|
} from './types'
|
|
14
15
|
import type { RedirectConfig, NormalizedRedirectConfig } from './types/redirect'
|
|
15
16
|
import { ref, computed } from 'vue'
|
|
@@ -26,6 +27,8 @@ let redirectConfig: NormalizedRedirectConfig | null = null
|
|
|
26
27
|
let autoRedirectRouter: any = null // Router instance for auto-redirect
|
|
27
28
|
let cachedAuthGuard: any = null // Cached router guard
|
|
28
29
|
const accountInfo = ref<AccountInfo | null>(null)
|
|
30
|
+
const tenants = ref<TenantInfo[]>([])
|
|
31
|
+
const currentTenant = ref<TenantInfo | null>(null)
|
|
29
32
|
|
|
30
33
|
interface InitParams {
|
|
31
34
|
baseURL: string
|
|
@@ -262,17 +265,16 @@ export function useAuth() {
|
|
|
262
265
|
}
|
|
263
266
|
|
|
264
267
|
const getAccountType = () => {
|
|
265
|
-
return user.value?.type ?? '
|
|
268
|
+
return user.value?.type ?? 'identity'
|
|
266
269
|
}
|
|
267
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Check if identity is linked to a person
|
|
273
|
+
* @returns true if the identity has a person linked
|
|
274
|
+
*/
|
|
268
275
|
const isPersonAccount = () => {
|
|
269
|
-
return user.value?.
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const isEntityAccount = () => {
|
|
273
|
-
return user.value?.type === 'entity'
|
|
276
|
+
return user.value?.hasPersonLinked === true
|
|
274
277
|
}
|
|
275
|
-
|
|
276
278
|
// Actions
|
|
277
279
|
async function logout() {
|
|
278
280
|
// Call logout API
|
|
@@ -315,6 +317,55 @@ export function useAuth() {
|
|
|
315
317
|
}
|
|
316
318
|
}
|
|
317
319
|
|
|
320
|
+
async function loadTenants(): Promise<TenantInfo[]> {
|
|
321
|
+
try {
|
|
322
|
+
const { data } = await api.getTenants()
|
|
323
|
+
tenants.value = data
|
|
324
|
+
|
|
325
|
+
// Auto-select first tenant if none selected and tenants available
|
|
326
|
+
if (currentTenant.value === null && tenants.value.length > 0) {
|
|
327
|
+
const firstActiveTenant = tenants.value.find(t => t.status === 'active')
|
|
328
|
+
if (firstActiveTenant !== undefined) {
|
|
329
|
+
setTenant(firstActiveTenant.id)
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return tenants.value
|
|
334
|
+
} catch {
|
|
335
|
+
// If 404 or other error, assume multi-tenancy not supported
|
|
336
|
+
tenants.value = []
|
|
337
|
+
return []
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function setTenant(tenantId: string | null) {
|
|
342
|
+
if (tenantId === null) {
|
|
343
|
+
currentTenant.value = null
|
|
344
|
+
api.setTenantId(null)
|
|
345
|
+
return
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const tenant = tenants.value.find(t => t.id === tenantId)
|
|
349
|
+
if (tenant !== undefined) {
|
|
350
|
+
currentTenant.value = tenant
|
|
351
|
+
api.setTenantId(tenantId)
|
|
352
|
+
} else {
|
|
353
|
+
throw new Error(`Tenant with ID ${tenantId} not found`)
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function switchTenant(tenantId: string): void {
|
|
358
|
+
setTenant(tenantId)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function getTenants() {
|
|
362
|
+
return tenants.value
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function getCurrentTenant() {
|
|
366
|
+
return currentTenant.value
|
|
367
|
+
}
|
|
368
|
+
|
|
318
369
|
async function signup(newUser: NewUser) {
|
|
319
370
|
// Check password match if password is provided
|
|
320
371
|
const hasPassword = newUser.password !== undefined && newUser.password.length > 0
|
|
@@ -478,6 +529,10 @@ export function useAuth() {
|
|
|
478
529
|
// SSO Providers (ready to use!)
|
|
479
530
|
sso,
|
|
480
531
|
|
|
532
|
+
// Multi-Tenancy State
|
|
533
|
+
tenants,
|
|
534
|
+
currentTenant,
|
|
535
|
+
|
|
481
536
|
// Getters
|
|
482
537
|
getFullName,
|
|
483
538
|
getIsLoggedIn,
|
|
@@ -485,7 +540,8 @@ export function useAuth() {
|
|
|
485
540
|
getRoles,
|
|
486
541
|
getAccountType,
|
|
487
542
|
isPersonAccount,
|
|
488
|
-
|
|
543
|
+
getTenants,
|
|
544
|
+
getCurrentTenant,
|
|
489
545
|
|
|
490
546
|
// Authentication Actions
|
|
491
547
|
login,
|
|
@@ -523,5 +579,10 @@ export function useAuth() {
|
|
|
523
579
|
getSessions,
|
|
524
580
|
revokeSession,
|
|
525
581
|
revokeAllSessions,
|
|
582
|
+
|
|
583
|
+
// Multi-Tenancy Actions
|
|
584
|
+
loadTenants,
|
|
585
|
+
setTenant,
|
|
586
|
+
switchTenant,
|
|
526
587
|
}
|
|
527
588
|
}
|