@bagelink/auth 1.12.3 → 1.12.8
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 +54 -1
- package/dist/index.cjs +154 -1181
- package/dist/index.d.ts +0 -9
- package/dist/index.mjs +155 -1182
- package/dist/routes.d.ts +0 -36
- package/dist/types.d.ts +68 -1
- package/dist/useAuth.d.ts +17 -1
- package/package.json +1 -1
- package/src/api.ts +118 -0
- package/src/index.ts +0 -13
- package/src/routes.ts +0 -96
- package/src/types.ts +81 -1
- package/src/useAuth.ts +103 -0
- package/dist/Callback-BHqVaZZm.cjs +0 -4
- package/dist/Callback-C-XghN_z.js +0 -4
- package/dist/ForgotPasswordPage-BV9tyhHl.cjs +0 -4
- package/dist/ForgotPasswordPage-DvttMGb0.js +0 -4
- package/dist/LoginPage-hv1wc54S.cjs +0 -4
- package/dist/LoginPage-klj1NV4J.js +0 -4
- package/dist/ResetPasswordPage-COPrJmW8.cjs +0 -4
- package/dist/ResetPasswordPage-nvQ4uupb.js +0 -4
- package/dist/SignupPage-m36w9PLJ.cjs +0 -4
- package/dist/SignupPage-oUFYApYW.js +0 -4
- package/dist/components/auth/ForgotPasswordForm.vue.d.ts +0 -23
- package/dist/components/auth/LoginForm.vue.d.ts +0 -58
- package/dist/components/auth/ResetPasswordForm.vue.d.ts +0 -28
- package/dist/components/auth/SignupForm.vue.d.ts +0 -34
- package/dist/components/index.d.ts +0 -4
- package/dist/pages/Callback.vue.d.ts +0 -2
- package/dist/pages/ForgotPasswordPage.vue.d.ts +0 -13
- package/dist/pages/LoginPage.vue.d.ts +0 -38
- package/dist/pages/ResetPasswordPage.vue.d.ts +0 -13
- package/dist/pages/SignupPage.vue.d.ts +0 -16
- package/src/components/auth/ForgotPasswordForm.vue +0 -97
- package/src/components/auth/LoginForm.vue +0 -258
- package/src/components/auth/ResetPasswordForm.vue +0 -156
- package/src/components/auth/SignupForm.vue +0 -231
- package/src/components/index.ts +0 -5
- package/src/pages/Callback.vue +0 -196
- package/src/pages/ForgotPasswordPage.vue +0 -42
- package/src/pages/LoginPage.vue +0 -68
- package/src/pages/ResetPasswordPage.vue +0 -47
- package/src/pages/SignupPage.vue +0 -46
package/dist/routes.d.ts
CHANGED
|
@@ -1,39 +1,3 @@
|
|
|
1
|
-
import { RouteRecordRaw } from 'vue-router';
|
|
2
|
-
export interface AuthRouteConfig {
|
|
3
|
-
/** Base path for auth routes. Defaults to '/' */
|
|
4
|
-
basePath?: string;
|
|
5
|
-
/** Route names prefix. Defaults to '' */
|
|
6
|
-
namePrefix?: string;
|
|
7
|
-
/** Custom route names */
|
|
8
|
-
routeNames?: {
|
|
9
|
-
login?: string;
|
|
10
|
-
signup?: string;
|
|
11
|
-
forgotPassword?: string;
|
|
12
|
-
resetPassword?: string;
|
|
13
|
-
callback?: string;
|
|
14
|
-
};
|
|
15
|
-
/** Redirect path after successful auth. Defaults to '/' */
|
|
16
|
-
redirectTo?: string;
|
|
17
|
-
/** Custom layout wrapper component */
|
|
18
|
-
layout?: any;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Creates auth routes for Vue Router
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* ```ts
|
|
25
|
-
* import { createAuthRoutes } from '@bagelink/auth'
|
|
26
|
-
*
|
|
27
|
-
* const routes = [
|
|
28
|
-
* ...createAuthRoutes({
|
|
29
|
-
* basePath: '/auth',
|
|
30
|
-
* redirectTo: '/dashboard'
|
|
31
|
-
* }),
|
|
32
|
-
* // ... other routes
|
|
33
|
-
* ]
|
|
34
|
-
* ```
|
|
35
|
-
*/
|
|
36
|
-
export declare function createAuthRoutes(config?: AuthRouteConfig): RouteRecordRaw[];
|
|
37
1
|
/**
|
|
38
2
|
* Creates a navigation guard to redirect authenticated users away from auth pages
|
|
39
3
|
*
|
package/dist/types.d.ts
CHANGED
|
@@ -36,6 +36,74 @@ export interface TenantInfo {
|
|
|
36
36
|
created_at: string;
|
|
37
37
|
updated_at: string;
|
|
38
38
|
}
|
|
39
|
+
export interface CreateTenantRequest {
|
|
40
|
+
name: string;
|
|
41
|
+
slug?: string;
|
|
42
|
+
settings?: Record<string, any>;
|
|
43
|
+
}
|
|
44
|
+
export interface UpdateTenantRequest {
|
|
45
|
+
name?: string;
|
|
46
|
+
slug?: string;
|
|
47
|
+
settings?: Record<string, any>;
|
|
48
|
+
status?: TenantStatus;
|
|
49
|
+
}
|
|
50
|
+
export type MemberStatus = 'pending' | 'active' | 'inactive' | 'suspended';
|
|
51
|
+
export interface TenantMember {
|
|
52
|
+
identity_id: string;
|
|
53
|
+
tenant_id: string;
|
|
54
|
+
roles: string[];
|
|
55
|
+
status: MemberStatus;
|
|
56
|
+
metadata: Record<string, any> | null;
|
|
57
|
+
created_at: string;
|
|
58
|
+
updated_at: string;
|
|
59
|
+
}
|
|
60
|
+
export interface AddMemberRequest {
|
|
61
|
+
identity_id: string;
|
|
62
|
+
roles?: string[];
|
|
63
|
+
status?: MemberStatus;
|
|
64
|
+
}
|
|
65
|
+
export interface UpdateMemberRequest {
|
|
66
|
+
roles?: string[];
|
|
67
|
+
status?: MemberStatus;
|
|
68
|
+
metadata?: Record<string, any>;
|
|
69
|
+
}
|
|
70
|
+
export type InvitationStatus = 'pending' | 'accepted' | 'expired' | 'revoked';
|
|
71
|
+
export interface TenantInvitation {
|
|
72
|
+
id: string;
|
|
73
|
+
tenant_id: string;
|
|
74
|
+
email: string;
|
|
75
|
+
role: string;
|
|
76
|
+
status: InvitationStatus;
|
|
77
|
+
created_at: string;
|
|
78
|
+
expires_at: string | null;
|
|
79
|
+
}
|
|
80
|
+
export interface CreateInvitationRequest {
|
|
81
|
+
email: string;
|
|
82
|
+
role?: string;
|
|
83
|
+
message?: string;
|
|
84
|
+
}
|
|
85
|
+
export interface AcceptInvitationRequest {
|
|
86
|
+
first_name: string;
|
|
87
|
+
last_name: string;
|
|
88
|
+
password: string;
|
|
89
|
+
}
|
|
90
|
+
export type GetTenantsResponse = AxiosResponse<TenantInfo[]>;
|
|
91
|
+
export type GetTenantResponse = AxiosResponse<TenantInfo>;
|
|
92
|
+
export type CreateTenantResponse = AxiosResponse<TenantInfo>;
|
|
93
|
+
export type UpdateTenantResponse = AxiosResponse<TenantInfo>;
|
|
94
|
+
export type DeleteTenantResponse = AxiosResponse<MessageResponse>;
|
|
95
|
+
export type GetTenantMembersResponse = AxiosResponse<TenantMember[]>;
|
|
96
|
+
export type AddTenantMemberResponse = AxiosResponse<TenantMember>;
|
|
97
|
+
export type UpdateTenantMemberResponse = AxiosResponse<TenantMember>;
|
|
98
|
+
export type DeleteTenantMemberResponse = AxiosResponse<MessageResponse>;
|
|
99
|
+
export type GetTenantRolesResponse = AxiosResponse<string[]>;
|
|
100
|
+
export type CreateInvitationResponse = AxiosResponse<TenantInvitation>;
|
|
101
|
+
export type GetInvitationResponse = AxiosResponse<{
|
|
102
|
+
email: string;
|
|
103
|
+
first_name?: string | null;
|
|
104
|
+
last_name?: string | null;
|
|
105
|
+
}>;
|
|
106
|
+
export type AcceptInvitationResponse = AxiosResponse<MessageResponse>;
|
|
39
107
|
export type AuthenticationMethodType = 'password' | 'email_token' | 'sso' | 'otp';
|
|
40
108
|
export type SSOProvider = 'google' | 'microsoft' | 'github' | 'okta' | 'apple' | 'facebook' | 'custom';
|
|
41
109
|
export interface AuthenticationAccount {
|
|
@@ -298,7 +366,6 @@ export type InitiateSSOResponse = AxiosResponse<SSOInitiateResponse>;
|
|
|
298
366
|
export type CallbackSSOResponse = AxiosResponse<SSOCallbackResponse>;
|
|
299
367
|
export type LinkSSOResponse = AxiosResponse<SSOLinkResponse>;
|
|
300
368
|
export type UnlinkSSOResponse = AxiosResponse<SSOUnlinkResponse>;
|
|
301
|
-
export type GetTenantsResponse = AxiosResponse<TenantInfo[]>;
|
|
302
369
|
export type GetAuthStatusResponse = AxiosResponse<AuthStatusResponse>;
|
|
303
370
|
export type SendEmailTokenResponse = AxiosResponse<AuthenticationResponse>;
|
|
304
371
|
export type VerifyEmailTokenResponse = AxiosResponse<AuthenticationResponse>;
|
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, TenantInfo, AuthState } from './types';
|
|
2
|
+
import { AccountInfo, User, NewUser, UpdatePasswordForm, UpdateAccountRequest, AuthEventMap, SSOProvider, SSOInitiateRequest, SSOCallbackRequest, SSOLinkRequest, TenantInfo, CreateTenantRequest, UpdateTenantRequest, AddMemberRequest, UpdateMemberRequest, CreateInvitationRequest, AcceptInvitationRequest, AuthState } from './types';
|
|
3
3
|
import { RedirectConfig, NormalizedRedirectConfig } from './types/redirect';
|
|
4
4
|
interface InitParams {
|
|
5
5
|
baseURL: string;
|
|
@@ -194,5 +194,21 @@ export declare function useAuth(): {
|
|
|
194
194
|
loadTenants: () => Promise<TenantInfo[]>;
|
|
195
195
|
setTenant: (tenantId: string | null) => void;
|
|
196
196
|
switchTenant: (tenantId: string) => void;
|
|
197
|
+
createTenant: (data: CreateTenantRequest) => Promise<TenantInfo>;
|
|
198
|
+
updateTenant: (tenantId: string, data: UpdateTenantRequest) => Promise<TenantInfo>;
|
|
199
|
+
deleteTenant: (tenantId: string) => Promise<void>;
|
|
200
|
+
getTenantMembers: (tenantId: string, status?: string) => Promise<import('./types').TenantMember[]>;
|
|
201
|
+
addTenantMember: (tenantId: string, member: AddMemberRequest) => Promise<import('./types').TenantMember>;
|
|
202
|
+
updateTenantMember: (tenantId: string, identityId: string, updates: UpdateMemberRequest) => Promise<import('./types').TenantMember>;
|
|
203
|
+
activateTenantMember: (tenantId: string, identityId: string) => Promise<import('./types').TenantMember>;
|
|
204
|
+
removeTenantMember: (tenantId: string, identityId: string) => Promise<void>;
|
|
205
|
+
getTenantRoles: () => Promise<string[]>;
|
|
206
|
+
createInvitation: (data: CreateInvitationRequest) => Promise<import('./types').TenantInvitation>;
|
|
207
|
+
getInvitation: (token: string) => Promise<{
|
|
208
|
+
email: string;
|
|
209
|
+
first_name?: string | null;
|
|
210
|
+
last_name?: string | null;
|
|
211
|
+
}>;
|
|
212
|
+
acceptInvitation: (token: string, data?: AcceptInvitationRequest) => Promise<void>;
|
|
197
213
|
};
|
|
198
214
|
export {};
|
package/package.json
CHANGED
package/src/api.ts
CHANGED
|
@@ -47,6 +47,24 @@ import type {
|
|
|
47
47
|
LinkSSOResponse,
|
|
48
48
|
UnlinkSSOResponse,
|
|
49
49
|
GetTenantsResponse,
|
|
50
|
+
GetTenantResponse,
|
|
51
|
+
CreateTenantResponse,
|
|
52
|
+
UpdateTenantResponse,
|
|
53
|
+
DeleteTenantResponse,
|
|
54
|
+
GetTenantMembersResponse,
|
|
55
|
+
AddTenantMemberResponse,
|
|
56
|
+
UpdateTenantMemberResponse,
|
|
57
|
+
DeleteTenantMemberResponse,
|
|
58
|
+
GetTenantRolesResponse,
|
|
59
|
+
CreateInvitationResponse,
|
|
60
|
+
GetInvitationResponse,
|
|
61
|
+
AcceptInvitationResponse,
|
|
62
|
+
CreateTenantRequest,
|
|
63
|
+
UpdateTenantRequest,
|
|
64
|
+
AddMemberRequest,
|
|
65
|
+
UpdateMemberRequest,
|
|
66
|
+
CreateInvitationRequest,
|
|
67
|
+
AcceptInvitationRequest,
|
|
50
68
|
} from './types'
|
|
51
69
|
import { createAxiosInstance } from './utils'
|
|
52
70
|
|
|
@@ -336,4 +354,104 @@ export class AuthApi {
|
|
|
336
354
|
async getTenants(): Promise<GetTenantsResponse> {
|
|
337
355
|
return this.api.get('tenants')
|
|
338
356
|
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Get a single tenant by ID or slug
|
|
360
|
+
*/
|
|
361
|
+
async getTenant(idOrSlug: string): Promise<GetTenantResponse> {
|
|
362
|
+
return this.api.get(`tenants/${idOrSlug}`)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Create a new tenant (caller is auto-added as admin)
|
|
367
|
+
*/
|
|
368
|
+
async createTenant(data: CreateTenantRequest): Promise<CreateTenantResponse> {
|
|
369
|
+
return this.api.post('tenants/', data)
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Update a tenant
|
|
374
|
+
*/
|
|
375
|
+
async updateTenant(tenantId: string, data: UpdateTenantRequest): Promise<UpdateTenantResponse> {
|
|
376
|
+
return this.api.put(`tenants/${tenantId}`, data)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Delete a tenant
|
|
381
|
+
*/
|
|
382
|
+
async deleteTenant(tenantId: string): Promise<DeleteTenantResponse> {
|
|
383
|
+
return this.api.delete(`tenants/${tenantId}`)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// ============================================
|
|
387
|
+
// Tenant Member Methods
|
|
388
|
+
// ============================================
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* List members of a tenant
|
|
392
|
+
*/
|
|
393
|
+
async getTenantMembers(tenantId: string, status?: string): Promise<GetTenantMembersResponse> {
|
|
394
|
+
return this.api.get(`tenants/${tenantId}/members`, { params: status ? { status } : undefined })
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Add a member to a tenant by identity_id
|
|
399
|
+
*/
|
|
400
|
+
async addTenantMember(tenantId: string, data: AddMemberRequest): Promise<AddTenantMemberResponse> {
|
|
401
|
+
return this.api.post(`tenants/${tenantId}/members`, data)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Update a tenant member's roles/status/metadata
|
|
406
|
+
*/
|
|
407
|
+
async updateTenantMember(tenantId: string, identityId: string, data: UpdateMemberRequest): Promise<UpdateTenantMemberResponse> {
|
|
408
|
+
return this.api.put(`tenants/${tenantId}/members/${identityId}`, data)
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Activate a pending membership
|
|
413
|
+
*/
|
|
414
|
+
async activateTenantMember(tenantId: string, identityId: string): Promise<AddTenantMemberResponse> {
|
|
415
|
+
return this.api.post(`tenants/${tenantId}/members/${identityId}/activate`, {})
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Remove a member from a tenant
|
|
420
|
+
*/
|
|
421
|
+
async removeTenantMember(tenantId: string, identityId: string): Promise<DeleteTenantMemberResponse> {
|
|
422
|
+
return this.api.delete(`tenants/${tenantId}/members/${identityId}`)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* List available roles
|
|
427
|
+
*/
|
|
428
|
+
async getTenantRoles(): Promise<GetTenantRolesResponse> {
|
|
429
|
+
return this.api.get('tenants/roles')
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ============================================
|
|
433
|
+
// Invitation Methods
|
|
434
|
+
// ============================================
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Create an invitation for a new user to join a tenant
|
|
438
|
+
* Requires X-Tenant-ID header (set via setTenantId)
|
|
439
|
+
*/
|
|
440
|
+
async createInvitation(data: CreateInvitationRequest): Promise<CreateInvitationResponse> {
|
|
441
|
+
return this.api.post('invitations/', data)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Get invitation metadata by token (no auth required)
|
|
446
|
+
*/
|
|
447
|
+
async getInvitation(token: string): Promise<GetInvitationResponse> {
|
|
448
|
+
return this.api.get(`invitations/${token}`)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Accept an invitation — creates TenantMembership for the authenticated user
|
|
453
|
+
*/
|
|
454
|
+
async acceptInvitation(token: string, data?: AcceptInvitationRequest): Promise<AcceptInvitationResponse> {
|
|
455
|
+
return this.api.post(`invitations/${token}/accept`, data ?? {})
|
|
456
|
+
}
|
|
339
457
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,19 +1,6 @@
|
|
|
1
1
|
// Core auth functionality
|
|
2
2
|
export * from './api'
|
|
3
|
-
// Components
|
|
4
|
-
export { default as ForgotPasswordForm } from './components/auth/ForgotPasswordForm.vue'
|
|
5
|
-
export { default as LoginForm } from './components/auth/LoginForm.vue'
|
|
6
|
-
export { default as ResetPasswordForm } from './components/auth/ResetPasswordForm.vue'
|
|
7
|
-
|
|
8
|
-
export { default as SignupForm } from './components/auth/SignupForm.vue'
|
|
9
3
|
export * from './constants'
|
|
10
|
-
// Page components
|
|
11
|
-
export { default as Callback } from './pages/Callback.vue'
|
|
12
|
-
export { default as ForgotPasswordPage } from './pages/ForgotPasswordPage.vue'
|
|
13
|
-
|
|
14
|
-
export { default as LoginPage } from './pages/LoginPage.vue'
|
|
15
|
-
export { default as ResetPasswordPage } from './pages/ResetPasswordPage.vue'
|
|
16
|
-
export { default as SignupPage } from './pages/SignupPage.vue'
|
|
17
4
|
|
|
18
5
|
// Redirect utilities
|
|
19
6
|
export * from './redirect'
|
package/src/routes.ts
CHANGED
|
@@ -1,99 +1,3 @@
|
|
|
1
|
-
import type { RouteRecordRaw } from 'vue-router'
|
|
2
|
-
|
|
3
|
-
export interface AuthRouteConfig {
|
|
4
|
-
/** Base path for auth routes. Defaults to '/' */
|
|
5
|
-
basePath?: string
|
|
6
|
-
/** Route names prefix. Defaults to '' */
|
|
7
|
-
namePrefix?: string
|
|
8
|
-
/** Custom route names */
|
|
9
|
-
routeNames?: {
|
|
10
|
-
login?: string
|
|
11
|
-
signup?: string
|
|
12
|
-
forgotPassword?: string
|
|
13
|
-
resetPassword?: string
|
|
14
|
-
callback?: string
|
|
15
|
-
}
|
|
16
|
-
/** Redirect path after successful auth. Defaults to '/' */
|
|
17
|
-
redirectTo?: string
|
|
18
|
-
/** Custom layout wrapper component */
|
|
19
|
-
layout?: any
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Creates auth routes for Vue Router
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* import { createAuthRoutes } from '@bagelink/auth'
|
|
28
|
-
*
|
|
29
|
-
* const routes = [
|
|
30
|
-
* ...createAuthRoutes({
|
|
31
|
-
* basePath: '/auth',
|
|
32
|
-
* redirectTo: '/dashboard'
|
|
33
|
-
* }),
|
|
34
|
-
* // ... other routes
|
|
35
|
-
* ]
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
export function createAuthRoutes(config: AuthRouteConfig = {}): RouteRecordRaw[] {
|
|
39
|
-
const {
|
|
40
|
-
basePath = '',
|
|
41
|
-
namePrefix = '',
|
|
42
|
-
routeNames = {},
|
|
43
|
-
layout
|
|
44
|
-
} = config
|
|
45
|
-
|
|
46
|
-
const createRouteName = (name: string) => namePrefix ? `${namePrefix}${name}` : name
|
|
47
|
-
|
|
48
|
-
// Lazy load components
|
|
49
|
-
const routes: RouteRecordRaw[] = [
|
|
50
|
-
{
|
|
51
|
-
path: `${basePath}/login`,
|
|
52
|
-
name: routeNames.login || createRouteName('Login'),
|
|
53
|
-
component: () => import('./pages/LoginPage.vue'),
|
|
54
|
-
meta: { requiresAuth: false }
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
path: `${basePath}/signup`,
|
|
58
|
-
name: routeNames.signup || createRouteName('Signup'),
|
|
59
|
-
component: () => import('./pages/SignupPage.vue'),
|
|
60
|
-
meta: { requiresAuth: false }
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
path: `${basePath}/forgot-password`,
|
|
64
|
-
name: routeNames.forgotPassword || createRouteName('ForgotPassword'),
|
|
65
|
-
component: () => import('./pages/ForgotPasswordPage.vue'),
|
|
66
|
-
meta: { requiresAuth: false }
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
path: `${basePath}/reset-password`,
|
|
70
|
-
name: routeNames.resetPassword || createRouteName('ResetPassword'),
|
|
71
|
-
component: () => import('./pages/ResetPasswordPage.vue'),
|
|
72
|
-
meta: { requiresAuth: false }
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
path: `${basePath}/callback`,
|
|
76
|
-
name: routeNames.callback || createRouteName('AuthCallback'),
|
|
77
|
-
component: () => import('./pages/Callback.vue'),
|
|
78
|
-
meta: { requiresAuth: false }
|
|
79
|
-
}
|
|
80
|
-
]
|
|
81
|
-
|
|
82
|
-
// Wrap in layout if provided
|
|
83
|
-
if (layout) {
|
|
84
|
-
return [{
|
|
85
|
-
path: basePath,
|
|
86
|
-
component: layout,
|
|
87
|
-
children: routes.map(route => ({
|
|
88
|
-
...route,
|
|
89
|
-
path: route.path.replace(basePath, '')
|
|
90
|
-
}))
|
|
91
|
-
}]
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return routes
|
|
95
|
-
}
|
|
96
|
-
|
|
97
1
|
/**
|
|
98
2
|
* Creates a navigation guard to redirect authenticated users away from auth pages
|
|
99
3
|
*
|
package/src/types.ts
CHANGED
|
@@ -56,6 +56,87 @@ export interface TenantInfo {
|
|
|
56
56
|
updated_at: string
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
export interface CreateTenantRequest {
|
|
60
|
+
name: string
|
|
61
|
+
slug?: string
|
|
62
|
+
settings?: Record<string, any>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface UpdateTenantRequest {
|
|
66
|
+
name?: string
|
|
67
|
+
slug?: string
|
|
68
|
+
settings?: Record<string, any>
|
|
69
|
+
status?: TenantStatus
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── Tenant Membership ────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
export type MemberStatus = 'pending' | 'active' | 'inactive' | 'suspended'
|
|
75
|
+
|
|
76
|
+
export interface TenantMember {
|
|
77
|
+
identity_id: string
|
|
78
|
+
tenant_id: string
|
|
79
|
+
roles: string[]
|
|
80
|
+
status: MemberStatus
|
|
81
|
+
metadata: Record<string, any> | null
|
|
82
|
+
created_at: string
|
|
83
|
+
updated_at: string
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface AddMemberRequest {
|
|
87
|
+
identity_id: string
|
|
88
|
+
roles?: string[]
|
|
89
|
+
status?: MemberStatus
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface UpdateMemberRequest {
|
|
93
|
+
roles?: string[]
|
|
94
|
+
status?: MemberStatus
|
|
95
|
+
metadata?: Record<string, any>
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ─── Invitations ──────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
export type InvitationStatus = 'pending' | 'accepted' | 'expired' | 'revoked'
|
|
101
|
+
|
|
102
|
+
export interface TenantInvitation {
|
|
103
|
+
id: string
|
|
104
|
+
tenant_id: string
|
|
105
|
+
email: string
|
|
106
|
+
role: string
|
|
107
|
+
status: InvitationStatus
|
|
108
|
+
created_at: string
|
|
109
|
+
expires_at: string | null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface CreateInvitationRequest {
|
|
113
|
+
email: string
|
|
114
|
+
role?: string
|
|
115
|
+
message?: string
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface AcceptInvitationRequest {
|
|
119
|
+
first_name: string
|
|
120
|
+
last_name: string
|
|
121
|
+
password: string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ─── Tenant API response types ────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
export type GetTenantsResponse = AxiosResponse<TenantInfo[]>
|
|
127
|
+
export type GetTenantResponse = AxiosResponse<TenantInfo>
|
|
128
|
+
export type CreateTenantResponse = AxiosResponse<TenantInfo>
|
|
129
|
+
export type UpdateTenantResponse = AxiosResponse<TenantInfo>
|
|
130
|
+
export type DeleteTenantResponse = AxiosResponse<MessageResponse>
|
|
131
|
+
export type GetTenantMembersResponse = AxiosResponse<TenantMember[]>
|
|
132
|
+
export type AddTenantMemberResponse = AxiosResponse<TenantMember>
|
|
133
|
+
export type UpdateTenantMemberResponse = AxiosResponse<TenantMember>
|
|
134
|
+
export type DeleteTenantMemberResponse = AxiosResponse<MessageResponse>
|
|
135
|
+
export type GetTenantRolesResponse = AxiosResponse<string[]>
|
|
136
|
+
export type CreateInvitationResponse = AxiosResponse<TenantInvitation>
|
|
137
|
+
export type GetInvitationResponse = AxiosResponse<{ email: string, first_name?: string | null, last_name?: string | null }>
|
|
138
|
+
export type AcceptInvitationResponse = AxiosResponse<MessageResponse>
|
|
139
|
+
|
|
59
140
|
export type AuthenticationMethodType
|
|
60
141
|
= | 'password'
|
|
61
142
|
| 'email_token'
|
|
@@ -363,7 +444,6 @@ export type InitiateSSOResponse = AxiosResponse<SSOInitiateResponse>
|
|
|
363
444
|
export type CallbackSSOResponse = AxiosResponse<SSOCallbackResponse>
|
|
364
445
|
export type LinkSSOResponse = AxiosResponse<SSOLinkResponse>
|
|
365
446
|
export type UnlinkSSOResponse = AxiosResponse<SSOUnlinkResponse>
|
|
366
|
-
export type GetTenantsResponse = AxiosResponse<TenantInfo[]>
|
|
367
447
|
export type GetAuthStatusResponse = AxiosResponse<AuthStatusResponse>
|
|
368
448
|
export type SendEmailTokenResponse = AxiosResponse<AuthenticationResponse>
|
|
369
449
|
export type VerifyEmailTokenResponse = AxiosResponse<AuthenticationResponse>
|
package/src/useAuth.ts
CHANGED
|
@@ -11,6 +11,12 @@ import type {
|
|
|
11
11
|
SSOCallbackRequest,
|
|
12
12
|
SSOLinkRequest,
|
|
13
13
|
TenantInfo,
|
|
14
|
+
CreateTenantRequest,
|
|
15
|
+
UpdateTenantRequest,
|
|
16
|
+
AddMemberRequest,
|
|
17
|
+
UpdateMemberRequest,
|
|
18
|
+
CreateInvitationRequest,
|
|
19
|
+
AcceptInvitationRequest,
|
|
14
20
|
} from './types'
|
|
15
21
|
import type { RedirectConfig, NormalizedRedirectConfig } from './types/redirect'
|
|
16
22
|
import { ref, computed } from 'vue'
|
|
@@ -382,6 +388,85 @@ export function useAuth() {
|
|
|
382
388
|
return currentTenant.value
|
|
383
389
|
}
|
|
384
390
|
|
|
391
|
+
// ============================================
|
|
392
|
+
// Tenant CRUD
|
|
393
|
+
// ============================================
|
|
394
|
+
|
|
395
|
+
async function createTenant(data: CreateTenantRequest) {
|
|
396
|
+
const { data: tenant } = await api.createTenant(data)
|
|
397
|
+
await loadTenants()
|
|
398
|
+
return tenant
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async function updateTenant(tenantId: string, data: UpdateTenantRequest) {
|
|
402
|
+
const { data: tenant } = await api.updateTenant(tenantId, data)
|
|
403
|
+
// Refresh list so currentTenant stays in sync
|
|
404
|
+
await loadTenants()
|
|
405
|
+
return tenant
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
async function deleteTenant(tenantId: string) {
|
|
409
|
+
await api.deleteTenant(tenantId)
|
|
410
|
+
if (currentTenant.value?.id === tenantId) {
|
|
411
|
+
currentTenant.value = null
|
|
412
|
+
api.setTenantId(null)
|
|
413
|
+
}
|
|
414
|
+
await loadTenants()
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ============================================
|
|
418
|
+
// Tenant Members
|
|
419
|
+
// ============================================
|
|
420
|
+
|
|
421
|
+
async function getTenantMembers(tenantId: string, status?: string) {
|
|
422
|
+
const { data } = await api.getTenantMembers(tenantId, status)
|
|
423
|
+
return data
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function addTenantMember(tenantId: string, member: AddMemberRequest) {
|
|
427
|
+
const { data } = await api.addTenantMember(tenantId, member)
|
|
428
|
+
return data
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async function updateTenantMember(tenantId: string, identityId: string, updates: UpdateMemberRequest) {
|
|
432
|
+
const { data } = await api.updateTenantMember(tenantId, identityId, updates)
|
|
433
|
+
return data
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async function activateTenantMember(tenantId: string, identityId: string) {
|
|
437
|
+
const { data } = await api.activateTenantMember(tenantId, identityId)
|
|
438
|
+
return data
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
async function removeTenantMember(tenantId: string, identityId: string) {
|
|
442
|
+
await api.removeTenantMember(tenantId, identityId)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async function getTenantRoles() {
|
|
446
|
+
const { data } = await api.getTenantRoles()
|
|
447
|
+
return data
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ============================================
|
|
451
|
+
// Invitations
|
|
452
|
+
// ============================================
|
|
453
|
+
|
|
454
|
+
async function createInvitation(data: CreateInvitationRequest) {
|
|
455
|
+
const { data: invitation } = await api.createInvitation(data)
|
|
456
|
+
return invitation
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async function getInvitation(token: string) {
|
|
460
|
+
const { data } = await api.getInvitation(token)
|
|
461
|
+
return data
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function acceptInvitation(token: string, data?: AcceptInvitationRequest) {
|
|
465
|
+
await api.acceptInvitation(token, data)
|
|
466
|
+
// Re-check auth so tenants are reloaded after membership is created
|
|
467
|
+
await checkAuth()
|
|
468
|
+
}
|
|
469
|
+
|
|
385
470
|
async function signup(newUser: NewUser) {
|
|
386
471
|
// Check password match if password is provided
|
|
387
472
|
const hasPassword = newUser.password !== undefined && newUser.password.length > 0
|
|
@@ -572,5 +657,23 @@ export function useAuth() {
|
|
|
572
657
|
loadTenants,
|
|
573
658
|
setTenant,
|
|
574
659
|
switchTenant,
|
|
660
|
+
|
|
661
|
+
// Tenant CRUD (requires tenancy: true)
|
|
662
|
+
createTenant,
|
|
663
|
+
updateTenant,
|
|
664
|
+
deleteTenant,
|
|
665
|
+
|
|
666
|
+
// Tenant Members
|
|
667
|
+
getTenantMembers,
|
|
668
|
+
addTenantMember,
|
|
669
|
+
updateTenantMember,
|
|
670
|
+
activateTenantMember,
|
|
671
|
+
removeTenantMember,
|
|
672
|
+
getTenantRoles,
|
|
673
|
+
|
|
674
|
+
// Invitations
|
|
675
|
+
createInvitation,
|
|
676
|
+
getInvitation,
|
|
677
|
+
acceptInvitation,
|
|
575
678
|
}
|
|
576
679
|
}
|