@dashai/sdk 0.7.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/LICENSE +21 -0
- package/README.md +377 -0
- package/dist/auth/client.cjs +185 -0
- package/dist/auth/client.cjs.map +1 -0
- package/dist/auth/client.d.cts +137 -0
- package/dist/auth/client.d.ts +137 -0
- package/dist/auth/client.js +175 -0
- package/dist/auth/client.js.map +1 -0
- package/dist/auth/middleware.cjs +352 -0
- package/dist/auth/middleware.cjs.map +1 -0
- package/dist/auth/middleware.d.cts +90 -0
- package/dist/auth/middleware.d.ts +90 -0
- package/dist/auth/middleware.js +343 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/auth/server.cjs +445 -0
- package/dist/auth/server.cjs.map +1 -0
- package/dist/auth/server.d.cts +170 -0
- package/dist/auth/server.d.ts +170 -0
- package/dist/auth/server.js +432 -0
- package/dist/auth/server.js.map +1 -0
- package/dist/auth/types.cjs +31 -0
- package/dist/auth/types.cjs.map +1 -0
- package/dist/auth/types.d.cts +163 -0
- package/dist/auth/types.d.ts +163 -0
- package/dist/auth/types.js +29 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/deps/index.cjs +117 -0
- package/dist/deps/index.cjs.map +1 -0
- package/dist/deps/index.d.cts +93 -0
- package/dist/deps/index.d.ts +93 -0
- package/dist/deps/index.js +112 -0
- package/dist/deps/index.js.map +1 -0
- package/dist/errors-BV75u7b9.d.cts +207 -0
- package/dist/errors-BV75u7b9.d.ts +207 -0
- package/dist/index.cjs +721 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +171 -0
- package/dist/index.d.ts +171 -0
- package/dist/index.js +703 -0
- package/dist/index.js.map +1 -0
- package/dist/query/index.cjs +621 -0
- package/dist/query/index.cjs.map +1 -0
- package/dist/query/index.d.cts +800 -0
- package/dist/query/index.d.ts +800 -0
- package/dist/query/index.js +617 -0
- package/dist/query/index.js.map +1 -0
- package/dist/react/index.cjs +199 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +117 -0
- package/dist/react/index.d.ts +117 -0
- package/dist/react/index.js +195 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-BLNQ1S1C.d.cts +396 -0
- package/dist/types-BLNQ1S1C.d.ts +396 -0
- package/package.json +109 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { AuthOptions, ExchangeResult, RbacMeResponse, Session } from './types.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Server-side auth helpers — used from React Server Components, route
|
|
5
|
+
* handlers, and server actions. Imports `next/headers` lazily so the
|
|
6
|
+
* subpath remains importable from non-Next environments (tests, unit
|
|
7
|
+
* fixtures) without crashing on missing peer deps.
|
|
8
|
+
*
|
|
9
|
+
* Consumers:
|
|
10
|
+
* import { getServerSession, exchangeCode } from '@dashai/sdk/auth/server';
|
|
11
|
+
*
|
|
12
|
+
* Public surface:
|
|
13
|
+
* - `getServerSession(opts?)` — read the current session, or null
|
|
14
|
+
* - `exchangeCode(code, opts?)` — exchange an SSO one-time code for a session
|
|
15
|
+
* - `signOut(opts?)` — clear the session cookie + tell the backend
|
|
16
|
+
*
|
|
17
|
+
* All three accept an optional `AuthOptions` argument. Defaults come
|
|
18
|
+
* from `DASHWISE_*` env vars; see `config.ts`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get the current session, or `null` if the user isn't signed in.
|
|
23
|
+
*
|
|
24
|
+
* Reads the session cookie from `next/headers` and validates it
|
|
25
|
+
* against the DashWise backend at `/api/auth/sessions/me`. Returns
|
|
26
|
+
* the validated `Session` on success.
|
|
27
|
+
*
|
|
28
|
+
* Cache: this call is uncached on purpose. The backend's `/me`
|
|
29
|
+
* endpoint is cheap (single DB row by indexed PK). Consumers that
|
|
30
|
+
* call `getServerSession()` multiple times per request should
|
|
31
|
+
* memoize at the React Server Component level.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* import { getServerSession } from '@dashai/sdk/auth/server';
|
|
35
|
+
* import { redirect } from 'next/navigation';
|
|
36
|
+
*
|
|
37
|
+
* export default async function DashboardPage() {
|
|
38
|
+
* const session = await getServerSession();
|
|
39
|
+
* if (!session) redirect('/api/auth/sign-in');
|
|
40
|
+
* return <h1>Hi {session.user.email}</h1>;
|
|
41
|
+
* }
|
|
42
|
+
*/
|
|
43
|
+
declare function getServerSession(options?: AuthOptions): Promise<Session | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Fetch `/api/installations/:id/rbac/me` for the active session.
|
|
46
|
+
* Returns the resolved roles + permissions, or null on any failure
|
|
47
|
+
* (network, HTTP error, schema mismatch). Failure is non-fatal —
|
|
48
|
+
* the caller treats null as "no permissions" (default-deny).
|
|
49
|
+
*
|
|
50
|
+
* Exported so `auth/middleware.ts` can reuse the same code path
|
|
51
|
+
* without re-implementing the request shape.
|
|
52
|
+
*/
|
|
53
|
+
declare function fetchRbacMe(apiBaseUrl: string, cookieHeader: string, installationId: number, fetchImpl: typeof globalThis.fetch): Promise<RbacMeResponse | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Exchange an SSO one-time `code` (received at the OAuth callback
|
|
56
|
+
* URL `?code=...`) for a session. On success, sets the session
|
|
57
|
+
* cookie via `next/headers` AND returns the decoded session so the
|
|
58
|
+
* caller can chain a `redirect(returnTo)`.
|
|
59
|
+
*
|
|
60
|
+
* Call from your `/api/auth/callback/route.ts`:
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* import { exchangeCode } from '@dashai/sdk/auth/server';
|
|
64
|
+
* import { NextResponse } from 'next/server';
|
|
65
|
+
*
|
|
66
|
+
* export async function GET(request: Request) {
|
|
67
|
+
* const url = new URL(request.url);
|
|
68
|
+
* const code = url.searchParams.get('code');
|
|
69
|
+
* const returnTo = url.searchParams.get('return_to') ?? '/';
|
|
70
|
+
* if (!code) return NextResponse.redirect(new URL('/', request.url));
|
|
71
|
+
* await exchangeCode(code); // sets the cookie
|
|
72
|
+
* return NextResponse.redirect(new URL(returnTo, request.url));
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* Throws `DashwiseError` with `code === 'INVALID_EXCHANGE_CODE'` if
|
|
76
|
+
* the backend rejects (expired / replay / forged code).
|
|
77
|
+
*/
|
|
78
|
+
declare function exchangeCode(code: string, options?: AuthOptions): Promise<ExchangeResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Sign out the current user: clears the session cookie and notifies
|
|
81
|
+
* the backend so the server-side session record can be revoked
|
|
82
|
+
* before its natural expiry.
|
|
83
|
+
*
|
|
84
|
+
* Safe to call when not signed in (no-op). Returns nothing.
|
|
85
|
+
*
|
|
86
|
+
* Backend-side revocation is best-effort: if `/sign-out` fails, the
|
|
87
|
+
* cookie is still cleared client-side, so the user's local session
|
|
88
|
+
* is over regardless. The backend will reap the orphaned session
|
|
89
|
+
* record on its next periodic sweep.
|
|
90
|
+
*/
|
|
91
|
+
declare function signOut(options?: AuthOptions): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Sync check: does the given session hold `permissionKey`?
|
|
94
|
+
*
|
|
95
|
+
* Accepts either the bare form (`tasks:delete`) or the namespaced
|
|
96
|
+
* form (`tasks-app:tasks:delete`). Returns false if the session is
|
|
97
|
+
* null OR has no RBAC data (default-deny floor).
|
|
98
|
+
*
|
|
99
|
+
* For consumers using `getServerSession()`, prefer the async variants
|
|
100
|
+
* below — they handle the case where you forgot to call
|
|
101
|
+
* `getServerSession()` first.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* const session = await getServerSession();
|
|
105
|
+
* if (!hasPermissionInSession(session, 'tasks:delete')) {
|
|
106
|
+
* return new Response('Forbidden', { status: 403 });
|
|
107
|
+
* }
|
|
108
|
+
*/
|
|
109
|
+
declare function hasPermissionInSession(session: Session | null | undefined, permissionKey: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Sync check: does the given session hold `roleKey`?
|
|
112
|
+
* Same semantics as `hasPermissionInSession`.
|
|
113
|
+
*/
|
|
114
|
+
declare function hasRoleInSession(session: Session | null | undefined, roleKey: string): boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Async convenience: fetch the session AND check a permission in
|
|
117
|
+
* one call. Returns true iff a session exists + holds the key.
|
|
118
|
+
*
|
|
119
|
+
* Most server-component code paths look like:
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* if (!(await hasPermission('tasks:delete'))) {
|
|
123
|
+
* return <Forbidden />;
|
|
124
|
+
* }
|
|
125
|
+
*/
|
|
126
|
+
declare function hasPermission(permissionKey: string, options?: AuthOptions): Promise<boolean>;
|
|
127
|
+
/**
|
|
128
|
+
* Async convenience: as `hasPermission` but for roles.
|
|
129
|
+
*/
|
|
130
|
+
declare function hasRole(roleKey: string, options?: AuthOptions): Promise<boolean>;
|
|
131
|
+
/**
|
|
132
|
+
* Throw `DashwiseError('PERMISSION_DENIED')` if the session lacks
|
|
133
|
+
* `permissionKey`. Pattern for guarding server actions + RSC
|
|
134
|
+
* branches that should redirect-to-403 on failure.
|
|
135
|
+
*
|
|
136
|
+
* Throws `RBAC_UNAVAILABLE` if there's no session at all — distinct
|
|
137
|
+
* from PERMISSION_DENIED so callers can branch on "needs sign-in"
|
|
138
|
+
* vs "signed in but forbidden".
|
|
139
|
+
*
|
|
140
|
+
* Returns the loaded session on success — saves a second
|
|
141
|
+
* `getServerSession()` call in handlers that need both.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* import { requirePermission } from '@dashai/sdk/auth/server';
|
|
145
|
+
*
|
|
146
|
+
* export default async function DeletePanel() {
|
|
147
|
+
* const session = await requirePermission('tasks:delete');
|
|
148
|
+
* return <DeleteForm userEmail={session.user.email} />;
|
|
149
|
+
* }
|
|
150
|
+
*/
|
|
151
|
+
declare function requirePermission(permissionKey: string, options?: AuthOptions): Promise<Session>;
|
|
152
|
+
/**
|
|
153
|
+
* Throw `PERMISSION_DENIED` unless the session holds at least one of
|
|
154
|
+
* the requested permissions. Useful for endpoints with multiple
|
|
155
|
+
* acceptable scopes.
|
|
156
|
+
*/
|
|
157
|
+
declare function requireAnyPermission(permissionKeys: readonly string[], options?: AuthOptions): Promise<Session>;
|
|
158
|
+
/**
|
|
159
|
+
* Throw `PERMISSION_DENIED` unless the session holds all of the
|
|
160
|
+
* requested permissions. Useful for multi-permission gating where
|
|
161
|
+
* partial grants aren't acceptable.
|
|
162
|
+
*/
|
|
163
|
+
declare function requireAllPermissions(permissionKeys: readonly string[], options?: AuthOptions): Promise<Session>;
|
|
164
|
+
/**
|
|
165
|
+
* Throw `PERMISSION_DENIED` unless the session holds `roleKey`.
|
|
166
|
+
* Sugar over `requirePermission` for role-centric handlers.
|
|
167
|
+
*/
|
|
168
|
+
declare function requireRole(roleKey: string, options?: AuthOptions): Promise<Session>;
|
|
169
|
+
|
|
170
|
+
export { exchangeCode, fetchRbacMe, getServerSession, hasPermission, hasPermissionInSession, hasRole, hasRoleInSession, requireAllPermissions, requireAnyPermission, requirePermission, requireRole, signOut };
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { AuthOptions, ExchangeResult, RbacMeResponse, Session } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Server-side auth helpers — used from React Server Components, route
|
|
5
|
+
* handlers, and server actions. Imports `next/headers` lazily so the
|
|
6
|
+
* subpath remains importable from non-Next environments (tests, unit
|
|
7
|
+
* fixtures) without crashing on missing peer deps.
|
|
8
|
+
*
|
|
9
|
+
* Consumers:
|
|
10
|
+
* import { getServerSession, exchangeCode } from '@dashai/sdk/auth/server';
|
|
11
|
+
*
|
|
12
|
+
* Public surface:
|
|
13
|
+
* - `getServerSession(opts?)` — read the current session, or null
|
|
14
|
+
* - `exchangeCode(code, opts?)` — exchange an SSO one-time code for a session
|
|
15
|
+
* - `signOut(opts?)` — clear the session cookie + tell the backend
|
|
16
|
+
*
|
|
17
|
+
* All three accept an optional `AuthOptions` argument. Defaults come
|
|
18
|
+
* from `DASHWISE_*` env vars; see `config.ts`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get the current session, or `null` if the user isn't signed in.
|
|
23
|
+
*
|
|
24
|
+
* Reads the session cookie from `next/headers` and validates it
|
|
25
|
+
* against the DashWise backend at `/api/auth/sessions/me`. Returns
|
|
26
|
+
* the validated `Session` on success.
|
|
27
|
+
*
|
|
28
|
+
* Cache: this call is uncached on purpose. The backend's `/me`
|
|
29
|
+
* endpoint is cheap (single DB row by indexed PK). Consumers that
|
|
30
|
+
* call `getServerSession()` multiple times per request should
|
|
31
|
+
* memoize at the React Server Component level.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* import { getServerSession } from '@dashai/sdk/auth/server';
|
|
35
|
+
* import { redirect } from 'next/navigation';
|
|
36
|
+
*
|
|
37
|
+
* export default async function DashboardPage() {
|
|
38
|
+
* const session = await getServerSession();
|
|
39
|
+
* if (!session) redirect('/api/auth/sign-in');
|
|
40
|
+
* return <h1>Hi {session.user.email}</h1>;
|
|
41
|
+
* }
|
|
42
|
+
*/
|
|
43
|
+
declare function getServerSession(options?: AuthOptions): Promise<Session | null>;
|
|
44
|
+
/**
|
|
45
|
+
* Fetch `/api/installations/:id/rbac/me` for the active session.
|
|
46
|
+
* Returns the resolved roles + permissions, or null on any failure
|
|
47
|
+
* (network, HTTP error, schema mismatch). Failure is non-fatal —
|
|
48
|
+
* the caller treats null as "no permissions" (default-deny).
|
|
49
|
+
*
|
|
50
|
+
* Exported so `auth/middleware.ts` can reuse the same code path
|
|
51
|
+
* without re-implementing the request shape.
|
|
52
|
+
*/
|
|
53
|
+
declare function fetchRbacMe(apiBaseUrl: string, cookieHeader: string, installationId: number, fetchImpl: typeof globalThis.fetch): Promise<RbacMeResponse | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Exchange an SSO one-time `code` (received at the OAuth callback
|
|
56
|
+
* URL `?code=...`) for a session. On success, sets the session
|
|
57
|
+
* cookie via `next/headers` AND returns the decoded session so the
|
|
58
|
+
* caller can chain a `redirect(returnTo)`.
|
|
59
|
+
*
|
|
60
|
+
* Call from your `/api/auth/callback/route.ts`:
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* import { exchangeCode } from '@dashai/sdk/auth/server';
|
|
64
|
+
* import { NextResponse } from 'next/server';
|
|
65
|
+
*
|
|
66
|
+
* export async function GET(request: Request) {
|
|
67
|
+
* const url = new URL(request.url);
|
|
68
|
+
* const code = url.searchParams.get('code');
|
|
69
|
+
* const returnTo = url.searchParams.get('return_to') ?? '/';
|
|
70
|
+
* if (!code) return NextResponse.redirect(new URL('/', request.url));
|
|
71
|
+
* await exchangeCode(code); // sets the cookie
|
|
72
|
+
* return NextResponse.redirect(new URL(returnTo, request.url));
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* Throws `DashwiseError` with `code === 'INVALID_EXCHANGE_CODE'` if
|
|
76
|
+
* the backend rejects (expired / replay / forged code).
|
|
77
|
+
*/
|
|
78
|
+
declare function exchangeCode(code: string, options?: AuthOptions): Promise<ExchangeResult>;
|
|
79
|
+
/**
|
|
80
|
+
* Sign out the current user: clears the session cookie and notifies
|
|
81
|
+
* the backend so the server-side session record can be revoked
|
|
82
|
+
* before its natural expiry.
|
|
83
|
+
*
|
|
84
|
+
* Safe to call when not signed in (no-op). Returns nothing.
|
|
85
|
+
*
|
|
86
|
+
* Backend-side revocation is best-effort: if `/sign-out` fails, the
|
|
87
|
+
* cookie is still cleared client-side, so the user's local session
|
|
88
|
+
* is over regardless. The backend will reap the orphaned session
|
|
89
|
+
* record on its next periodic sweep.
|
|
90
|
+
*/
|
|
91
|
+
declare function signOut(options?: AuthOptions): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Sync check: does the given session hold `permissionKey`?
|
|
94
|
+
*
|
|
95
|
+
* Accepts either the bare form (`tasks:delete`) or the namespaced
|
|
96
|
+
* form (`tasks-app:tasks:delete`). Returns false if the session is
|
|
97
|
+
* null OR has no RBAC data (default-deny floor).
|
|
98
|
+
*
|
|
99
|
+
* For consumers using `getServerSession()`, prefer the async variants
|
|
100
|
+
* below — they handle the case where you forgot to call
|
|
101
|
+
* `getServerSession()` first.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* const session = await getServerSession();
|
|
105
|
+
* if (!hasPermissionInSession(session, 'tasks:delete')) {
|
|
106
|
+
* return new Response('Forbidden', { status: 403 });
|
|
107
|
+
* }
|
|
108
|
+
*/
|
|
109
|
+
declare function hasPermissionInSession(session: Session | null | undefined, permissionKey: string): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Sync check: does the given session hold `roleKey`?
|
|
112
|
+
* Same semantics as `hasPermissionInSession`.
|
|
113
|
+
*/
|
|
114
|
+
declare function hasRoleInSession(session: Session | null | undefined, roleKey: string): boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Async convenience: fetch the session AND check a permission in
|
|
117
|
+
* one call. Returns true iff a session exists + holds the key.
|
|
118
|
+
*
|
|
119
|
+
* Most server-component code paths look like:
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* if (!(await hasPermission('tasks:delete'))) {
|
|
123
|
+
* return <Forbidden />;
|
|
124
|
+
* }
|
|
125
|
+
*/
|
|
126
|
+
declare function hasPermission(permissionKey: string, options?: AuthOptions): Promise<boolean>;
|
|
127
|
+
/**
|
|
128
|
+
* Async convenience: as `hasPermission` but for roles.
|
|
129
|
+
*/
|
|
130
|
+
declare function hasRole(roleKey: string, options?: AuthOptions): Promise<boolean>;
|
|
131
|
+
/**
|
|
132
|
+
* Throw `DashwiseError('PERMISSION_DENIED')` if the session lacks
|
|
133
|
+
* `permissionKey`. Pattern for guarding server actions + RSC
|
|
134
|
+
* branches that should redirect-to-403 on failure.
|
|
135
|
+
*
|
|
136
|
+
* Throws `RBAC_UNAVAILABLE` if there's no session at all — distinct
|
|
137
|
+
* from PERMISSION_DENIED so callers can branch on "needs sign-in"
|
|
138
|
+
* vs "signed in but forbidden".
|
|
139
|
+
*
|
|
140
|
+
* Returns the loaded session on success — saves a second
|
|
141
|
+
* `getServerSession()` call in handlers that need both.
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* import { requirePermission } from '@dashai/sdk/auth/server';
|
|
145
|
+
*
|
|
146
|
+
* export default async function DeletePanel() {
|
|
147
|
+
* const session = await requirePermission('tasks:delete');
|
|
148
|
+
* return <DeleteForm userEmail={session.user.email} />;
|
|
149
|
+
* }
|
|
150
|
+
*/
|
|
151
|
+
declare function requirePermission(permissionKey: string, options?: AuthOptions): Promise<Session>;
|
|
152
|
+
/**
|
|
153
|
+
* Throw `PERMISSION_DENIED` unless the session holds at least one of
|
|
154
|
+
* the requested permissions. Useful for endpoints with multiple
|
|
155
|
+
* acceptable scopes.
|
|
156
|
+
*/
|
|
157
|
+
declare function requireAnyPermission(permissionKeys: readonly string[], options?: AuthOptions): Promise<Session>;
|
|
158
|
+
/**
|
|
159
|
+
* Throw `PERMISSION_DENIED` unless the session holds all of the
|
|
160
|
+
* requested permissions. Useful for multi-permission gating where
|
|
161
|
+
* partial grants aren't acceptable.
|
|
162
|
+
*/
|
|
163
|
+
declare function requireAllPermissions(permissionKeys: readonly string[], options?: AuthOptions): Promise<Session>;
|
|
164
|
+
/**
|
|
165
|
+
* Throw `PERMISSION_DENIED` unless the session holds `roleKey`.
|
|
166
|
+
* Sugar over `requirePermission` for role-centric handlers.
|
|
167
|
+
*/
|
|
168
|
+
declare function requireRole(roleKey: string, options?: AuthOptions): Promise<Session>;
|
|
169
|
+
|
|
170
|
+
export { exchangeCode, fetchRbacMe, getServerSession, hasPermission, hasPermissionInSession, hasRole, hasRoleInSession, requireAllPermissions, requireAnyPermission, requirePermission, requireRole, signOut };
|