@meistrari/auth-nuxt 3.10.1 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meistrari/auth-nuxt",
3
3
  "configKey": "telaAuth",
4
- "version": "3.10.1",
4
+ "version": "3.12.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -115,6 +115,11 @@ const module$1 = defineNuxtModule({
115
115
  as: "useTelaOrganization",
116
116
  from: resolver.resolve("runtime/composables/organization")
117
117
  });
118
+ addImports({
119
+ name: "useTelaAdmin",
120
+ as: "useTelaAdmin",
121
+ from: resolver.resolve("runtime/composables/admin")
122
+ });
118
123
  addPlugin(resolver.resolve("./runtime/plugins/handshake"));
119
124
  }
120
125
  });
@@ -0,0 +1,55 @@
1
+ import type { AdminOrganization, CreateAdminOrganizationOptions, GetAdminOrganizationOptions, ListAdminApplicationsOptions, ListAdminApplicationsResult, ListAdminOrganizationMembersOptions, ListAdminOrganizationMembersResult, ListAdminOrganizationsOptions, ListAdminOrganizationsResult, ListAdminUsersOptions, ListAdminUsersResult, UpdateAdminOrganizationOptions } from '@meistrari/auth-core';
2
+ export interface UseTelaAdminReturn {
3
+ /**
4
+ * Lists users from the Better Auth `user` table as a global admin.
5
+ * @param options - Pagination, search, filter, and ordering options
6
+ * @returns The paginated user listing
7
+ */
8
+ listUsers: (options?: ListAdminUsersOptions) => Promise<ListAdminUsersResult>;
9
+ /**
10
+ * Lists applications as a global admin, optionally filtered by organization.
11
+ * @param options - Pagination, ordering, organization filter, and search options
12
+ * @returns The application listing with entitlement details
13
+ */
14
+ listApplications: (options?: ListAdminApplicationsOptions) => Promise<ListAdminApplicationsResult>;
15
+ /**
16
+ * Lists all organizations as a global admin, optionally with member and
17
+ * application previews and totals when a preview size is requested.
18
+ * @param options - Pagination, ordering, search, and preview options
19
+ * @returns The paginated organization listing
20
+ */
21
+ listOrganizations: (options?: ListAdminOrganizationsOptions) => Promise<ListAdminOrganizationsResult>;
22
+ /**
23
+ * Fetches a single organization as a global admin, optionally with adjacent
24
+ * IDs in the listing defined by the ordering options.
25
+ * @param options - Organization ID and listing adjacency options
26
+ * @returns The organization
27
+ */
28
+ getOrganization: (options: GetAdminOrganizationOptions) => Promise<AdminOrganization>;
29
+ /**
30
+ * Lists active members for a single organization as a global admin.
31
+ * @param options - Organization ID and pagination options
32
+ * @returns The paginated organization member listing
33
+ */
34
+ listOrganizationMembers: (options: ListAdminOrganizationMembersOptions) => Promise<ListAdminOrganizationMembersResult>;
35
+ /**
36
+ * Creates an organization as a global admin without joining it.
37
+ * @param options - Organization name and optional slug, logo, metadata, and settings
38
+ * @returns The created organization
39
+ */
40
+ createOrganization: (options: CreateAdminOrganizationOptions) => Promise<AdminOrganization>;
41
+ /**
42
+ * Updates an organization's profile and settings as a global admin.
43
+ * @param options - Organization ID and the fields to update
44
+ * @returns The updated organization
45
+ */
46
+ updateOrganization: (options: UpdateAdminOrganizationOptions) => Promise<AdminOrganization>;
47
+ }
48
+ /**
49
+ * Composable for global admin operations.
50
+ *
51
+ * All operations require the authenticated user to hold the global admin role.
52
+ *
53
+ * @returns An object containing admin management functions.
54
+ */
55
+ export declare function useTelaAdmin(): UseTelaAdminReturn;
@@ -0,0 +1,76 @@
1
+ import { useCookie, useRuntimeConfig } from "#app";
2
+ import { createNuxtAuthClient } from "../shared.js";
3
+ function buildListApplicationsUrl(apiUrl, options = {}) {
4
+ const base = apiUrl.endsWith("/") ? apiUrl : `${apiUrl}/`;
5
+ const url = new URL("applications/", base);
6
+ if (options.limit !== void 0)
7
+ url.searchParams.set("limit", String(options.limit));
8
+ if (options.membersPreview !== void 0)
9
+ url.searchParams.set("membersPreview", String(options.membersPreview));
10
+ if (options.offset !== void 0)
11
+ url.searchParams.set("offset", String(options.offset));
12
+ if (options.orderBy)
13
+ url.searchParams.set("orderBy", options.orderBy);
14
+ if (options.orderDirection)
15
+ url.searchParams.set("orderDirection", options.orderDirection);
16
+ if (options.organizationId)
17
+ url.searchParams.set("organizationId", options.organizationId);
18
+ if (options.search)
19
+ url.searchParams.set("search", options.search);
20
+ return url;
21
+ }
22
+ async function parseAdminResponseError(response) {
23
+ try {
24
+ const body = await response.json();
25
+ return body.error ?? body.message ?? response.statusText;
26
+ } catch {
27
+ return response.statusText;
28
+ }
29
+ }
30
+ export function useTelaAdmin() {
31
+ const { jwtCookieName, apiUrl } = useRuntimeConfig().public.telaAuth;
32
+ const authClient = createNuxtAuthClient(apiUrl, () => useCookie(jwtCookieName).value ?? null);
33
+ async function listUsers(options) {
34
+ return await authClient.admin.listUsers(options);
35
+ }
36
+ async function listApplications(options) {
37
+ const token = useCookie(jwtCookieName).value;
38
+ const headers = new Headers({ Accept: "application/json" });
39
+ if (token) {
40
+ headers.set("Authorization", `Bearer ${token}`);
41
+ }
42
+ const response = await fetch(buildListApplicationsUrl(apiUrl, options), {
43
+ credentials: "include",
44
+ headers
45
+ });
46
+ if (!response.ok) {
47
+ const message = await parseAdminResponseError(response);
48
+ throw new Error(`Failed to list applications: ${message}`);
49
+ }
50
+ return await response.json();
51
+ }
52
+ async function listOrganizations(options) {
53
+ return await authClient.admin.listOrganizations(options);
54
+ }
55
+ async function getOrganization(options) {
56
+ return await authClient.admin.getOrganization(options);
57
+ }
58
+ async function listOrganizationMembers(options) {
59
+ return await authClient.admin.listOrganizationMembers(options);
60
+ }
61
+ async function createOrganization(options) {
62
+ return await authClient.admin.createOrganization(options);
63
+ }
64
+ async function updateOrganization(options) {
65
+ return await authClient.admin.updateOrganization(options);
66
+ }
67
+ return {
68
+ listUsers,
69
+ listApplications,
70
+ listOrganizations,
71
+ getOrganization,
72
+ listOrganizationMembers,
73
+ createOrganization,
74
+ updateOrganization
75
+ };
76
+ }
@@ -1,5 +1,77 @@
1
1
  import type { Ref } from 'vue';
