@bagelink/auth 1.7.94 → 1.7.98
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 +113 -53
- package/dist/index.mjs +113 -53
- package/dist/types.d.ts +30 -18
- package/dist/useAuth.d.ts +85 -25
- package/package.json +1 -1
- package/src/api.ts +60 -27
- package/src/types.ts +58 -49
- package/src/useAuth.ts +69 -8
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
|
}
|
|
@@ -68,14 +90,14 @@ export class AuthApi {
|
|
|
68
90
|
* Get available authentication methods
|
|
69
91
|
*/
|
|
70
92
|
async getAuthMethods(): Promise<GetMethodsResponse> {
|
|
71
|
-
return this.api.get('
|
|
93
|
+
return this.api.get('authentication/methods')
|
|
72
94
|
}
|
|
73
95
|
|
|
74
96
|
/**
|
|
75
97
|
* Register a new account
|
|
76
98
|
*/
|
|
77
99
|
async register(data: RegisterRequest): Promise<RegisterResponse> {
|
|
78
|
-
return this.api.post('
|
|
100
|
+
return this.api.post('authentication/register', {
|
|
79
101
|
...data,
|
|
80
102
|
email: data.email.toLowerCase(),
|
|
81
103
|
})
|
|
@@ -85,7 +107,7 @@ export class AuthApi {
|
|
|
85
107
|
* Login with password
|
|
86
108
|
*/
|
|
87
109
|
async login(email: string, password: string): Promise<LoginResponse> {
|
|
88
|
-
return this.api.post('
|
|
110
|
+
return this.api.post('authentication/login/password', {
|
|
89
111
|
email: email.toLowerCase(),
|
|
90
112
|
password,
|
|
91
113
|
})
|
|
@@ -95,14 +117,14 @@ export class AuthApi {
|
|
|
95
117
|
* Logout and clear session
|
|
96
118
|
*/
|
|
97
119
|
async logout(): Promise<LogoutResponse> {
|
|
98
|
-
return this.api.post('
|
|
120
|
+
return this.api.post('authentication/logout', {})
|
|
99
121
|
}
|
|
100
122
|
|
|
101
123
|
/**
|
|
102
124
|
* Refresh current session
|
|
103
125
|
*/
|
|
104
126
|
async refreshSession(): Promise<RefreshSessionResponse> {
|
|
105
|
-
return this.api.post('
|
|
127
|
+
return this.api.post('authentication/refresh', {})
|
|
106
128
|
}
|
|
107
129
|
|
|
108
130
|
// ============================================
|
|
@@ -114,7 +136,7 @@ export class AuthApi {
|
|
|
114
136
|
* Returns authorization URL to redirect user to
|
|
115
137
|
*/
|
|
116
138
|
async initiateSSO(data: SSOInitiateRequest): Promise<SSOInitiateResponse> {
|
|
117
|
-
return this.api.post(
|
|
139
|
+
return this.api.post(`authentication/sso/${data.provider}/initiate`, {
|
|
118
140
|
redirect_uri: data.redirect_uri,
|
|
119
141
|
state: data.state,
|
|
120
142
|
})
|
|
@@ -124,7 +146,7 @@ export class AuthApi {
|
|
|
124
146
|
* Complete SSO login after callback from provider
|
|
125
147
|
*/
|
|
126
148
|
async ssoCallback(data: SSOCallbackRequest): Promise<SSOCallbackResponse> {
|
|
127
|
-
return this.api.post(
|
|
149
|
+
return this.api.post(`authentication/sso/${data.provider}/callback`, {
|
|
128
150
|
code: data.code,
|
|
129
151
|
state: data.state,
|
|
130
152
|
})
|
|
@@ -134,7 +156,7 @@ export class AuthApi {
|
|
|
134
156
|
* Link an SSO provider to existing account
|
|
135
157
|
*/
|
|
136
158
|
async linkSSOProvider(data: SSOLinkRequest): Promise<SSOLinkResponse> {
|
|
137
|
-
return this.api.post(
|
|
159
|
+
return this.api.post(`authentication/sso/${data.provider}/link`, {
|
|
138
160
|
code: data.code,
|
|
139
161
|
state: data.state,
|
|
140
162
|
})
|
|
@@ -144,7 +166,7 @@ export class AuthApi {
|
|
|
144
166
|
* Unlink an SSO provider from account
|
|
145
167
|
*/
|
|
146
168
|
async unlinkSSOProvider(provider: SSOProvider): Promise<SSOUnlinkResponse> {
|
|
147
|
-
return this.api.delete(
|
|
169
|
+
return this.api.delete(`authentication/sso/${provider}/unlink`)
|
|
148
170
|
}
|
|
149
171
|
|
|
150
172
|
// ============================================
|
|
@@ -155,21 +177,21 @@ export class AuthApi {
|
|
|
155
177
|
* Get current user account info
|
|
156
178
|
*/
|
|
157
179
|
async getCurrentUser(): Promise<GetMeResponse> {
|
|
158
|
-
return this.api.get('
|
|
180
|
+
return this.api.get('authentication/me')
|
|
159
181
|
}
|
|
160
182
|
|
|
161
183
|
/**
|
|
162
184
|
* Update current user profile
|
|
163
185
|
*/
|
|
164
186
|
async updateCurrentUser(data: UpdateAccountRequest): Promise<UpdateMeResponse> {
|
|
165
|
-
return this.api.patch('
|
|
187
|
+
return this.api.patch('authentication/me', data)
|
|
166
188
|
}
|
|
167
189
|
|
|
168
190
|
/**
|
|
169
191
|
* Delete current user account
|
|
170
192
|
*/
|
|
171
193
|
async deleteCurrentUser(): Promise<DeleteMeResponse> {
|
|
172
|
-
return this.api.delete('
|
|
194
|
+
return this.api.delete('authentication/me')
|
|
173
195
|
}
|
|
174
196
|
|
|
175
197
|
// ============================================
|
|
@@ -180,7 +202,7 @@ export class AuthApi {
|
|
|
180
202
|
* Get account information by ID
|
|
181
203
|
*/
|
|
182
204
|
async getAccount(accountId: string): Promise<GetAccountResponse> {
|
|
183
|
-
return this.api.get(
|
|
205
|
+
return this.api.get(`authentication/account/${accountId}`)
|
|
184
206
|
}
|
|
185
207
|
|
|
186
208
|
/**
|
|
@@ -190,21 +212,21 @@ export class AuthApi {
|
|
|
190
212
|
accountId: string,
|
|
191
213
|
data: UpdateAccountRequest
|
|
192
214
|
): Promise<UpdateAccountResponse> {
|
|
193
|
-
return this.api.patch(
|
|
215
|
+
return this.api.patch(`authentication/account/${accountId}`, data)
|
|
194
216
|
}
|
|
195
217
|
|
|
196
218
|
/**
|
|
197
219
|
* Delete account by ID
|
|
198
220
|
*/
|
|
199
221
|
async deleteAccount(accountId: string): Promise<DeleteAccountResponse> {
|
|
200
|
-
return this.api.delete(
|
|
222
|
+
return this.api.delete(`authentication/account/${accountId}`)
|
|
201
223
|
}
|
|
202
224
|
|
|
203
225
|
/**
|
|
204
226
|
* Activate account by ID
|
|
205
227
|
*/
|
|
206
228
|
async activateAccount(accountId: string): Promise<ActivateAccountResponse> {
|
|
207
|
-
return this.api.post(
|
|
229
|
+
return this.api.post(`authentication/account/${accountId}/activate`, {})
|
|
208
230
|
}
|
|
209
231
|
|
|
210
232
|
/**
|
|
@@ -213,7 +235,7 @@ export class AuthApi {
|
|
|
213
235
|
async deactivateAccount(
|
|
214
236
|
accountId: string
|
|
215
237
|
): Promise<DeactivateAccountResponse> {
|
|
216
|
-
return this.api.post(
|
|
238
|
+
return this.api.post(`authentication/account/${accountId}/deactivate`, {})
|
|
217
239
|
}
|
|
218
240
|
|
|
219
241
|
// ============================================
|
|
@@ -224,14 +246,14 @@ export class AuthApi {
|
|
|
224
246
|
* Change password (requires current password)
|
|
225
247
|
*/
|
|
226
248
|
async changePassword(data: ChangePasswordRequest): Promise<ChangePasswordResponse> {
|
|
227
|
-
return this.api.post('
|
|
249
|
+
return this.api.post('authentication/password/change', data)
|
|
228
250
|
}
|
|
229
251
|
|
|
230
252
|
/**
|
|
231
253
|
* Initiate forgot password flow
|
|
232
254
|
*/
|
|
233
255
|
async forgotPassword(email: string): Promise<ForgotPasswordResponse> {
|
|
234
|
-
return this.api.post('
|
|
256
|
+
return this.api.post('authentication/password/forgot', {
|
|
235
257
|
email: email.toLowerCase(),
|
|
236
258
|
})
|
|
237
259
|
}
|
|
@@ -240,14 +262,14 @@ export class AuthApi {
|
|
|
240
262
|
* Verify password reset token
|
|
241
263
|
*/
|
|
242
264
|
async verifyResetToken(token: string): Promise<VerifyResetTokenResponse> {
|
|
243
|
-
return this.api.get(
|
|
265
|
+
return this.api.get(`authentication/password/verify-reset-token/${token}`)
|
|
244
266
|
}
|
|
245
267
|
|
|
246
268
|
/**
|
|
247
269
|
* Reset password with token
|
|
248
270
|
*/
|
|
249
271
|
async resetPassword(data: ResetPasswordRequest): Promise<ResetPasswordResponse> {
|
|
250
|
-
return this.api.post('
|
|
272
|
+
return this.api.post('authentication/password/reset', data)
|
|
251
273
|
}
|
|
252
274
|
|
|
253
275
|
// ============================================
|
|
@@ -261,7 +283,7 @@ export class AuthApi {
|
|
|
261
283
|
data: SendVerificationRequest = {},
|
|
262
284
|
user?: AuthenticationAccount
|
|
263
285
|
): Promise<SendVerificationResponse> {
|
|
264
|
-
return this.api.post('
|
|
286
|
+
return this.api.post('authentication/verify/send', data, {
|
|
265
287
|
params: user ? { user } : undefined,
|
|
266
288
|
})
|
|
267
289
|
}
|
|
@@ -270,7 +292,7 @@ export class AuthApi {
|
|
|
270
292
|
* Verify email with token
|
|
271
293
|
*/
|
|
272
294
|
async verifyEmail(token: string): Promise<VerifyEmailResponse> {
|
|
273
|
-
return this.api.post('
|
|
295
|
+
return this.api.post('authentication/verify/email', { token })
|
|
274
296
|
}
|
|
275
297
|
|
|
276
298
|
// ============================================
|
|
@@ -281,27 +303,38 @@ export class AuthApi {
|
|
|
281
303
|
* Get sessions for an account
|
|
282
304
|
*/
|
|
283
305
|
async getSessions(accountId: string): Promise<GetSessionsResponse> {
|
|
284
|
-
return this.api.get(
|
|
306
|
+
return this.api.get(`authentication/sessions/${accountId}`)
|
|
285
307
|
}
|
|
286
308
|
|
|
287
309
|
/**
|
|
288
310
|
* Revoke a specific session
|
|
289
311
|
*/
|
|
290
312
|
async revokeSession(sessionToken: string): Promise<DeleteSessionResponse> {
|
|
291
|
-
return this.api.delete(
|
|
313
|
+
return this.api.delete(`authentication/sessions/${sessionToken}`)
|
|
292
314
|
}
|
|
293
315
|
|
|
294
316
|
/**
|
|
295
317
|
* Revoke all sessions for an account
|
|
296
318
|
*/
|
|
297
319
|
async revokeAllSessions(accountId: string): Promise<DeleteAllSessionsResponse> {
|
|
298
|
-
return this.api.delete(
|
|
320
|
+
return this.api.delete(`authentication/sessions/account/${accountId}`)
|
|
299
321
|
}
|
|
300
322
|
|
|
301
323
|
/**
|
|
302
324
|
* Cleanup expired sessions (admin)
|
|
303
325
|
*/
|
|
304
326
|
async cleanupSessions(): Promise<CleanupSessionsResponse> {
|
|
305
|
-
return this.api.post('
|
|
327
|
+
return this.api.post('authentication/cleanup-sessions', {})
|
|
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')
|
|
306
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
|
}
|