@wrelik/auth 0.2.1 → 2.0.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/.turbo/turbo-build.log +18 -22
- package/CHANGELOG.md +26 -0
- package/dist/client/index.d.mts +5 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/{next.js → client/index.js} +19 -25
- package/dist/client/index.mjs +22 -0
- package/dist/server/index.d.mts +11 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/{index.js → server/index.js} +36 -28
- package/dist/server/index.mjs +54 -0
- package/dist/shared/index.d.mts +21 -0
- package/dist/shared/index.d.ts +21 -0
- package/dist/shared/index.js +18 -0
- package/dist/shared/index.mjs +0 -0
- package/package.json +28 -8
- package/src/client/index.contract.test.ts +24 -0
- package/src/client/index.test.ts +18 -0
- package/src/client/index.ts +23 -0
- package/src/server/index.test.ts +61 -0
- package/src/server/index.ts +60 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/types.ts +21 -0
- package/dist/chunk-5WLTDLEP.mjs +0 -31
- package/dist/chunk-HWIZ2Q5U.mjs +0 -17
- package/dist/index.d.mts +0 -7
- package/dist/index.d.ts +0 -7
- package/dist/index.mjs +0 -16
- package/dist/next.d.mts +0 -5
- package/dist/next.d.ts +0 -5
- package/dist/next.mjs +0 -14
- package/dist/react-native.d.mts +0 -14
- package/dist/react-native.d.ts +0 -14
- package/dist/react-native.js +0 -76
- package/dist/react-native.mjs +0 -27
- package/dist/shared-Z5LN5APr.d.mts +0 -11
- package/dist/shared-Z5LN5APr.d.ts +0 -11
- package/src/index.test.ts +0 -29
- package/src/index.ts +0 -21
- package/src/next.ts +0 -8
- package/src/react-native.ts +0 -28
- package/src/shared.test.ts +0 -27
- package/src/shared.ts +0 -33
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
|
|
2
|
-
> @wrelik/auth@0.
|
|
3
|
-
> tsup src/index.ts src/
|
|
2
|
+
> @wrelik/auth@2.0.0 build /home/runner/work/wrelik-kit/wrelik-kit/packages/auth
|
|
3
|
+
> tsup src/server/index.ts src/client/index.ts src/shared/index.ts --format cjs,esm --dts --clean
|
|
4
4
|
|
|
5
|
-
[34mCLI[39m Building entry: src/index.ts, src/
|
|
5
|
+
[34mCLI[39m Building entry: src/client/index.ts, src/shared/index.ts, src/server/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
7
|
[34mCLI[39m tsup v8.5.1
|
|
8
8
|
[34mCLI[39m Target: es2019
|
|
9
9
|
[34mCLI[39m Cleaning output folder
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
11
|
[34mESM[39m Build start
|
|
12
|
-
[32mCJS[39m [1mdist/index.js
|
|
13
|
-
[32mCJS[39m [1mdist/
|
|
14
|
-
[32mCJS[39m [1mdist/
|
|
15
|
-
[32mCJS[39m ⚡️ Build success in
|
|
16
|
-
[32mESM[39m [1mdist/index.mjs
|
|
17
|
-
[32mESM[39m [1mdist/
|
|
18
|
-
[32mESM[39m [1mdist/
|
|
19
|
-
[32mESM[39m
|
|
20
|
-
[32mESM[39m [1mdist/chunk-5WLTDLEP.mjs [22m[32m748.00 B[39m
|
|
21
|
-
[32mESM[39m ⚡️ Build success in 55ms
|
|
12
|
+
[32mCJS[39m [1mdist/client/index.js [22m[32m1.62 KB[39m
|
|
13
|
+
[32mCJS[39m [1mdist/shared/index.js [22m[32m767.00 B[39m
|
|
14
|
+
[32mCJS[39m [1mdist/server/index.js [22m[32m2.70 KB[39m
|
|
15
|
+
[32mCJS[39m ⚡️ Build success in 62ms
|
|
16
|
+
[32mESM[39m [1mdist/shared/index.mjs [22m[32m0 B[39m
|
|
17
|
+
[32mESM[39m [1mdist/client/index.mjs [22m[32m631.00 B[39m
|
|
18
|
+
[32mESM[39m [1mdist/server/index.mjs [22m[32m1.53 KB[39m
|
|
19
|
+
[32mESM[39m ⚡️ Build success in 65ms
|
|
22
20
|
[34mDTS[39m Build start
|
|
23
|
-
[32mDTS[39m ⚡️ Build success in
|
|
24
|
-
[32mDTS[39m [1mdist/index.d.ts
|
|
25
|
-
[32mDTS[39m [1mdist/
|
|
26
|
-
[32mDTS[39m [1mdist/
|
|
27
|
-
[32mDTS[39m [1mdist/
|
|
28
|
-
[32mDTS[39m [1mdist/index.d.mts
|
|
29
|
-
[32mDTS[39m [1mdist/
|
|
30
|
-
[32mDTS[39m [1mdist/react-native.d.mts [22m[32m432.00 B[39m
|
|
31
|
-
[32mDTS[39m [1mdist/shared-Z5LN5APr.d.mts [22m[32m525.00 B[39m
|
|
21
|
+
[32mDTS[39m ⚡️ Build success in 5529ms
|
|
22
|
+
[32mDTS[39m [1mdist/client/index.d.ts [22m[32m155.00 B[39m
|
|
23
|
+
[32mDTS[39m [1mdist/server/index.d.ts [22m[32m684.00 B[39m
|
|
24
|
+
[32mDTS[39m [1mdist/shared/index.d.ts [22m[32m479.00 B[39m
|
|
25
|
+
[32mDTS[39m [1mdist/client/index.d.mts [22m[32m156.00 B[39m
|
|
26
|
+
[32mDTS[39m [1mdist/server/index.d.mts [22m[32m685.00 B[39m
|
|
27
|
+
[32mDTS[39m [1mdist/shared/index.d.mts [22m[32m479.00 B[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @wrelik/auth
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 818ed58: Refactor runtime packages to strict subpath exports (`/server`, `/client`, `/shared`) with side-effect free entrypoints and hard CI/runtime boundary enforcement.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [818ed58]
|
|
12
|
+
- @wrelik/errors@2.0.0
|
|
13
|
+
|
|
14
|
+
## 0.2.2
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 2b9cbf7: Fix auth role checks to reject malformed runtime role payloads and remove a duplicate TypeScript compiler option in errors to eliminate build warnings.
|
|
19
|
+
- Updated dependencies [2b9cbf7]
|
|
20
|
+
- @wrelik/errors@0.2.1
|
|
21
|
+
|
|
22
|
+
## Unreleased
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Fix role authorization checks to deny malformed runtime `roles` payloads.
|
|
27
|
+
- Add regression coverage to ensure malformed `roles` values cannot grant access.
|
|
28
|
+
|
|
3
29
|
## 0.2.1
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
|
@@ -17,37 +17,31 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
|
|
20
|
-
// src/
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
23
|
-
|
|
20
|
+
// src/client/index.ts
|
|
21
|
+
var client_exports = {};
|
|
22
|
+
__export(client_exports, {
|
|
23
|
+
mapClerkToSession: () => mapClerkToSession
|
|
24
24
|
});
|
|
25
|
-
module.exports = __toCommonJS(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
function
|
|
33
|
-
var _a, _b;
|
|
34
|
-
|
|
25
|
+
module.exports = __toCommonJS(client_exports);
|
|
26
|
+
function normalizeRoles(value) {
|
|
27
|
+
if (!Array.isArray(value)) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
return value.filter((role) => typeof role === "string");
|
|
31
|
+
}
|
|
32
|
+
function mapClerkToSession(input) {
|
|
33
|
+
var _a, _b, _c, _d;
|
|
34
|
+
const auth = input != null ? input : {};
|
|
35
|
+
if (!auth.userId) {
|
|
35
36
|
return { userId: null, tenantId: null, roles: [] };
|
|
36
37
|
}
|
|
37
|
-
const roles = ((_b = (_a = auth2.sessionClaims) == null ? void 0 : _a.publicMetadata) == null ? void 0 : _b.roles) || [];
|
|
38
38
|
return {
|
|
39
|
-
userId:
|
|
40
|
-
tenantId:
|
|
41
|
-
roles
|
|
39
|
+
userId: auth.userId,
|
|
40
|
+
tenantId: (_a = auth.orgId) != null ? _a : null,
|
|
41
|
+
roles: normalizeRoles((_d = (_c = (_b = auth.session) == null ? void 0 : _b.user) == null ? void 0 : _c.publicMetadata) == null ? void 0 : _d.roles)
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
// src/next.ts
|
|
46
|
-
function getSession() {
|
|
47
|
-
const clerkAuth = (0, import_nextjs.auth)();
|
|
48
|
-
return fromClerkAuth(clerkAuth);
|
|
49
|
-
}
|
|
50
44
|
// Annotate the CommonJS export names for ESM import in node:
|
|
51
45
|
0 && (module.exports = {
|
|
52
|
-
|
|
46
|
+
mapClerkToSession
|
|
53
47
|
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/client/index.ts
|
|
2
|
+
function normalizeRoles(value) {
|
|
3
|
+
if (!Array.isArray(value)) {
|
|
4
|
+
return [];
|
|
5
|
+
}
|
|
6
|
+
return value.filter((role) => typeof role === "string");
|
|
7
|
+
}
|
|
8
|
+
function mapClerkToSession(input) {
|
|
9
|
+
var _a, _b, _c, _d;
|
|
10
|
+
const auth = input != null ? input : {};
|
|
11
|
+
if (!auth.userId) {
|
|
12
|
+
return { userId: null, tenantId: null, roles: [] };
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
userId: auth.userId,
|
|
16
|
+
tenantId: (_a = auth.orgId) != null ? _a : null,
|
|
17
|
+
roles: normalizeRoles((_d = (_c = (_b = auth.session) == null ? void 0 : _b.user) == null ? void 0 : _c.publicMetadata) == null ? void 0 : _d.roles)
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
mapClerkToSession
|
|
22
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend';
|
|
2
|
+
import { WorkflowSession } from '../shared/index.mjs';
|
|
3
|
+
|
|
4
|
+
declare function fromClerkAuth(clerkAuth: SignedInAuthObject | SignedOutAuthObject | null): WorkflowSession;
|
|
5
|
+
declare function getSession(): WorkflowSession;
|
|
6
|
+
declare function requireUser(session: WorkflowSession | null): string;
|
|
7
|
+
declare function requireTenant(session: WorkflowSession | null): string;
|
|
8
|
+
declare function hasRole(session: WorkflowSession | null, role: string): boolean;
|
|
9
|
+
declare function requireRole(session: WorkflowSession | null, role: string): void;
|
|
10
|
+
|
|
11
|
+
export { fromClerkAuth, getSession, hasRole, requireRole, requireTenant, requireUser };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend';
|
|
2
|
+
import { WorkflowSession } from '../shared/index.js';
|
|
3
|
+
|
|
4
|
+
declare function fromClerkAuth(clerkAuth: SignedInAuthObject | SignedOutAuthObject | null): WorkflowSession;
|
|
5
|
+
declare function getSession(): WorkflowSession;
|
|
6
|
+
declare function requireUser(session: WorkflowSession | null): string;
|
|
7
|
+
declare function requireTenant(session: WorkflowSession | null): string;
|
|
8
|
+
declare function hasRole(session: WorkflowSession | null, role: string): boolean;
|
|
9
|
+
declare function requireRole(session: WorkflowSession | null, role: string): void;
|
|
10
|
+
|
|
11
|
+
export { fromClerkAuth, getSession, hasRole, requireRole, requireTenant, requireUser };
|
|
@@ -17,58 +17,66 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var
|
|
22
|
-
__export(
|
|
20
|
+
// src/server/index.ts
|
|
21
|
+
var server_exports = {};
|
|
22
|
+
__export(server_exports, {
|
|
23
23
|
fromClerkAuth: () => fromClerkAuth,
|
|
24
|
+
getSession: () => getSession,
|
|
24
25
|
hasRole: () => hasRole,
|
|
25
26
|
requireRole: () => requireRole,
|
|
26
27
|
requireTenant: () => requireTenant,
|
|
27
28
|
requireUser: () => requireUser
|
|
28
29
|
});
|
|
29
|
-
module.exports = __toCommonJS(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
module.exports = __toCommonJS(server_exports);
|
|
31
|
+
var import_nextjs = require("@clerk/nextjs");
|
|
32
|
+
var import_shared = require("@wrelik/errors/shared");
|
|
33
|
+
function normalizeRoles(value) {
|
|
34
|
+
if (!Array.isArray(value)) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
return value.filter((role) => typeof role === "string");
|
|
38
|
+
}
|
|
39
|
+
function fromClerkAuth(clerkAuth) {
|
|
40
|
+
var _a, _b, _c;
|
|
41
|
+
if (!(clerkAuth == null ? void 0 : clerkAuth.userId)) {
|
|
42
|
+
return { userId: null, tenantId: null, roles: [] };
|
|
43
|
+
}
|
|
44
|
+
const authLike = clerkAuth;
|
|
45
|
+
const claims = (_a = authLike.sessionClaims) != null ? _a : {};
|
|
46
|
+
return {
|
|
47
|
+
userId: clerkAuth.userId,
|
|
48
|
+
tenantId: (_b = clerkAuth.orgId) != null ? _b : null,
|
|
49
|
+
roles: normalizeRoles((_c = claims.publicMetadata) == null ? void 0 : _c.roles)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function getSession() {
|
|
53
|
+
return fromClerkAuth((0, import_nextjs.auth)());
|
|
54
|
+
}
|
|
33
55
|
function requireUser(session) {
|
|
34
|
-
if (!session
|
|
35
|
-
throw new
|
|
56
|
+
if (!(session == null ? void 0 : session.userId)) {
|
|
57
|
+
throw new import_shared.AuthRequiredError();
|
|
36
58
|
}
|
|
37
59
|
return session.userId;
|
|
38
60
|
}
|
|
39
61
|
function requireTenant(session) {
|
|
40
62
|
requireUser(session);
|
|
41
|
-
if (!session
|
|
42
|
-
throw new
|
|
63
|
+
if (!(session == null ? void 0 : session.tenantId)) {
|
|
64
|
+
throw new import_shared.TenantRequiredError();
|
|
43
65
|
}
|
|
44
66
|
return session.tenantId;
|
|
45
67
|
}
|
|
46
68
|
function hasRole(session, role) {
|
|
47
|
-
|
|
48
|
-
return session.roles.includes(role);
|
|
69
|
+
return Boolean((session == null ? void 0 : session.userId) && Array.isArray(session.roles) && session.roles.includes(role));
|
|
49
70
|
}
|
|
50
71
|
function requireRole(session, role) {
|
|
51
72
|
if (!hasRole(session, role)) {
|
|
52
|
-
throw new
|
|
73
|
+
throw new import_shared.PermissionDeniedError(`Role ${role} required`);
|
|
53
74
|
}
|
|
54
75
|
}
|
|
55
|
-
|
|
56
|
-
// src/index.ts
|
|
57
|
-
function fromClerkAuth(auth) {
|
|
58
|
-
var _a, _b;
|
|
59
|
-
if (!auth || !auth.userId) {
|
|
60
|
-
return { userId: null, tenantId: null, roles: [] };
|
|
61
|
-
}
|
|
62
|
-
const roles = ((_b = (_a = auth.sessionClaims) == null ? void 0 : _a.publicMetadata) == null ? void 0 : _b.roles) || [];
|
|
63
|
-
return {
|
|
64
|
-
userId: auth.userId,
|
|
65
|
-
tenantId: auth.orgId || null,
|
|
66
|
-
roles
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
76
|
// Annotate the CommonJS export names for ESM import in node:
|
|
70
77
|
0 && (module.exports = {
|
|
71
78
|
fromClerkAuth,
|
|
79
|
+
getSession,
|
|
72
80
|
hasRole,
|
|
73
81
|
requireRole,
|
|
74
82
|
requireTenant,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/server/index.ts
|
|
2
|
+
import { auth } from "@clerk/nextjs";
|
|
3
|
+
import { AuthRequiredError, PermissionDeniedError, TenantRequiredError } from "@wrelik/errors/shared";
|
|
4
|
+
function normalizeRoles(value) {
|
|
5
|
+
if (!Array.isArray(value)) {
|
|
6
|
+
return [];
|
|
7
|
+
}
|
|
8
|
+
return value.filter((role) => typeof role === "string");
|
|
9
|
+
}
|
|
10
|
+
function fromClerkAuth(clerkAuth) {
|
|
11
|
+
var _a, _b, _c;
|
|
12
|
+
if (!(clerkAuth == null ? void 0 : clerkAuth.userId)) {
|
|
13
|
+
return { userId: null, tenantId: null, roles: [] };
|
|
14
|
+
}
|
|
15
|
+
const authLike = clerkAuth;
|
|
16
|
+
const claims = (_a = authLike.sessionClaims) != null ? _a : {};
|
|
17
|
+
return {
|
|
18
|
+
userId: clerkAuth.userId,
|
|
19
|
+
tenantId: (_b = clerkAuth.orgId) != null ? _b : null,
|
|
20
|
+
roles: normalizeRoles((_c = claims.publicMetadata) == null ? void 0 : _c.roles)
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function getSession() {
|
|
24
|
+
return fromClerkAuth(auth());
|
|
25
|
+
}
|
|
26
|
+
function requireUser(session) {
|
|
27
|
+
if (!(session == null ? void 0 : session.userId)) {
|
|
28
|
+
throw new AuthRequiredError();
|
|
29
|
+
}
|
|
30
|
+
return session.userId;
|
|
31
|
+
}
|
|
32
|
+
function requireTenant(session) {
|
|
33
|
+
requireUser(session);
|
|
34
|
+
if (!(session == null ? void 0 : session.tenantId)) {
|
|
35
|
+
throw new TenantRequiredError();
|
|
36
|
+
}
|
|
37
|
+
return session.tenantId;
|
|
38
|
+
}
|
|
39
|
+
function hasRole(session, role) {
|
|
40
|
+
return Boolean((session == null ? void 0 : session.userId) && Array.isArray(session.roles) && session.roles.includes(role));
|
|
41
|
+
}
|
|
42
|
+
function requireRole(session, role) {
|
|
43
|
+
if (!hasRole(session, role)) {
|
|
44
|
+
throw new PermissionDeniedError(`Role ${role} required`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
fromClerkAuth,
|
|
49
|
+
getSession,
|
|
50
|
+
hasRole,
|
|
51
|
+
requireRole,
|
|
52
|
+
requireTenant,
|
|
53
|
+
requireUser
|
|
54
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface WorkflowSession {
|
|
2
|
+
userId: string | null;
|
|
3
|
+
tenantId: string | null;
|
|
4
|
+
roles: string[];
|
|
5
|
+
}
|
|
6
|
+
interface ClerkSessionLike {
|
|
7
|
+
userId?: string | null;
|
|
8
|
+
orgId?: string | null;
|
|
9
|
+
sessionClaims?: unknown;
|
|
10
|
+
}
|
|
11
|
+
interface ClerkClientSessionLike {
|
|
12
|
+
userId?: string | null;
|
|
13
|
+
orgId?: string | null;
|
|
14
|
+
session?: {
|
|
15
|
+
user?: {
|
|
16
|
+
publicMetadata?: unknown;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type { ClerkClientSessionLike, ClerkSessionLike, WorkflowSession };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface WorkflowSession {
|
|
2
|
+
userId: string | null;
|
|
3
|
+
tenantId: string | null;
|
|
4
|
+
roles: string[];
|
|
5
|
+
}
|
|
6
|
+
interface ClerkSessionLike {
|
|
7
|
+
userId?: string | null;
|
|
8
|
+
orgId?: string | null;
|
|
9
|
+
sessionClaims?: unknown;
|
|
10
|
+
}
|
|
11
|
+
interface ClerkClientSessionLike {
|
|
12
|
+
userId?: string | null;
|
|
13
|
+
orgId?: string | null;
|
|
14
|
+
session?: {
|
|
15
|
+
user?: {
|
|
16
|
+
publicMetadata?: unknown;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type { ClerkClientSessionLike, ClerkSessionLike, WorkflowSession };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/shared/index.ts
|
|
17
|
+
var shared_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(shared_exports);
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrelik/auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -9,16 +9,28 @@
|
|
|
9
9
|
"url": "https://github.com/lwhite702/wrelik-kit.git",
|
|
10
10
|
"directory": "packages/auth"
|
|
11
11
|
},
|
|
12
|
-
"
|
|
13
|
-
"types": "./dist/index.d.ts",
|
|
12
|
+
"sideEffects": false,
|
|
14
13
|
"exports": {
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
"./server": {
|
|
15
|
+
"types": "./dist/server/index.d.ts",
|
|
16
|
+
"import": "./dist/server/index.mjs",
|
|
17
|
+
"require": "./dist/server/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./client": {
|
|
20
|
+
"types": "./dist/client/index.d.ts",
|
|
21
|
+
"import": "./dist/client/index.mjs",
|
|
22
|
+
"require": "./dist/client/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./shared": {
|
|
25
|
+
"types": "./dist/shared/index.d.ts",
|
|
26
|
+
"import": "./dist/shared/index.mjs",
|
|
27
|
+
"require": "./dist/shared/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./package.json": "./package.json"
|
|
18
30
|
},
|
|
19
31
|
"dependencies": {
|
|
20
32
|
"@clerk/backend": "^0.38.0",
|
|
21
|
-
"@wrelik/errors": "
|
|
33
|
+
"@wrelik/errors": "2.0.0"
|
|
22
34
|
},
|
|
23
35
|
"peerDependencies": {
|
|
24
36
|
"@clerk/nextjs": "^4.29.0"
|
|
@@ -29,9 +41,17 @@
|
|
|
29
41
|
"@wrelik/eslint-config": "0.1.1",
|
|
30
42
|
"@wrelik/tsconfig": "0.1.1"
|
|
31
43
|
},
|
|
44
|
+
"wrelik": {
|
|
45
|
+
"runtimes": [
|
|
46
|
+
"server",
|
|
47
|
+
"client",
|
|
48
|
+
"shared"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
32
51
|
"scripts": {
|
|
33
|
-
"build": "tsup src/index.ts src/
|
|
52
|
+
"build": "tsup src/server/index.ts src/client/index.ts src/shared/index.ts --format cjs,esm --dts --clean",
|
|
34
53
|
"lint": "eslint src/",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
35
55
|
"test": "vitest run --passWithNoTests"
|
|
36
56
|
}
|
|
37
57
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { mapClerkToSession } from './index';
|
|
3
|
+
|
|
4
|
+
describe('@wrelik/auth/client contract', () => {
|
|
5
|
+
it('normalizes mixed roles from Clerk client payload', () => {
|
|
6
|
+
const session = mapClerkToSession({
|
|
7
|
+
userId: 'u1',
|
|
8
|
+
orgId: 't1',
|
|
9
|
+
session: {
|
|
10
|
+
user: {
|
|
11
|
+
publicMetadata: {
|
|
12
|
+
roles: ['admin', 42, 'owner'],
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
expect(session).toEqual({
|
|
19
|
+
userId: 'u1',
|
|
20
|
+
tenantId: 't1',
|
|
21
|
+
roles: ['admin', 'owner'],
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { mapClerkToSession } from './index';
|
|
3
|
+
|
|
4
|
+
describe('@wrelik/auth/client', () => {
|
|
5
|
+
it('maps unknown auth input to a safe empty session', () => {
|
|
6
|
+
expect(mapClerkToSession(null)).toEqual({ userId: null, tenantId: null, roles: [] });
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('maps role metadata without permission checks', () => {
|
|
10
|
+
const session = mapClerkToSession({
|
|
11
|
+
userId: 'u1',
|
|
12
|
+
orgId: 'o1',
|
|
13
|
+
session: { user: { publicMetadata: { roles: ['admin', 1] } } },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
expect(session).toEqual({ userId: 'u1', tenantId: 'o1', roles: ['admin'] });
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ClerkClientSessionLike, WorkflowSession } from '../shared';
|
|
2
|
+
|
|
3
|
+
function normalizeRoles(value: unknown): string[] {
|
|
4
|
+
if (!Array.isArray(value)) {
|
|
5
|
+
return [];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
return value.filter((role): role is string => typeof role === 'string');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function mapClerkToSession(input: unknown): WorkflowSession {
|
|
12
|
+
const auth = (input ?? {}) as ClerkClientSessionLike;
|
|
13
|
+
|
|
14
|
+
if (!auth.userId) {
|
|
15
|
+
return { userId: null, tenantId: null, roles: [] };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
userId: auth.userId,
|
|
20
|
+
tenantId: auth.orgId ?? null,
|
|
21
|
+
roles: normalizeRoles((auth.session?.user?.publicMetadata as { roles?: unknown } | undefined)?.roles),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { AuthRequiredError, PermissionDeniedError, TenantRequiredError } from '@wrelik/errors/shared';
|
|
3
|
+
import { fromClerkAuth, hasRole, requireRole, requireTenant, requireUser } from './index';
|
|
4
|
+
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
vi.resetModules();
|
|
7
|
+
vi.clearAllMocks();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('@wrelik/auth/server', () => {
|
|
11
|
+
it('requires authenticated users and tenant contexts', () => {
|
|
12
|
+
expect(() => requireUser(null)).toThrow(AuthRequiredError);
|
|
13
|
+
expect(() => requireTenant({ userId: 'u1', tenantId: null, roles: [] })).toThrow(TenantRequiredError);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('checks role membership with runtime-shape safety', () => {
|
|
17
|
+
expect(hasRole({ userId: 'u1', tenantId: 't1', roles: ['admin'] }, 'admin')).toBe(true);
|
|
18
|
+
expect(() => requireRole({ userId: 'u1', tenantId: 't1', roles: [] }, 'admin')).toThrow(
|
|
19
|
+
PermissionDeniedError,
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('maps Clerk auth payloads defensively', () => {
|
|
24
|
+
const session = fromClerkAuth({
|
|
25
|
+
userId: 'u1',
|
|
26
|
+
orgId: 'o1',
|
|
27
|
+
sessionClaims: { publicMetadata: { roles: ['admin', 1] } },
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
} as any);
|
|
30
|
+
|
|
31
|
+
expect(session).toEqual({ userId: 'u1', tenantId: 'o1', roles: ['admin'] });
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('retrieves and normalizes sessions from Clerk auth()', async () => {
|
|
35
|
+
vi.resetModules();
|
|
36
|
+
vi.doMock('@clerk/nextjs', () => ({
|
|
37
|
+
auth: () => ({
|
|
38
|
+
userId: 'u1',
|
|
39
|
+
orgId: 'o1',
|
|
40
|
+
sessionClaims: { publicMetadata: { roles: ['admin', 123] } },
|
|
41
|
+
}),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
const mod = await import('./index');
|
|
45
|
+
expect(mod.getSession()).toEqual({ userId: 'u1', tenantId: 'o1', roles: ['admin'] });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('returns anonymous session when Clerk auth() is signed out', async () => {
|
|
49
|
+
vi.resetModules();
|
|
50
|
+
vi.doMock('@clerk/nextjs', () => ({
|
|
51
|
+
auth: () => ({ userId: null, orgId: null, sessionClaims: {} }),
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
const mod = await import('./index');
|
|
55
|
+
expect(mod.getSession()).toEqual({ userId: null, tenantId: null, roles: [] });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('does not expose a root package import', () => {
|
|
59
|
+
expect(() => require.resolve('@wrelik/auth')).toThrow();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-imports */
|
|
2
|
+
import type { SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend';
|
|
3
|
+
import { auth } from '@clerk/nextjs';
|
|
4
|
+
import { AuthRequiredError, PermissionDeniedError, TenantRequiredError } from '@wrelik/errors/shared';
|
|
5
|
+
import type { ClerkSessionLike, WorkflowSession } from '../shared';
|
|
6
|
+
|
|
7
|
+
function normalizeRoles(value: unknown): string[] {
|
|
8
|
+
if (!Array.isArray(value)) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return value.filter((role): role is string => typeof role === 'string');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function fromClerkAuth(clerkAuth: SignedInAuthObject | SignedOutAuthObject | null): WorkflowSession {
|
|
16
|
+
if (!clerkAuth?.userId) {
|
|
17
|
+
return { userId: null, tenantId: null, roles: [] };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const authLike: ClerkSessionLike = clerkAuth;
|
|
21
|
+
const claims = (authLike.sessionClaims as { publicMetadata?: { roles?: unknown } } | undefined) ?? {};
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
userId: clerkAuth.userId,
|
|
25
|
+
tenantId: clerkAuth.orgId ?? null,
|
|
26
|
+
roles: normalizeRoles(claims.publicMetadata?.roles),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getSession(): WorkflowSession {
|
|
31
|
+
return fromClerkAuth(auth());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function requireUser(session: WorkflowSession | null): string {
|
|
35
|
+
if (!session?.userId) {
|
|
36
|
+
throw new AuthRequiredError();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return session.userId;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function requireTenant(session: WorkflowSession | null): string {
|
|
43
|
+
requireUser(session);
|
|
44
|
+
|
|
45
|
+
if (!session?.tenantId) {
|
|
46
|
+
throw new TenantRequiredError();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return session.tenantId;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function hasRole(session: WorkflowSession | null, role: string): boolean {
|
|
53
|
+
return Boolean(session?.userId && Array.isArray(session.roles) && session.roles.includes(role));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function requireRole(session: WorkflowSession | null, role: string): void {
|
|
57
|
+
if (!hasRole(session, role)) {
|
|
58
|
+
throw new PermissionDeniedError(`Role ${role} required`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { WorkflowSession, ClerkSessionLike, ClerkClientSessionLike } from './types';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface WorkflowSession {
|
|
2
|
+
userId: string | null;
|
|
3
|
+
tenantId: string | null;
|
|
4
|
+
roles: string[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface ClerkSessionLike {
|
|
8
|
+
userId?: string | null;
|
|
9
|
+
orgId?: string | null;
|
|
10
|
+
sessionClaims?: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ClerkClientSessionLike {
|
|
14
|
+
userId?: string | null;
|
|
15
|
+
orgId?: string | null;
|
|
16
|
+
session?: {
|
|
17
|
+
user?: {
|
|
18
|
+
publicMetadata?: unknown;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
}
|
package/dist/chunk-5WLTDLEP.mjs
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// src/shared.ts
|
|
2
|
-
import { AuthRequiredError, PermissionDeniedError, TenantRequiredError } from "@wrelik/errors";
|
|
3
|
-
function requireUser(session) {
|
|
4
|
-
if (!session || !session.userId) {
|
|
5
|
-
throw new AuthRequiredError();
|
|
6
|
-
}
|
|
7
|
-
return session.userId;
|
|
8
|
-
}
|
|
9
|
-
function requireTenant(session) {
|
|
10
|
-
requireUser(session);
|
|
11
|
-
if (!session || !session.tenantId) {
|
|
12
|
-
throw new TenantRequiredError();
|
|
13
|
-
}
|
|
14
|
-
return session.tenantId;
|
|
15
|
-
}
|
|
16
|
-
function hasRole(session, role) {
|
|
17
|
-
if (!session || !session.userId) return false;
|
|
18
|
-
return session.roles.includes(role);
|
|
19
|
-
}
|
|
20
|
-
function requireRole(session, role) {
|
|
21
|
-
if (!hasRole(session, role)) {
|
|
22
|
-
throw new PermissionDeniedError(`Role ${role} required`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export {
|
|
27
|
-
requireUser,
|
|
28
|
-
requireTenant,
|
|
29
|
-
hasRole,
|
|
30
|
-
requireRole
|
|
31
|
-
};
|
package/dist/chunk-HWIZ2Q5U.mjs
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// src/index.ts
|
|
2
|
-
function fromClerkAuth(auth) {
|
|
3
|
-
var _a, _b;
|
|
4
|
-
if (!auth || !auth.userId) {
|
|
5
|
-
return { userId: null, tenantId: null, roles: [] };
|
|
6
|
-
}
|
|
7
|
-
const roles = ((_b = (_a = auth.sessionClaims) == null ? void 0 : _a.publicMetadata) == null ? void 0 : _b.roles) || [];
|
|
8
|
-
return {
|
|
9
|
-
userId: auth.userId,
|
|
10
|
-
tenantId: auth.orgId || null,
|
|
11
|
-
roles
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export {
|
|
16
|
-
fromClerkAuth
|
|
17
|
-
};
|
package/dist/index.d.mts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend';
|
|
2
|
-
import { W as WorkflowSession } from './shared-Z5LN5APr.mjs';
|
|
3
|
-
export { h as hasRole, r as requireRole, a as requireTenant, b as requireUser } from './shared-Z5LN5APr.mjs';
|
|
4
|
-
|
|
5
|
-
declare function fromClerkAuth(auth: SignedInAuthObject | SignedOutAuthObject | null): WorkflowSession;
|
|
6
|
-
|
|
7
|
-
export { WorkflowSession, fromClerkAuth };
|
package/dist/index.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend';
|
|
2
|
-
import { W as WorkflowSession } from './shared-Z5LN5APr.js';
|
|
3
|
-
export { h as hasRole, r as requireRole, a as requireTenant, b as requireUser } from './shared-Z5LN5APr.js';
|
|
4
|
-
|
|
5
|
-
declare function fromClerkAuth(auth: SignedInAuthObject | SignedOutAuthObject | null): WorkflowSession;
|
|
6
|
-
|
|
7
|
-
export { WorkflowSession, fromClerkAuth };
|
package/dist/index.mjs
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
fromClerkAuth
|
|
3
|
-
} from "./chunk-HWIZ2Q5U.mjs";
|
|
4
|
-
import {
|
|
5
|
-
hasRole,
|
|
6
|
-
requireRole,
|
|
7
|
-
requireTenant,
|
|
8
|
-
requireUser
|
|
9
|
-
} from "./chunk-5WLTDLEP.mjs";
|
|
10
|
-
export {
|
|
11
|
-
fromClerkAuth,
|
|
12
|
-
hasRole,
|
|
13
|
-
requireRole,
|
|
14
|
-
requireTenant,
|
|
15
|
-
requireUser
|
|
16
|
-
};
|
package/dist/next.d.mts
DELETED
package/dist/next.d.ts
DELETED
package/dist/next.mjs
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
fromClerkAuth
|
|
3
|
-
} from "./chunk-HWIZ2Q5U.mjs";
|
|
4
|
-
import "./chunk-5WLTDLEP.mjs";
|
|
5
|
-
|
|
6
|
-
// src/next.ts
|
|
7
|
-
import { auth } from "@clerk/nextjs";
|
|
8
|
-
function getSession() {
|
|
9
|
-
const clerkAuth = auth();
|
|
10
|
-
return fromClerkAuth(clerkAuth);
|
|
11
|
-
}
|
|
12
|
-
export {
|
|
13
|
-
getSession
|
|
14
|
-
};
|
package/dist/react-native.d.mts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { W as WorkflowSession } from './shared-Z5LN5APr.mjs';
|
|
2
|
-
export { h as hasRole, r as requireRole, a as requireTenant, b as requireUser } from './shared-Z5LN5APr.mjs';
|
|
3
|
-
|
|
4
|
-
declare function mapClerkToSession(auth: {
|
|
5
|
-
userId?: string | null;
|
|
6
|
-
orgId?: string | null;
|
|
7
|
-
session?: {
|
|
8
|
-
user?: {
|
|
9
|
-
publicMetadata?: any;
|
|
10
|
-
};
|
|
11
|
-
};
|
|
12
|
-
} | null): WorkflowSession;
|
|
13
|
-
|
|
14
|
-
export { WorkflowSession, mapClerkToSession };
|
package/dist/react-native.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { W as WorkflowSession } from './shared-Z5LN5APr.js';
|
|
2
|
-
export { h as hasRole, r as requireRole, a as requireTenant, b as requireUser } from './shared-Z5LN5APr.js';
|
|
3
|
-
|
|
4
|
-
declare function mapClerkToSession(auth: {
|
|
5
|
-
userId?: string | null;
|
|
6
|
-
orgId?: string | null;
|
|
7
|
-
session?: {
|
|
8
|
-
user?: {
|
|
9
|
-
publicMetadata?: any;
|
|
10
|
-
};
|
|
11
|
-
};
|
|
12
|
-
} | null): WorkflowSession;
|
|
13
|
-
|
|
14
|
-
export { WorkflowSession, mapClerkToSession };
|
package/dist/react-native.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/react-native.ts
|
|
21
|
-
var react_native_exports = {};
|
|
22
|
-
__export(react_native_exports, {
|
|
23
|
-
hasRole: () => hasRole,
|
|
24
|
-
mapClerkToSession: () => mapClerkToSession,
|
|
25
|
-
requireRole: () => requireRole,
|
|
26
|
-
requireTenant: () => requireTenant,
|
|
27
|
-
requireUser: () => requireUser
|
|
28
|
-
});
|
|
29
|
-
module.exports = __toCommonJS(react_native_exports);
|
|
30
|
-
|
|
31
|
-
// src/shared.ts
|
|
32
|
-
var import_errors = require("@wrelik/errors");
|
|
33
|
-
function requireUser(session) {
|
|
34
|
-
if (!session || !session.userId) {
|
|
35
|
-
throw new import_errors.AuthRequiredError();
|
|
36
|
-
}
|
|
37
|
-
return session.userId;
|
|
38
|
-
}
|
|
39
|
-
function requireTenant(session) {
|
|
40
|
-
requireUser(session);
|
|
41
|
-
if (!session || !session.tenantId) {
|
|
42
|
-
throw new import_errors.TenantRequiredError();
|
|
43
|
-
}
|
|
44
|
-
return session.tenantId;
|
|
45
|
-
}
|
|
46
|
-
function hasRole(session, role) {
|
|
47
|
-
if (!session || !session.userId) return false;
|
|
48
|
-
return session.roles.includes(role);
|
|
49
|
-
}
|
|
50
|
-
function requireRole(session, role) {
|
|
51
|
-
if (!hasRole(session, role)) {
|
|
52
|
-
throw new import_errors.PermissionDeniedError(`Role ${role} required`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// src/react-native.ts
|
|
57
|
-
function mapClerkToSession(auth) {
|
|
58
|
-
var _a, _b, _c;
|
|
59
|
-
if (!auth || !auth.userId) {
|
|
60
|
-
return { userId: null, tenantId: null, roles: [] };
|
|
61
|
-
}
|
|
62
|
-
const roles = ((_c = (_b = (_a = auth.session) == null ? void 0 : _a.user) == null ? void 0 : _b.publicMetadata) == null ? void 0 : _c.roles) || [];
|
|
63
|
-
return {
|
|
64
|
-
userId: auth.userId,
|
|
65
|
-
tenantId: auth.orgId || null,
|
|
66
|
-
roles
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
70
|
-
0 && (module.exports = {
|
|
71
|
-
hasRole,
|
|
72
|
-
mapClerkToSession,
|
|
73
|
-
requireRole,
|
|
74
|
-
requireTenant,
|
|
75
|
-
requireUser
|
|
76
|
-
});
|
package/dist/react-native.mjs
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
hasRole,
|
|
3
|
-
requireRole,
|
|
4
|
-
requireTenant,
|
|
5
|
-
requireUser
|
|
6
|
-
} from "./chunk-5WLTDLEP.mjs";
|
|
7
|
-
|
|
8
|
-
// src/react-native.ts
|
|
9
|
-
function mapClerkToSession(auth) {
|
|
10
|
-
var _a, _b, _c;
|
|
11
|
-
if (!auth || !auth.userId) {
|
|
12
|
-
return { userId: null, tenantId: null, roles: [] };
|
|
13
|
-
}
|
|
14
|
-
const roles = ((_c = (_b = (_a = auth.session) == null ? void 0 : _a.user) == null ? void 0 : _b.publicMetadata) == null ? void 0 : _c.roles) || [];
|
|
15
|
-
return {
|
|
16
|
-
userId: auth.userId,
|
|
17
|
-
tenantId: auth.orgId || null,
|
|
18
|
-
roles
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
export {
|
|
22
|
-
hasRole,
|
|
23
|
-
mapClerkToSession,
|
|
24
|
-
requireRole,
|
|
25
|
-
requireTenant,
|
|
26
|
-
requireUser
|
|
27
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
interface WorkflowSession {
|
|
2
|
-
userId: string | null;
|
|
3
|
-
tenantId: string | null;
|
|
4
|
-
roles: string[];
|
|
5
|
-
}
|
|
6
|
-
declare function requireUser(session: WorkflowSession | null): string;
|
|
7
|
-
declare function requireTenant(session: WorkflowSession | null): string;
|
|
8
|
-
declare function hasRole(session: WorkflowSession | null, role: string): boolean;
|
|
9
|
-
declare function requireRole(session: WorkflowSession | null, role: string): void;
|
|
10
|
-
|
|
11
|
-
export { type WorkflowSession as W, requireTenant as a, requireUser as b, hasRole as h, requireRole as r };
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
interface WorkflowSession {
|
|
2
|
-
userId: string | null;
|
|
3
|
-
tenantId: string | null;
|
|
4
|
-
roles: string[];
|
|
5
|
-
}
|
|
6
|
-
declare function requireUser(session: WorkflowSession | null): string;
|
|
7
|
-
declare function requireTenant(session: WorkflowSession | null): string;
|
|
8
|
-
declare function hasRole(session: WorkflowSession | null, role: string): boolean;
|
|
9
|
-
declare function requireRole(session: WorkflowSession | null, role: string): void;
|
|
10
|
-
|
|
11
|
-
export { type WorkflowSession as W, requireTenant as a, requireUser as b, hasRole as h, requireRole as r };
|
package/src/index.test.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { requireUser, fromClerkAuth } from './index';
|
|
3
|
-
import { AuthRequiredError } from '@wrelik/errors';
|
|
4
|
-
|
|
5
|
-
describe('@wrelik/auth', () => {
|
|
6
|
-
it('should throw AuthRequiredError if user is null', () => {
|
|
7
|
-
const session = { userId: null, tenantId: null, roles: [] };
|
|
8
|
-
expect(() => requireUser(session)).toThrow(AuthRequiredError);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('should return userId if session is valid', () => {
|
|
12
|
-
const session = { userId: 'user_123', tenantId: null, roles: [] };
|
|
13
|
-
expect(requireUser(session)).toBe('user_123');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('should correctly parse Clerk auth object', () => {
|
|
17
|
-
const clerkAuth = {
|
|
18
|
-
userId: 'user_123',
|
|
19
|
-
orgId: 'org_456',
|
|
20
|
-
sessionClaims: { publicMetadata: { roles: ['admin'] } },
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
-
} as any;
|
|
23
|
-
|
|
24
|
-
const session = fromClerkAuth(clerkAuth);
|
|
25
|
-
expect(session.userId).toBe('user_123');
|
|
26
|
-
expect(session.tenantId).toBe('org_456');
|
|
27
|
-
expect(session.roles).toContain('admin');
|
|
28
|
-
});
|
|
29
|
-
});
|
package/src/index.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend';
|
|
2
|
-
import { type WorkflowSession } from './shared';
|
|
3
|
-
|
|
4
|
-
export * from './shared';
|
|
5
|
-
|
|
6
|
-
export function fromClerkAuth(
|
|
7
|
-
auth: SignedInAuthObject | SignedOutAuthObject | null,
|
|
8
|
-
): WorkflowSession {
|
|
9
|
-
if (!auth || !auth.userId) {
|
|
10
|
-
return { userId: null, tenantId: null, roles: [] };
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
-
const roles = ((auth.sessionClaims?.publicMetadata as any)?.roles as string[]) || [];
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
userId: auth.userId,
|
|
18
|
-
tenantId: auth.orgId || null,
|
|
19
|
-
roles,
|
|
20
|
-
};
|
|
21
|
-
}
|
package/src/next.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-restricted-imports */
|
|
2
|
-
import { auth } from '@clerk/nextjs';
|
|
3
|
-
import { fromClerkAuth, type WorkflowSession } from './index';
|
|
4
|
-
|
|
5
|
-
export function getSession(): WorkflowSession {
|
|
6
|
-
const clerkAuth = auth();
|
|
7
|
-
return fromClerkAuth(clerkAuth);
|
|
8
|
-
}
|
package/src/react-native.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { type WorkflowSession } from './shared';
|
|
2
|
-
|
|
3
|
-
export * from './shared';
|
|
4
|
-
|
|
5
|
-
// We accept the `useAuth` or `useUser` return value or similar from Clerk Expo
|
|
6
|
-
// but to be loose coupling, we defined it as any with some shape checks if needed.
|
|
7
|
-
// However, the requirement says "converts the Clerk Expo session/user representation".
|
|
8
|
-
export function mapClerkToSession(
|
|
9
|
-
auth: {
|
|
10
|
-
userId?: string | null;
|
|
11
|
-
orgId?: string | null;
|
|
12
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
-
session?: { user?: { publicMetadata?: any } };
|
|
14
|
-
} | null,
|
|
15
|
-
): WorkflowSession {
|
|
16
|
-
if (!auth || !auth.userId) {
|
|
17
|
-
return { userId: null, tenantId: null, roles: [] };
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// The roles might be in publicMetadata of the user object in session
|
|
21
|
-
const roles = (auth.session?.user?.publicMetadata?.roles as string[]) || [];
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
userId: auth.userId,
|
|
25
|
-
tenantId: auth.orgId || null,
|
|
26
|
-
roles,
|
|
27
|
-
};
|
|
28
|
-
}
|
package/src/shared.test.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { requireUser, requireTenant, hasRole } from './shared';
|
|
3
|
-
|
|
4
|
-
describe('Auth Shared', () => {
|
|
5
|
-
it('requireUser throws if no user', () => {
|
|
6
|
-
expect(() => requireUser(null)).toThrow('Authentication required');
|
|
7
|
-
expect(() => requireUser({ userId: null, tenantId: null, roles: [] })).toThrow(
|
|
8
|
-
'Authentication required',
|
|
9
|
-
);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('requireUser returns userId', () => {
|
|
13
|
-
expect(requireUser({ userId: 'u1', tenantId: null, roles: [] })).toBe('u1');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('requireTenant throws if no tenant', () => {
|
|
17
|
-
expect(() => requireTenant({ userId: 'u1', tenantId: null, roles: [] })).toThrow(
|
|
18
|
-
'Tenant context required',
|
|
19
|
-
);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('hasRole checks roles', () => {
|
|
23
|
-
const session = { userId: 'u1', tenantId: 't1', roles: ['admin'] };
|
|
24
|
-
expect(hasRole(session, 'admin')).toBe(true);
|
|
25
|
-
expect(hasRole(session, 'user')).toBe(false);
|
|
26
|
-
});
|
|
27
|
-
});
|
package/src/shared.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { AuthRequiredError, PermissionDeniedError, TenantRequiredError } from '@wrelik/errors';
|
|
2
|
-
|
|
3
|
-
export interface WorkflowSession {
|
|
4
|
-
userId: string | null;
|
|
5
|
-
tenantId: string | null;
|
|
6
|
-
roles: string[];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function requireUser(session: WorkflowSession | null): string {
|
|
10
|
-
if (!session || !session.userId) {
|
|
11
|
-
throw new AuthRequiredError();
|
|
12
|
-
}
|
|
13
|
-
return session.userId;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function requireTenant(session: WorkflowSession | null): string {
|
|
17
|
-
requireUser(session);
|
|
18
|
-
if (!session || !session.tenantId) {
|
|
19
|
-
throw new TenantRequiredError();
|
|
20
|
-
}
|
|
21
|
-
return session.tenantId;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function hasRole(session: WorkflowSession | null, role: string): boolean {
|
|
25
|
-
if (!session || !session.userId) return false;
|
|
26
|
-
return session.roles.includes(role);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function requireRole(session: WorkflowSession | null, role: string) {
|
|
30
|
-
if (!hasRole(session, role)) {
|
|
31
|
-
throw new PermissionDeniedError(`Role ${role} required`);
|
|
32
|
-
}
|
|
33
|
-
}
|