@getstrata/core 0.5.79 → 0.5.81
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/CHANGELOG.md +8 -0
- package/README.md +1 -1
- package/dist/core/auth/intendedUrlCookie.d.ts +10 -0
- package/dist/core/runtime/appKeyPrefix.d.ts +3 -1
- package/dist/entries/audit/exportAuditLogs.js +6 -0
- package/dist/entries/audit/siemFormatter.js +6 -0
- package/dist/entries/auth/intendedUrlCookie.js +176 -0
- package/dist/entries/auth/oauth/providers.js +6 -0
- package/dist/entries/auth/oauth/samlProvider.js +6 -0
- package/dist/entries/auth/passwordConfirmCookie.js +58 -2
- package/dist/entries/auth/sessionCookie.js +58 -2
- package/dist/entries/auth/sessionGuard.js +58 -2
- package/dist/entries/auth/tokenHash.js +58 -2
- package/dist/entries/cache/createCacheStore.js +6 -0
- package/dist/entries/facades.js +6 -0
- package/dist/entries/http/csrfMiddleware.js +56 -2
- package/dist/entries/http/csrfToken.js +56 -2
- package/dist/entries/http/flashMiddleware.js +58 -2
- package/dist/entries/http/flashSession.js +58 -2
- package/dist/entries/http/loginThrottleMiddleware.js +6 -0
- package/dist/entries/http/memoryThrottleMiddleware.js +6 -0
- package/dist/entries/http/requireVerifiedMiddleware.js +7 -1
- package/dist/entries/http/scimThrottleMiddleware.js +6 -0
- package/dist/entries/http/securityHeadersMiddleware.js +6 -0
- package/dist/entries/http/signedUrl.js +57 -1
- package/dist/entries/http/throttleMiddleware.js +6 -0
- package/dist/entries/jobs/exportAuditLogsJob.js +6 -0
- package/dist/entries/mail/mailer.js +6 -0
- package/dist/entries/openapi/generator.js +6 -0
- package/dist/entries/queue/createAppQueue.js +6 -0
- package/dist/entries/queue/publicQueue.js +6 -0
- package/dist/entries/queue/queueMetrics.js +6 -0
- package/dist/entries/queue/redisQueue.js +6 -0
- package/dist/entries/runtime/appKeyPrefix.js +8 -0
- package/dist/entries/security/oauthState.js +57 -1
- package/dist/entries/security/safeFetch.js +6 -0
- package/dist/entries/tracing/tracingMiddleware.js +6 -0
- package/dist/entries/view.js +58 -4
- package/dist/index.js +11 -7
- package/package.json +7 -2
|
@@ -5,6 +5,60 @@ import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
|
5
5
|
// ../../src/core/http/csrfToken.ts
|
|
6
6
|
import { timingSafeEqual } from "crypto";
|
|
7
7
|
|
|
8
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
9
|
+
function appKeyPrefix() {
|
|
10
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
11
|
+
}
|
|
12
|
+
function appCookieName(kind) {
|
|
13
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
14
|
+
}
|
|
15
|
+
function appDevSecret(kind) {
|
|
16
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
17
|
+
}
|
|
18
|
+
function namespacedRedisKey(kind) {
|
|
19
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
20
|
+
}
|
|
21
|
+
function smtpEhloHost() {
|
|
22
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
23
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
24
|
+
return safe || "strata.local";
|
|
25
|
+
}
|
|
26
|
+
function siemEventType() {
|
|
27
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
28
|
+
}
|
|
29
|
+
function appUserAgent() {
|
|
30
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
31
|
+
}
|
|
32
|
+
function otelServiceName() {
|
|
33
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
34
|
+
}
|
|
35
|
+
function webhookSignatureHeader() {
|
|
36
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
37
|
+
}
|
|
38
|
+
function appDisplayName() {
|
|
39
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
40
|
+
}
|
|
41
|
+
function appEnv() {
|
|
42
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
43
|
+
}
|
|
44
|
+
function appUrl() {
|
|
45
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
46
|
+
}
|
|
47
|
+
function apiPrefix() {
|
|
48
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
49
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
50
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
51
|
+
return trimmed || "/api/v1";
|
|
52
|
+
}
|
|
53
|
+
function sdkClientClassName() {
|
|
54
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
55
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
56
|
+
return override;
|
|
57
|
+
}
|
|
58
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
59
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
60
|
+
}
|
|
61
|
+
|
|
8
62
|
// ../../src/core/http/cookies.ts
|
|
9
63
|
function readRequestCookie(request, name) {
|
|
10
64
|
const cookies = request.cookies;
|
|
@@ -63,10 +117,10 @@ function currentRequestMeta() {
|
|
|
63
117
|
var CSRF_COOKIE = "workhub_csrf";
|
|
64
118
|
var CSRF_TTL_MS = 60 * 60 * 1000;
|
|
65
119
|
function csrfCookieName() {
|
|
66
|
-
return process.env.CSRF_COOKIE_NAME?.trim() ||
|
|
120
|
+
return process.env.CSRF_COOKIE_NAME?.trim() || appCookieName("csrf");
|
|
67
121
|
}
|
|
68
122
|
function resolveCsrfSecret() {
|
|
69
|
-
return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || "
|
|
123
|
+
return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || appDevSecret("csrf-secret");
|
|
70
124
|
}
|
|
71
125
|
function csrfVerifyOptions() {
|
|
72
126
|
return { secret: resolveCsrfSecret(), maxAge: CSRF_TTL_MS };
|
|
@@ -2,6 +2,60 @@
|
|
|
2
2
|
// ../../src/core/http/csrfToken.ts
|
|
3
3
|
import { timingSafeEqual } from "crypto";
|
|
4
4
|
|
|
5
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
|
+
function appKeyPrefix() {
|
|
7
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
|
+
}
|
|
9
|
+
function appCookieName(kind) {
|
|
10
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function appDevSecret(kind) {
|
|
13
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
|
+
}
|
|
15
|
+
function namespacedRedisKey(kind) {
|
|
16
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
17
|
+
}
|
|
18
|
+
function smtpEhloHost() {
|
|
19
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
20
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
21
|
+
return safe || "strata.local";
|
|
22
|
+
}
|
|
23
|
+
function siemEventType() {
|
|
24
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
25
|
+
}
|
|
26
|
+
function appUserAgent() {
|
|
27
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
28
|
+
}
|
|
29
|
+
function otelServiceName() {
|
|
30
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
31
|
+
}
|
|
32
|
+
function webhookSignatureHeader() {
|
|
33
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
34
|
+
}
|
|
35
|
+
function appDisplayName() {
|
|
36
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
37
|
+
}
|
|
38
|
+
function appEnv() {
|
|
39
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
40
|
+
}
|
|
41
|
+
function appUrl() {
|
|
42
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
43
|
+
}
|
|
44
|
+
function apiPrefix() {
|
|
45
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
46
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
47
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
48
|
+
return trimmed || "/api/v1";
|
|
49
|
+
}
|
|
50
|
+
function sdkClientClassName() {
|
|
51
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
52
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
53
|
+
return override;
|
|
54
|
+
}
|
|
55
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
56
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
57
|
+
}
|
|
58
|
+
|
|
5
59
|
// ../../src/core/http/cookies.ts
|
|
6
60
|
function readRequestCookie(request, name) {
|
|
7
61
|
const cookies = request.cookies;
|
|
@@ -60,10 +114,10 @@ function currentRequestMeta() {
|
|
|
60
114
|
var CSRF_COOKIE = "workhub_csrf";
|
|
61
115
|
var CSRF_TTL_MS = 60 * 60 * 1000;
|
|
62
116
|
function csrfCookieName() {
|
|
63
|
-
return process.env.CSRF_COOKIE_NAME?.trim() ||
|
|
117
|
+
return process.env.CSRF_COOKIE_NAME?.trim() || appCookieName("csrf");
|
|
64
118
|
}
|
|
65
119
|
function resolveCsrfSecret() {
|
|
66
|
-
return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || "
|
|
120
|
+
return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || appDevSecret("csrf-secret");
|
|
67
121
|
}
|
|
68
122
|
function csrfVerifyOptions() {
|
|
69
123
|
return { secret: resolveCsrfSecret(), maxAge: CSRF_TTL_MS };
|
|
@@ -1,13 +1,69 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/http/flashSession.ts
|
|
3
3
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
4
|
+
|
|
5
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
|
+
function appKeyPrefix() {
|
|
7
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
|
+
}
|
|
9
|
+
function appCookieName(kind) {
|
|
10
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function appDevSecret(kind) {
|
|
13
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
|
+
}
|
|
15
|
+
function namespacedRedisKey(kind) {
|
|
16
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
17
|
+
}
|
|
18
|
+
function smtpEhloHost() {
|
|
19
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
20
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
21
|
+
return safe || "strata.local";
|
|
22
|
+
}
|
|
23
|
+
function siemEventType() {
|
|
24
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
25
|
+
}
|
|
26
|
+
function appUserAgent() {
|
|
27
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
28
|
+
}
|
|
29
|
+
function otelServiceName() {
|
|
30
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
31
|
+
}
|
|
32
|
+
function webhookSignatureHeader() {
|
|
33
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
34
|
+
}
|
|
35
|
+
function appDisplayName() {
|
|
36
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
37
|
+
}
|
|
38
|
+
function appEnv() {
|
|
39
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
40
|
+
}
|
|
41
|
+
function appUrl() {
|
|
42
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
43
|
+
}
|
|
44
|
+
function apiPrefix() {
|
|
45
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
46
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
47
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
48
|
+
return trimmed || "/api/v1";
|
|
49
|
+
}
|
|
50
|
+
function sdkClientClassName() {
|
|
51
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
52
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
53
|
+
return override;
|
|
54
|
+
}
|
|
55
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
56
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ../../src/core/http/flashSession.ts
|
|
4
60
|
var FLASH_COOKIE = "workhub_flash";
|
|
5
61
|
var FLASH_TTL_MS = 60 * 1000;
|
|
6
62
|
function flashCookieName() {
|
|
7
|
-
return process.env.FLASH_COOKIE_NAME?.trim() ||
|
|
63
|
+
return process.env.FLASH_COOKIE_NAME?.trim() || appCookieName("flash");
|
|
8
64
|
}
|
|
9
65
|
function resolveFlashSecret() {
|
|
10
|
-
return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || "
|
|
66
|
+
return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || appDevSecret("flash-secret");
|
|
11
67
|
}
|
|
12
68
|
function signFlashPayload(payload, issuedAt) {
|
|
13
69
|
const signature = createHmac("sha256", resolveFlashSecret()).update(`${payload}.${issuedAt}`).digest("hex");
|
|
@@ -1,13 +1,69 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/http/flashSession.ts
|
|
3
3
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
4
|
+
|
|
5
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
|
+
function appKeyPrefix() {
|
|
7
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
|
+
}
|
|
9
|
+
function appCookieName(kind) {
|
|
10
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function appDevSecret(kind) {
|
|
13
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
|
+
}
|
|
15
|
+
function namespacedRedisKey(kind) {
|
|
16
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
17
|
+
}
|
|
18
|
+
function smtpEhloHost() {
|
|
19
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
20
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
21
|
+
return safe || "strata.local";
|
|
22
|
+
}
|
|
23
|
+
function siemEventType() {
|
|
24
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
25
|
+
}
|
|
26
|
+
function appUserAgent() {
|
|
27
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
28
|
+
}
|
|
29
|
+
function otelServiceName() {
|
|
30
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
31
|
+
}
|
|
32
|
+
function webhookSignatureHeader() {
|
|
33
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
34
|
+
}
|
|
35
|
+
function appDisplayName() {
|
|
36
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
37
|
+
}
|
|
38
|
+
function appEnv() {
|
|
39
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
40
|
+
}
|
|
41
|
+
function appUrl() {
|
|
42
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
43
|
+
}
|
|
44
|
+
function apiPrefix() {
|
|
45
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
46
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
47
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
48
|
+
return trimmed || "/api/v1";
|
|
49
|
+
}
|
|
50
|
+
function sdkClientClassName() {
|
|
51
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
52
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
53
|
+
return override;
|
|
54
|
+
}
|
|
55
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
56
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ../../src/core/http/flashSession.ts
|
|
4
60
|
var FLASH_COOKIE = "workhub_flash";
|
|
5
61
|
var FLASH_TTL_MS = 60 * 1000;
|
|
6
62
|
function flashCookieName() {
|
|
7
|
-
return process.env.FLASH_COOKIE_NAME?.trim() ||
|
|
63
|
+
return process.env.FLASH_COOKIE_NAME?.trim() || appCookieName("flash");
|
|
8
64
|
}
|
|
9
65
|
function resolveFlashSecret() {
|
|
10
|
-
return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || "
|
|
66
|
+
return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || appDevSecret("flash-secret");
|
|
11
67
|
}
|
|
12
68
|
function signFlashPayload(payload, issuedAt) {
|
|
13
69
|
const signature = createHmac("sha256", resolveFlashSecret()).update(`${payload}.${issuedAt}`).digest("hex");
|
|
@@ -6,6 +6,12 @@ var {RedisClient } = globalThis.Bun;
|
|
|
6
6
|
function appKeyPrefix() {
|
|
7
7
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
8
|
}
|
|
9
|
+
function appCookieName(kind) {
|
|
10
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function appDevSecret(kind) {
|
|
13
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
|
+
}
|
|
9
15
|
function namespacedRedisKey(kind) {
|
|
10
16
|
return `${appKeyPrefix()}:${kind}`;
|
|
11
17
|
}
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
function appKeyPrefix() {
|
|
4
4
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
5
5
|
}
|
|
6
|
+
function appCookieName(kind) {
|
|
7
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
8
|
+
}
|
|
9
|
+
function appDevSecret(kind) {
|
|
10
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
11
|
+
}
|
|
6
12
|
function namespacedRedisKey(kind) {
|
|
7
13
|
return `${appKeyPrefix()}:${kind}`;
|
|
8
14
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
hasVerifiedEmail,
|
|
6
6
|
isEmailVerificationRequired
|
|
7
7
|
} from "@getstrata/core/auth/emailVerification";
|
|
8
|
+
import { createIntendedUrlCookieFromRequest } from "@getstrata/core/auth/intendedUrlCookie";
|
|
8
9
|
|
|
9
10
|
// ../../src/core/http/contentNegotiation.ts
|
|
10
11
|
function requestPrefersJson(request) {
|
|
@@ -44,7 +45,12 @@ function createRequireVerifiedMiddleware(auth) {
|
|
|
44
45
|
if (requestPrefersJson(request)) {
|
|
45
46
|
return Response.json({ error: UNVERIFIED_MESSAGE }, { status: 403 });
|
|
46
47
|
}
|
|
47
|
-
|
|
48
|
+
const headers = new Headers({ Location: VERIFY_NOTICE_PATH });
|
|
49
|
+
const intended = createIntendedUrlCookieFromRequest(request);
|
|
50
|
+
if (intended) {
|
|
51
|
+
headers.append("Set-Cookie", intended);
|
|
52
|
+
}
|
|
53
|
+
return new Response(null, { status: 302, headers });
|
|
48
54
|
};
|
|
49
55
|
}
|
|
50
56
|
export {
|
|
@@ -6,6 +6,12 @@ var {RedisClient } = globalThis.Bun;
|
|
|
6
6
|
function appKeyPrefix() {
|
|
7
7
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
8
|
}
|
|
9
|
+
function appCookieName(kind) {
|
|
10
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function appDevSecret(kind) {
|
|
13
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
|
+
}
|
|
9
15
|
function namespacedRedisKey(kind) {
|
|
10
16
|
return `${appKeyPrefix()}:${kind}`;
|
|
11
17
|
}
|
|
@@ -11,6 +11,12 @@ import { currentRequestMeta, runWithRequestMeta } from "@getstrata/core/http/req
|
|
|
11
11
|
function appKeyPrefix() {
|
|
12
12
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
13
13
|
}
|
|
14
|
+
function appCookieName(kind) {
|
|
15
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
16
|
+
}
|
|
17
|
+
function appDevSecret(kind) {
|
|
18
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
19
|
+
}
|
|
14
20
|
function namespacedRedisKey(kind) {
|
|
15
21
|
return `${appKeyPrefix()}:${kind}`;
|
|
16
22
|
}
|
|
@@ -3,8 +3,64 @@
|
|
|
3
3
|
import { createHmac } from "crypto";
|
|
4
4
|
import { ForbiddenError } from "@getstrata/core/errors/http";
|
|
5
5
|
import { timingSafeCompareString } from "@getstrata/core/security/timingSafeCompare";
|
|
6
|
+
|
|
7
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
8
|
+
function appKeyPrefix() {
|
|
9
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
10
|
+
}
|
|
11
|
+
function appCookieName(kind) {
|
|
12
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
13
|
+
}
|
|
14
|
+
function appDevSecret(kind) {
|
|
15
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
16
|
+
}
|
|
17
|
+
function namespacedRedisKey(kind) {
|
|
18
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
19
|
+
}
|
|
20
|
+
function smtpEhloHost() {
|
|
21
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
22
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
23
|
+
return safe || "strata.local";
|
|
24
|
+
}
|
|
25
|
+
function siemEventType() {
|
|
26
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
27
|
+
}
|
|
28
|
+
function appUserAgent() {
|
|
29
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
30
|
+
}
|
|
31
|
+
function otelServiceName() {
|
|
32
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
33
|
+
}
|
|
34
|
+
function webhookSignatureHeader() {
|
|
35
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
36
|
+
}
|
|
37
|
+
function appDisplayName() {
|
|
38
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
39
|
+
}
|
|
40
|
+
function appEnv() {
|
|
41
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
42
|
+
}
|
|
43
|
+
function appUrl() {
|
|
44
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
45
|
+
}
|
|
46
|
+
function apiPrefix() {
|
|
47
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
48
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
49
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
50
|
+
return trimmed || "/api/v1";
|
|
51
|
+
}
|
|
52
|
+
function sdkClientClassName() {
|
|
53
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
54
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
55
|
+
return override;
|
|
56
|
+
}
|
|
57
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
58
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ../../src/core/http/signedUrl.ts
|
|
6
62
|
function resolveSignedUrlSecret() {
|
|
7
|
-
return process.env.SIGNED_URL_SECRET?.trim() || process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || "
|
|
63
|
+
return process.env.SIGNED_URL_SECRET?.trim() || process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || appDevSecret("signed-url-secret");
|
|
8
64
|
}
|
|
9
65
|
function resolveSignedUrlOrigin() {
|
|
10
66
|
return (process.env.APP_URL ?? "http://localhost:3000").replace(/\/$/, "");
|
|
@@ -8,6 +8,12 @@ var {RedisClient } = globalThis.Bun;
|
|
|
8
8
|
function appKeyPrefix() {
|
|
9
9
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
10
10
|
}
|
|
11
|
+
function appCookieName(kind) {
|
|
12
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
13
|
+
}
|
|
14
|
+
function appDevSecret(kind) {
|
|
15
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
16
|
+
}
|
|
11
17
|
function namespacedRedisKey(kind) {
|
|
12
18
|
return `${appKeyPrefix()}:${kind}`;
|
|
13
19
|
}
|
|
@@ -6,6 +6,12 @@ import { repositoryConnection as db2 } from "@getstrata/core/database/repository
|
|
|
6
6
|
function appKeyPrefix() {
|
|
7
7
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
8
|
}
|
|
9
|
+
function appCookieName(kind) {
|
|
10
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function appDevSecret(kind) {
|
|
13
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
|
+
}
|
|
9
15
|
function namespacedRedisKey(kind) {
|
|
10
16
|
return `${appKeyPrefix()}:${kind}`;
|
|
11
17
|
}
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
function appKeyPrefix() {
|
|
4
4
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
5
5
|
}
|
|
6
|
+
function appCookieName(kind) {
|
|
7
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
8
|
+
}
|
|
9
|
+
function appDevSecret(kind) {
|
|
10
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
11
|
+
}
|
|
6
12
|
function namespacedRedisKey(kind) {
|
|
7
13
|
return `${appKeyPrefix()}:${kind}`;
|
|
8
14
|
}
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
function appKeyPrefix() {
|
|
4
4
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
5
5
|
}
|
|
6
|
+
function appCookieName(kind) {
|
|
7
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
8
|
+
}
|
|
9
|
+
function appDevSecret(kind) {
|
|
10
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
11
|
+
}
|
|
6
12
|
function namespacedRedisKey(kind) {
|
|
7
13
|
return `${appKeyPrefix()}:${kind}`;
|
|
8
14
|
}
|
|
@@ -160,6 +160,12 @@ var {RedisClient } = globalThis.Bun;
|
|
|
160
160
|
function appKeyPrefix() {
|
|
161
161
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
162
162
|
}
|
|
163
|
+
function appCookieName(kind) {
|
|
164
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
165
|
+
}
|
|
166
|
+
function appDevSecret(kind) {
|
|
167
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
168
|
+
}
|
|
163
169
|
function namespacedRedisKey(kind) {
|
|
164
170
|
return `${appKeyPrefix()}:${kind}`;
|
|
165
171
|
}
|
|
@@ -160,6 +160,12 @@ var {RedisClient } = globalThis.Bun;
|
|
|
160
160
|
function appKeyPrefix() {
|
|
161
161
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
162
162
|
}
|
|
163
|
+
function appCookieName(kind) {
|
|
164
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
165
|
+
}
|
|
166
|
+
function appDevSecret(kind) {
|
|
167
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
168
|
+
}
|
|
163
169
|
function namespacedRedisKey(kind) {
|
|
164
170
|
return `${appKeyPrefix()}:${kind}`;
|
|
165
171
|
}
|
|
@@ -163,6 +163,12 @@ var {RedisClient } = globalThis.Bun;
|
|
|
163
163
|
function appKeyPrefix() {
|
|
164
164
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
165
165
|
}
|
|
166
|
+
function appCookieName(kind) {
|
|
167
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
168
|
+
}
|
|
169
|
+
function appDevSecret(kind) {
|
|
170
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
171
|
+
}
|
|
166
172
|
function namespacedRedisKey(kind) {
|
|
167
173
|
return `${appKeyPrefix()}:${kind}`;
|
|
168
174
|
}
|
|
@@ -6,6 +6,12 @@ var {RedisClient } = globalThis.Bun;
|
|
|
6
6
|
function appKeyPrefix() {
|
|
7
7
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
8
|
}
|
|
9
|
+
function appCookieName(kind) {
|
|
10
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function appDevSecret(kind) {
|
|
13
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
|
+
}
|
|
9
15
|
function namespacedRedisKey(kind) {
|
|
10
16
|
return `${appKeyPrefix()}:${kind}`;
|
|
11
17
|
}
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
function appKeyPrefix() {
|
|
4
4
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
5
5
|
}
|
|
6
|
+
function appCookieName(kind) {
|
|
7
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
8
|
+
}
|
|
9
|
+
function appDevSecret(kind) {
|
|
10
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
11
|
+
}
|
|
6
12
|
function namespacedRedisKey(kind) {
|
|
7
13
|
return `${appKeyPrefix()}:${kind}`;
|
|
8
14
|
}
|
|
@@ -48,6 +54,8 @@ function sdkClientClassName() {
|
|
|
48
54
|
}
|
|
49
55
|
export {
|
|
50
56
|
apiPrefix,
|
|
57
|
+
appCookieName,
|
|
58
|
+
appDevSecret,
|
|
51
59
|
appDisplayName,
|
|
52
60
|
appEnv,
|
|
53
61
|
appKeyPrefix,
|
|
@@ -1,10 +1,66 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/security/oauthState.ts
|
|
3
3
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
4
|
+
|
|
5
|
+
// ../../src/core/runtime/appKeyPrefix.ts
|
|
6
|
+
function appKeyPrefix() {
|
|
7
|
+
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
|
+
}
|
|
9
|
+
function appCookieName(kind) {
|
|
10
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function appDevSecret(kind) {
|
|
13
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
|
+
}
|
|
15
|
+
function namespacedRedisKey(kind) {
|
|
16
|
+
return `${appKeyPrefix()}:${kind}`;
|
|
17
|
+
}
|
|
18
|
+
function smtpEhloHost() {
|
|
19
|
+
const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
|
|
20
|
+
const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
|
|
21
|
+
return safe || "strata.local";
|
|
22
|
+
}
|
|
23
|
+
function siemEventType() {
|
|
24
|
+
return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
|
|
25
|
+
}
|
|
26
|
+
function appUserAgent() {
|
|
27
|
+
return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
|
|
28
|
+
}
|
|
29
|
+
function otelServiceName() {
|
|
30
|
+
return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
|
|
31
|
+
}
|
|
32
|
+
function webhookSignatureHeader() {
|
|
33
|
+
return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
|
|
34
|
+
}
|
|
35
|
+
function appDisplayName() {
|
|
36
|
+
return process.env.APP_NAME?.trim() || "WorkHub";
|
|
37
|
+
}
|
|
38
|
+
function appEnv() {
|
|
39
|
+
return process.env.APP_ENV?.trim() || "local";
|
|
40
|
+
}
|
|
41
|
+
function appUrl() {
|
|
42
|
+
return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
|
|
43
|
+
}
|
|
44
|
+
function apiPrefix() {
|
|
45
|
+
const raw = process.env.API_PREFIX?.trim() || "/api/v1";
|
|
46
|
+
const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
|
|
47
|
+
const trimmed = withSlash.replace(/\/+$/, "");
|
|
48
|
+
return trimmed || "/api/v1";
|
|
49
|
+
}
|
|
50
|
+
function sdkClientClassName() {
|
|
51
|
+
const override = process.env.APP_SDK_CLASS?.trim();
|
|
52
|
+
if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
|
|
53
|
+
return override;
|
|
54
|
+
}
|
|
55
|
+
const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
|
|
56
|
+
return fromName ? `${fromName}Client` : "AppClient";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ../../src/core/security/oauthState.ts
|
|
4
60
|
var OAUTH_STATE_COOKIE = "oauth_state";
|
|
5
61
|
var OAUTH_STATE_TTL_MS = 10 * 60 * 1000;
|
|
6
62
|
function resolveOAuthStateSecret() {
|
|
7
|
-
return process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || "
|
|
63
|
+
return process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || appDevSecret("oauth-state-secret");
|
|
8
64
|
}
|
|
9
65
|
function signOAuthState(state, issuedAt) {
|
|
10
66
|
const payload = `${state}.${issuedAt}`;
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
function appKeyPrefix() {
|
|
4
4
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
5
5
|
}
|
|
6
|
+
function appCookieName(kind) {
|
|
7
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
8
|
+
}
|
|
9
|
+
function appDevSecret(kind) {
|
|
10
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
11
|
+
}
|
|
6
12
|
function namespacedRedisKey(kind) {
|
|
7
13
|
return `${appKeyPrefix()}:${kind}`;
|
|
8
14
|
}
|
|
@@ -6,6 +6,12 @@ import { randomBytes } from "crypto";
|
|
|
6
6
|
function appKeyPrefix() {
|
|
7
7
|
return process.env.APP_KEY_PREFIX?.trim() || "workhub";
|
|
8
8
|
}
|
|
9
|
+
function appCookieName(kind) {
|
|
10
|
+
return `${appKeyPrefix()}_${kind}`;
|
|
11
|
+
}
|
|
12
|
+
function appDevSecret(kind) {
|
|
13
|
+
return `${appKeyPrefix()}-dev-${kind}`;
|
|
14
|
+
}
|
|
9
15
|
function namespacedRedisKey(kind) {
|
|
10
16
|
return `${appKeyPrefix()}:${kind}`;
|
|
11
17
|
}
|