2
2
  import type { FullOrganization, User } from '@meistrari/auth-core';
3
+ export interface UseTelaApplicationAuthReturn {
4
+ /** Reactive reference to the current user. Null if not authenticated. */
5
+ user: Ref<User | null>;
6
+ /** Reactive reference to the active organization. Null if not authenticated. */
7
+ activeOrganization: Ref<FullOrganization | null>;
8
+ /**
9
+ * Initiates the OAuth 2.0 authorization flow with PKCE.
10
+ *
11
+ * Calls the server route to generate state and challenge. The server stores the
12
+ * verifier securely and returns only the challenge and state to the client.
13
+ * Then redirects to the auth dashboard login page.
14
+ *
15
+ * @throws {AuthorizationFlowError} If called on the server
16
+ */
17
+ login: () => Promise<void>;
18
+ /**
19
+ * Logs out the current user.
20
+ *
21
+ * Clears all authentication cookies and session state.
22
+ * After logout, the user will need to login again to access protected resources.
23
+ * Note: Code verifier cookies expire automatically after 10 minutes.
24
+ */
25
+ logout: () => Promise<void>;
26
+ /**
27
+ * Initializes or restores the user session.
28
+ *
29
+ * @deprecated The SDK's Nuxt plugin handles session restoration and background
30
+ * token refresh automatically. On app startup it hydrates `user` and
31
+ * `activeOrganization` via `/auth/whoami` (client) or `whoAmI` (server) and
32
+ * schedules refreshes ahead of access-token expiry. Calling this function
33
+ * is redundant in normal usage and will be removed in a future release.
34
+ *
35
+ * Validates the existing access token and triggers a refresh only if it is
36
+ * expired or within 1 minute of expiry.
37
+ *
38
+ * @throws {UserNotLoggedInError} If no access token is found in cookies
39
+ * @throws {RefreshTokenExpiredError} If tokens are missing, expired, or cannot be refreshed
40
+ */
41
+ initSession: () => Promise<void>;
42
+ /**
43
+ * Lists the organizations the user can access through this application.
44
+ *
45
+ * @deprecated Use `useTelaOrganization().listOrganizations()` instead.
46
+ * @returns The accessible organizations.
47
+ */
48
+ getAvailableOrganizations: () => Promise<FullOrganization[]>;
49
+ /**
50
+ * Switches the active organization.
51
+ *
52
+ * @deprecated Use `useTelaOrganization().setActiveOrganization(organizationId)` instead.
53
+ * @param organizationId - The id of the organization to make active.
54
+ */
55
+ switchOrganization: (organizationId: string) => Promise<void>;
56
+ /**
57
+ * Refreshes the access token using the refresh token.
58
+ *
59
+ * Calls the server route which exchanges the refresh token (stored in httponly cookie)
60
+ * for new access and refresh tokens. The server updates the cookies and returns
61
+ * the user and organization data.
62
+ *
63
+ * @throws {RefreshTokenExpiredError} If no refresh token is available or refresh fails
64
+ */
65
+ refreshToken: () => Promise<void>;
66
+ /**
67
+ * Retrieves the current access token.
68
+ * If the token is expired, or close to expiry, it will be refreshed automatically.
69
+ *
70
+ * @returns The current access token
71
+ * @throws {RefreshTokenExpiredError} If the token is expired and cannot be refreshed
72
+ */
73
+ getToken: () => Promise<string | null | undefined>;
74
+ }
3
75
  /**
4
76
  * Composable for managing Tela application authentication with OAuth 2.0 PKCE flow
5
77
  *
@@ -32,20 +104,4 @@ import type { FullOrganization, User } from '@meistrari/auth-core';
32
104
  * await auth.logout()
33
105
  * ```
34
106
  */
35
- export declare function useTelaApplicationAuth(): {
36
- user: Ref<User | null>;
37
- activeOrganization: Ref<FullOrganization | null>;
38
- login: () => Promise<void>;
39
- logout: () => Promise<void>;
40
- initSession: () => Promise<void>;
41
- /**
42
- * @deprecated Use `useTelaOrganization().listOrganizations()` instead.
43
- */
44
- getAvailableOrganizations: () => Promise<FullOrganization[]>;
45
- /**
46
- * @deprecated Use `useTelaOrganization().setActiveOrganization(organizationId)` instead.
47
- */
48
- switchOrganization: (organizationId: string) => Promise<void>;
49
- refreshToken: () => Promise<void>;
50
- getToken: () => Promise<string | null | undefined>;
51
- };
107
+ export declare function useTelaApplicationAuth(): UseTelaApplicationAuthReturn;
@@ -92,13 +92,6 @@ export function useTelaApplicationAuth() {
92
92
  activeOrganization: state.activeOrganization,
93
93
  login,
94
94
  logout,
95
- /**
96
- * @deprecated The SDK's Nuxt plugin handles session restoration and background
97
- * token refresh automatically. On app startup it hydrates `user` and
98
- * `activeOrganization` via `/auth/whoami` (client) or `whoAmI` (server) and
99
- * schedules refreshes ahead of access-token expiry. Calling this function
100
- * is redundant in normal usage and will be removed in a future release.
101
- */
102
95
  initSession,
103
96
  getAvailableOrganizations,
104
97
  switchOrganization,
@@ -1,27 +1,205 @@
1
1
  import type { CreateTeamPayload, FullOrganization, Invitation, InviteUserToOrganizationOptions, ListMembersOptions, Member, RemoveUserFromOrganizationOptions, Team, TeamMember, UpdateMemberRoleOptions, UpdateTeamPayload } from '@meistrari/auth-core';
2
2
  import type { Ref } from 'vue';
3
3
  export interface UseTelaApplicationOrganizationReturn {
4
+ /** Reactive reference to the active organization with members, invitations, and teams. */
4
5
  activeOrganization: Ref<FullOrganization | null>;
6
+ /** Reactive reference to the current user's membership in the active organization. */
5
7
  activeMember: Ref<Member | null>;
8
+ /**
9
+ * Re-fetches the full active organization (members, invitations, teams) from
10
+ * the Auth API and writes it back into `activeOrganization` state.
11
+ *
12
+ * Use this to reload the active organization on demand — for instance after
13
+ * mutations performed outside this composable. It is a no-op that returns
14
+ * `undefined` when there is no active organization id in state yet.
15
+ *
16
+ * @returns The refreshed active organization, or `undefined` when none is active.
17
+ */
6
18
  getActiveOrganization: () => Promise<FullOrganization | undefined>;
19
+ /**
20
+ * Alias for {@link listOrganizations}.
21
+ *
22
+ * @returns The organizations the user can access through this application.
23
+ */
7
24
  getAvailableOrganizations: () => Promise<FullOrganization[]>;
25
+ /**
26
+ * Lists the organizations the user can access through this application.
27
+ *
28
+ * This is not every organization the user belongs to: the Auth API only
29
+ * returns organizations the user is entitled to within the current
30
+ * application, so it doubles as the set of valid targets for
31
+ * {@link setActiveOrganization}.
32
+ *
33
+ * @returns The organizations accessible through this application.
34
+ */
8
35
  listOrganizations: () => Promise<FullOrganization[]>;
36
+ /**
37
+ * Switches the active organization via the `/auth/switch-organization` server
38
+ * route, then updates `user`, `activeOrganization`, and session assurance from
39
+ * the response.
40
+ *
41
+ * @param id - The id of the organization to make active.
42
+ */
9
43
  setActiveOrganization: (id: string) => Promise<void>;
44
+ /**
45
+ * Lists members of the active organization.
46
+ *
47
+ * Returns members on demand without touching `activeOrganization` state — the
48
+ * mutating methods here (invite/accept/remove/update-role) are what keep the
49
+ * cached `activeOrganization.members` in sync.
50
+ *
51
+ * @param options - Optional filtering/pagination options.
52
+ * @returns The active organization's members.
53
+ */
10
54
  listMembers: (options?: ListMembersOptions) => Promise<Member[]>;
55
+ /**
56
+ * Fetches the current user's own member record in the active organization
57
+ * (their role and membership metadata) and caches it in `activeMember` state.
58
+ *
59
+ * Useful for role-gating UI, since it reflects the caller's role in the
60
+ * organization currently active for this application.
61
+ *
62
+ * @returns The caller's member record in the active organization.
63
+ */
11
64
  getActiveMember: () => Promise<Member>;
65
+ /**
66
+ * Invites a user to the active organization.
67
+ *
68
+ * The invitation is created **application-scoped**: the Auth API tags it with
69
+ * the application derived from the access token, so accepting it later grants
70
+ * the invitee access to this application. The new invitation is appended to
71
+ * `activeOrganization.invitations` in local state.
72
+ *
73
+ * @param options - Invitee email, role, and optional team/resend settings.
74
+ * @returns The created invitation.
75
+ */
12
76
  inviteUserToOrganization: (options: InviteUserToOrganizationOptions) => Promise<Invitation>;
77
+ /**
78
+ * Accepts an invitation by id.
79
+ *
80
+ * For an application-scoped invitation, accepting **grants the user access to
81
+ * the associated application** (the Auth API writes an entitlement rule) and
82
+ * returns that application's `homeUrl`. For an invitation with no application,
83
+ * `homeUrl` is `null`. The active organization's member list is refreshed
84
+ * afterwards.
85
+ *
86
+ * @param id - The invitation id to accept.
87
+ * @returns An object with `homeUrl`: the application's home URL, or `null`.
88
+ */
13
89
  acceptInvitation: (id: string) => Promise<{
14
90
  homeUrl: string | null;
15
91
  }>;
92
+ /**
93
+ * Cancels a pending invitation before it is accepted, revoking it so the link
94
+ * can no longer be used, and removes it from `activeOrganization.invitations`
95
+ * in local state.
96
+ *
97
+ * @param id - The invitation id to cancel.
98
+ */
16
99
  cancelInvitation: (id: string) => Promise<void>;
100
+ /**
101
+ * Removes a member from the active organization, revoking their access, and
102
+ * prunes them from `activeOrganization.members` in local state.
103
+ *
104
+ * Since access to this application is granted through organization membership,
105
+ * removing the member also revokes their access to the application through
106
+ * this organization.
107
+ *
108
+ * @param options - Identifies the member to remove (by `memberId`).
109
+ */
17
110
  removeUserFromOrganization: (options: RemoveUserFromOrganizationOptions) => Promise<void>;
111
+ /**
112
+ * Changes a member's role in the active organization, then re-fetches the full
113
+ * member list and writes it back into `activeOrganization.members`.
114
+ *
115
+ * The role drives that member's permissions for organization operations, and
116
+ * the role-aware UI helpers (`<TelaRole>`, `v-if-role`, `v-show-role`) read
117
+ * from it.
118
+ *
119
+ * @param options - Identifies the member and the new role to assign.
120
+ */
18
121
  updateMemberRole: (options: UpdateMemberRoleOptions) => Promise<void>;
122
+ /**
123
+ * Creates a team in the active organization and appends it to
124
+ * `activeOrganization.teams` in local state.
125
+ *
126
+ * @param payload - The team creation payload.
127
+ * @returns The created team.
128
+ */
19
129
  createTeam: (payload: CreateTeamPayload) => Promise<Team>;
130
+ /**
131
+ * Updates a team and replaces it in `activeOrganization.teams` in local state.
132
+ *
133
+ * @param id - The team id to update.
134
+ * @param payload - The team update payload.
135
+ * @returns The updated team.
136
+ */
20
137
  updateTeam: (id: string, payload: UpdateTeamPayload) => Promise<Team>;
138
+ /**
139
+ * Deletes a team and removes it from `activeOrganization.teams` in local state.
140
+ *
141
+ * @param id - The team id to delete.
142
+ */
21
143
  deleteTeam: (id: string) => Promise<void>;
144
+ /**
145
+ * Fetches teams of the active organization on demand.
146
+ *
147
+ * Unlike {@link createTeam}/{@link updateTeam}/{@link deleteTeam}, this does
148
+ * not read from or write to the cached `activeOrganization.teams`; it always
149
+ * hits the Auth API.
150
+ *
151
+ * @returns The active organization's teams.
152
+ */
22
153
  listTeams: () => Promise<Team[]>;
154
+ /**
155
+ * Lists the users belonging to a team within the active organization.
156
+ *
157
+ * Team members are a subset of organization members; the team must belong to
158
+ * the active organization.
159
+ *
160
+ * @param id - The team id to list members for.
161
+ * @returns The team's members.
162
+ */
23
163
  listTeamMembers: (id: string) => Promise<TeamMember[]>;
164
+ /**
165
+ * Adds an existing organization member to a team.
166
+ *
167
+ * The user must already be a member of the active organization — this assigns
168
+ * them to a team within it, it does not invite them to the organization. The
169
+ * cached `activeOrganization.teams` is left untouched.
170
+ *
171
+ * @param teamId - The team to add the user to.
172
+ * @param userId - The id of the organization member to add.
173
+ * @returns The created team membership.
174
+ */
24
175
  addTeamMember: (teamId: string, userId: string) => Promise<TeamMember>;
176
+ /**
177
+ * Removes a user from a team without removing them from the organization.
178
+ *
179
+ * The user keeps their organization membership (and application access); only
180
+ * their team assignment is dropped.
181
+ *
182
+ * @param teamId - The team to remove the user from.
183
+ * @param userId - The id of the user to remove from the team.
184
+ */
25
185
  removeTeamMember: (teamId: string, userId: string) => Promise<void>;
26
186
  }
187
+ /**
188
+ * Composable for managing organizations in application authentication mode.
189
+ *
190
+ * Most methods call the Auth API through an application-scoped client built from
191
+ * the current application access token (`tela-access-token`). `setActiveOrganization`
192
+ * is the exception: it goes through the Nuxt server route `/auth/switch-organization`
193
+ * rather than that client, but the token is still forwarded via the
194
+ * `tela-access-token` cookie, so every method stays application-scoped. Scoping to
195
+ * the configured application is automatic: the Auth API derives the application
196
+ * from the token's audience (`aud`) claim, so callers never pass an `applicationId`.
197
+ * Invitations created here are tagged with the application, and accepting an
198
+ * application-scoped invitation grants the user access to that application.
199
+ *
200
+ * Methods that mutate organization data also keep the local `activeOrganization`
201
+ * state in sync (members, invitations, teams) so the UI updates without a refetch.
202
+ *
203
+ * @returns Organization state refs and management methods.
204
+ */
27
205
  export declare function useTelaOrganization(): UseTelaApplicationOrganizationReturn;
@@ -2,7 +2,26 @@ import type { Ref } from 'vue';
2
2
  import type { JWTPayload } from '@meistrari/auth-core';
3
3
  export type ApplicationSessionAssuranceValue = JWTPayload['assurance'] | null;
4
4
  export interface UseTelaApplicationSessionAssuranceReturn {
5
+ /** Read-only reactive reference to the current application session assurance. */
5
6
  assurance: Readonly<Ref<ApplicationSessionAssuranceValue>>;
7
+ /**
8
+ * Refreshes the application auth tokens through the `/auth/refresh` server
9
+ * route and updates `user`, `activeOrganization`, and the session assurance
10
+ * from the response.
11
+ *
12
+ * @returns The current session assurance after refreshing.
13
+ */
6
14
  refreshSessionAssurance: () => Promise<ApplicationSessionAssuranceValue>;
7
15
  }
16
+ /**
17
+ * Read-only composable for inspecting the application session assurance.
18
+ *
19
+ * In application authentication mode this composable only reads the current
20
+ * assurance and can refresh the application auth tokens to pick up a newly
21
+ * stepped-up assurance value. First-party step-up/write actions
22
+ * (`stepUpWithPassword`, `sendOtp`, `verifyOtp`, OAuth step-up) are not available
23
+ * in application mode.
24
+ *
25
+ * @returns The read-only `assurance` ref and `refreshSessionAssurance()`.
26
+ */
8
27
  export declare function useTelaSessionAssurance(): UseTelaApplicationSessionAssuranceReturn;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "extends": [
3
- "../../../../../../node_modules/@meistrari/mise-en-place/tsconfig.base.json",
3
+ "../../../../../node_modules/@meistrari/mise-en-place/tsconfig.base.json",
4
4
  "../../../.nuxt/tsconfig.server.json"
5
5
  ],
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/auth-nuxt",
3
- "version": "3.10.1",
3
+ "version": "3.12.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -36,7 +36,7 @@
36
36
  "docs": "nuxt-module-build prepare && typedoc"
37
37
  },
38
38
  "dependencies": {
39
- "@meistrari/auth-core": "1.21.0",
39
+ "@meistrari/auth-core": "1.23.0",
40
40
  "jose": "6.1.3"
41
41
  },
42
42
  "peerDependencies": {