@m5kdev/backend 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/lib/posthog.d.ts +0 -1
- package/dist/src/lib/sentry.d.ts +0 -1
- package/dist/src/modules/access/access.repository.d.ts +0 -1
- package/dist/src/modules/access/access.service.d.ts +0 -1
- package/dist/src/modules/access/access.test.d.ts +0 -1
- package/dist/src/modules/access/access.utils.d.ts +0 -1
- package/dist/src/modules/ai/ai.db.d.ts +0 -1
- package/dist/src/modules/ai/ai.prompt.d.ts +0 -1
- package/dist/src/modules/ai/ai.repository.d.ts +0 -1
- package/dist/src/modules/ai/ai.router.d.ts +0 -1
- package/dist/src/modules/ai/ai.service.d.ts +0 -1
- package/dist/src/modules/ai/ai.trpc.d.ts +5 -42
- package/dist/src/modules/ai/ai.trpc.js +5 -5
- package/dist/src/modules/ai/ideogram/ideogram.constants.d.ts +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.dto.d.ts +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.prompt.d.ts +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.repository.d.ts +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.service.d.ts +0 -1
- package/dist/src/modules/auth/auth.db.d.ts +0 -1
- package/dist/src/modules/auth/auth.dto.d.ts +7 -8
- package/dist/src/modules/auth/auth.lib.d.ts +8 -9
- package/dist/src/modules/auth/auth.middleware.d.ts +0 -1
- package/dist/src/modules/auth/auth.repository.d.ts +0 -1
- package/dist/src/modules/auth/auth.service.d.ts +0 -1
- package/dist/src/modules/auth/auth.trpc.d.ts +52 -89
- package/dist/src/modules/auth/auth.trpc.js +55 -53
- package/dist/src/modules/auth/auth.utils.d.ts +0 -1
- package/dist/src/modules/base/base.abstract.d.ts +0 -1
- package/dist/src/modules/base/base.dto.d.ts +2 -3
- package/dist/src/modules/base/base.grants.d.ts +0 -1
- package/dist/src/modules/base/base.grants.test.d.ts +0 -1
- package/dist/src/modules/base/base.repository.d.ts +0 -1
- package/dist/src/modules/base/base.service.d.ts +2 -4
- package/dist/src/modules/base/base.types.d.ts +0 -1
- package/dist/src/modules/billing/billing.db.d.ts +0 -1
- package/dist/src/modules/billing/billing.repository.d.ts +0 -1
- package/dist/src/modules/billing/billing.router.d.ts +0 -1
- package/dist/src/modules/billing/billing.service.d.ts +0 -1
- package/dist/src/modules/billing/billing.trpc.d.ts +6 -43
- package/dist/src/modules/billing/billing.trpc.js +7 -7
- package/dist/src/modules/clay/clay.repository.d.ts +0 -1
- package/dist/src/modules/clay/clay.service.d.ts +0 -1
- package/dist/src/modules/connect/connect.db.d.ts +0 -1
- package/dist/src/modules/connect/connect.dto.d.ts +8 -9
- package/dist/src/modules/connect/connect.linkedin.d.ts +0 -1
- package/dist/src/modules/connect/connect.oauth.d.ts +0 -1
- package/dist/src/modules/connect/connect.repository.d.ts +3 -4
- package/dist/src/modules/connect/connect.router.d.ts +0 -1
- package/dist/src/modules/connect/connect.service.d.ts +6 -7
- package/dist/src/modules/connect/connect.trpc.d.ts +9 -46
- package/dist/src/modules/connect/connect.trpc.js +7 -7
- package/dist/src/modules/connect/connect.types.d.ts +0 -1
- package/dist/src/modules/crypto/crypto.db.d.ts +0 -1
- package/dist/src/modules/crypto/crypto.repository.d.ts +0 -1
- package/dist/src/modules/crypto/crypto.service.d.ts +0 -1
- package/dist/src/modules/email/email.service.d.ts +0 -1
- package/dist/src/modules/file/file.repository.d.ts +0 -1
- package/dist/src/modules/file/file.router.d.ts +0 -1
- package/dist/src/modules/file/file.service.d.ts +0 -1
- package/dist/src/modules/recurrence/recurrence.db.d.ts +0 -1
- package/dist/src/modules/recurrence/recurrence.repository.d.ts +0 -1
- package/dist/src/modules/recurrence/recurrence.service.d.ts +0 -1
- package/dist/src/modules/recurrence/recurrence.trpc.d.ts +11 -48
- package/dist/src/modules/recurrence/recurrence.trpc.js +17 -17
- package/dist/src/modules/social/social.dto.d.ts +0 -1
- package/dist/src/modules/social/social.linkedin.d.ts +0 -1
- package/dist/src/modules/social/social.linkedin.test.d.ts +0 -1
- package/dist/src/modules/social/social.service.d.ts +0 -1
- package/dist/src/modules/social/social.types.d.ts +0 -1
- package/dist/src/modules/tag/tag.db.d.ts +0 -1
- package/dist/src/modules/tag/tag.dto.d.ts +0 -1
- package/dist/src/modules/tag/tag.repository.d.ts +0 -1
- package/dist/src/modules/tag/tag.service.d.ts +0 -1
- package/dist/src/modules/tag/tag.trpc.d.ts +10 -47
- package/dist/src/modules/tag/tag.trpc.js +15 -15
- package/dist/src/modules/utils/applyPagination.d.ts +0 -1
- package/dist/src/modules/utils/applySorting.d.ts +0 -1
- package/dist/src/modules/utils/getConditionsFromFilters.d.ts +0 -1
- package/dist/src/modules/video/video.service.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.constants.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.db.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.dto.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.repository.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.router.d.ts +0 -1
- package/dist/src/modules/webhook/webhook.service.d.ts +0 -1
- package/dist/src/modules/workflow/workflow.db.d.ts +0 -1
- package/dist/src/modules/workflow/workflow.repository.d.ts +0 -1
- package/dist/src/modules/workflow/workflow.service.d.ts +0 -1
- package/dist/src/modules/workflow/workflow.trpc.d.ts +6 -43
- package/dist/src/modules/workflow/workflow.trpc.js +7 -7
- package/dist/src/modules/workflow/workflow.types.d.ts +0 -1
- package/dist/src/modules/workflow/workflow.utils.d.ts +0 -1
- package/dist/src/test/stubs/utils.d.ts +0 -1
- package/dist/src/trpc/context.d.ts +4 -5
- package/dist/src/trpc/index.d.ts +0 -1
- package/dist/src/trpc/procedures.d.ts +24 -25
- package/dist/src/trpc/utils.d.ts +0 -1
- package/dist/src/types.d.ts +61 -209
- package/dist/src/types.js +4 -5
- package/dist/src/utils/errors.d.ts +0 -1
- package/dist/src/utils/logger.d.ts +0 -1
- package/dist/src/utils/posthog.d.ts +0 -1
- package/dist/src/utils/trpc.d.ts +58 -0
- package/dist/src/utils/trpc.js +63 -0
- package/dist/src/utils/types.d.ts +0 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -8
- package/.cursor/rules/backend.mdc +0 -70
- package/.turbo/turbo-build.log +0 -5
- package/.turbo/turbo-check-types.log +0 -5
- package/.turbo/turbo-lint$colon$fix.log +0 -255
- package/CHANGELOG.md +0 -37
- package/dist/src/lib/posthog.d.ts.map +0 -1
- package/dist/src/lib/sentry.d.ts.map +0 -1
- package/dist/src/modules/access/access.repository.d.ts.map +0 -1
- package/dist/src/modules/access/access.service.d.ts.map +0 -1
- package/dist/src/modules/access/access.test.d.ts.map +0 -1
- package/dist/src/modules/access/access.utils.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.db.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.prompt.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.repository.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.router.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.service.d.ts.map +0 -1
- package/dist/src/modules/ai/ai.trpc.d.ts.map +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.constants.d.ts.map +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.dto.d.ts.map +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.prompt.d.ts.map +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.repository.d.ts.map +0 -1
- package/dist/src/modules/ai/ideogram/ideogram.service.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.db.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.dto.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.lib.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.middleware.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.repository.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.service.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.trpc.d.ts.map +0 -1
- package/dist/src/modules/auth/auth.utils.d.ts.map +0 -1
- package/dist/src/modules/base/base.abstract.d.ts.map +0 -1
- package/dist/src/modules/base/base.dto.d.ts.map +0 -1
- package/dist/src/modules/base/base.grants.d.ts.map +0 -1
- package/dist/src/modules/base/base.grants.test.d.ts.map +0 -1
- package/dist/src/modules/base/base.repository.d.ts.map +0 -1
- package/dist/src/modules/base/base.service.d.ts.map +0 -1
- package/dist/src/modules/base/base.types.d.ts.map +0 -1
- package/dist/src/modules/billing/billing.db.d.ts.map +0 -1
- package/dist/src/modules/billing/billing.repository.d.ts.map +0 -1
- package/dist/src/modules/billing/billing.router.d.ts.map +0 -1
- package/dist/src/modules/billing/billing.service.d.ts.map +0 -1
- package/dist/src/modules/billing/billing.trpc.d.ts.map +0 -1
- package/dist/src/modules/clay/clay.repository.d.ts.map +0 -1
- package/dist/src/modules/clay/clay.service.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.db.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.dto.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.linkedin.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.oauth.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.repository.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.router.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.service.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.trpc.d.ts.map +0 -1
- package/dist/src/modules/connect/connect.types.d.ts.map +0 -1
- package/dist/src/modules/crypto/crypto.db.d.ts.map +0 -1
- package/dist/src/modules/crypto/crypto.repository.d.ts.map +0 -1
- package/dist/src/modules/crypto/crypto.service.d.ts.map +0 -1
- package/dist/src/modules/email/email.service.d.ts.map +0 -1
- package/dist/src/modules/file/file.repository.d.ts.map +0 -1
- package/dist/src/modules/file/file.router.d.ts.map +0 -1
- package/dist/src/modules/file/file.service.d.ts.map +0 -1
- package/dist/src/modules/recurrence/recurrence.db.d.ts.map +0 -1
- package/dist/src/modules/recurrence/recurrence.repository.d.ts.map +0 -1
- package/dist/src/modules/recurrence/recurrence.service.d.ts.map +0 -1
- package/dist/src/modules/recurrence/recurrence.trpc.d.ts.map +0 -1
- package/dist/src/modules/social/social.dto.d.ts.map +0 -1
- package/dist/src/modules/social/social.linkedin.d.ts.map +0 -1
- package/dist/src/modules/social/social.linkedin.test.d.ts.map +0 -1
- package/dist/src/modules/social/social.service.d.ts.map +0 -1
- package/dist/src/modules/social/social.types.d.ts.map +0 -1
- package/dist/src/modules/tag/tag.db.d.ts.map +0 -1
- package/dist/src/modules/tag/tag.dto.d.ts.map +0 -1
- package/dist/src/modules/tag/tag.repository.d.ts.map +0 -1
- package/dist/src/modules/tag/tag.service.d.ts.map +0 -1
- package/dist/src/modules/tag/tag.trpc.d.ts.map +0 -1
- package/dist/src/modules/utils/applyPagination.d.ts.map +0 -1
- package/dist/src/modules/utils/applySorting.d.ts.map +0 -1
- package/dist/src/modules/utils/getConditionsFromFilters.d.ts.map +0 -1
- package/dist/src/modules/video/video.service.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.constants.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.db.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.dto.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.repository.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.router.d.ts.map +0 -1
- package/dist/src/modules/webhook/webhook.service.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.db.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.repository.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.service.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.trpc.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.types.d.ts.map +0 -1
- package/dist/src/modules/workflow/workflow.utils.d.ts.map +0 -1
- package/dist/src/test/stubs/utils.d.ts.map +0 -1
- package/dist/src/trpc/context.d.ts.map +0 -1
- package/dist/src/trpc/index.d.ts.map +0 -1
- package/dist/src/trpc/procedures.d.ts.map +0 -1
- package/dist/src/trpc/utils.d.ts.map +0 -1
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/utils/errors.d.ts.map +0 -1
- package/dist/src/utils/logger.d.ts.map +0 -1
- package/dist/src/utils/posthog.d.ts.map +0 -1
- package/dist/src/utils/types.d.ts.map +0 -1
- package/jest.config.ts +0 -19
- package/src/lib/posthog.ts +0 -5
- package/src/lib/sentry.ts +0 -8
- package/src/modules/access/access.repository.ts +0 -36
- package/src/modules/access/access.service.ts +0 -81
- package/src/modules/access/access.test.ts +0 -216
- package/src/modules/access/access.utils.ts +0 -46
- package/src/modules/ai/ai.db.ts +0 -38
- package/src/modules/ai/ai.prompt.ts +0 -47
- package/src/modules/ai/ai.repository.ts +0 -53
- package/src/modules/ai/ai.router.ts +0 -148
- package/src/modules/ai/ai.service.ts +0 -310
- package/src/modules/ai/ai.trpc.ts +0 -22
- package/src/modules/ai/ideogram/ideogram.constants.ts +0 -170
- package/src/modules/ai/ideogram/ideogram.dto.ts +0 -64
- package/src/modules/ai/ideogram/ideogram.prompt.ts +0 -858
- package/src/modules/ai/ideogram/ideogram.repository.ts +0 -39
- package/src/modules/ai/ideogram/ideogram.service.ts +0 -14
- package/src/modules/auth/auth.db.ts +0 -224
- package/src/modules/auth/auth.dto.ts +0 -47
- package/src/modules/auth/auth.lib.ts +0 -349
- package/src/modules/auth/auth.middleware.ts +0 -62
- package/src/modules/auth/auth.repository.ts +0 -672
- package/src/modules/auth/auth.service.ts +0 -261
- package/src/modules/auth/auth.trpc.ts +0 -208
- package/src/modules/auth/auth.utils.ts +0 -117
- package/src/modules/base/base.abstract.ts +0 -62
- package/src/modules/base/base.dto.ts +0 -206
- package/src/modules/base/base.grants.test.ts +0 -861
- package/src/modules/base/base.grants.ts +0 -199
- package/src/modules/base/base.repository.ts +0 -433
- package/src/modules/base/base.service.ts +0 -154
- package/src/modules/base/base.types.ts +0 -7
- package/src/modules/billing/billing.db.ts +0 -27
- package/src/modules/billing/billing.repository.ts +0 -328
- package/src/modules/billing/billing.router.ts +0 -77
- package/src/modules/billing/billing.service.ts +0 -177
- package/src/modules/billing/billing.trpc.ts +0 -17
- package/src/modules/clay/clay.repository.ts +0 -29
- package/src/modules/clay/clay.service.ts +0 -61
- package/src/modules/connect/connect.db.ts +0 -32
- package/src/modules/connect/connect.dto.ts +0 -44
- package/src/modules/connect/connect.linkedin.ts +0 -70
- package/src/modules/connect/connect.oauth.ts +0 -288
- package/src/modules/connect/connect.repository.ts +0 -65
- package/src/modules/connect/connect.router.ts +0 -76
- package/src/modules/connect/connect.service.ts +0 -171
- package/src/modules/connect/connect.trpc.ts +0 -26
- package/src/modules/connect/connect.types.ts +0 -27
- package/src/modules/crypto/crypto.db.ts +0 -15
- package/src/modules/crypto/crypto.repository.ts +0 -13
- package/src/modules/crypto/crypto.service.ts +0 -57
- package/src/modules/email/email.service.ts +0 -222
- package/src/modules/file/file.repository.ts +0 -95
- package/src/modules/file/file.router.ts +0 -108
- package/src/modules/file/file.service.ts +0 -186
- package/src/modules/recurrence/recurrence.db.ts +0 -79
- package/src/modules/recurrence/recurrence.repository.ts +0 -70
- package/src/modules/recurrence/recurrence.service.ts +0 -105
- package/src/modules/recurrence/recurrence.trpc.ts +0 -82
- package/src/modules/social/social.dto.ts +0 -22
- package/src/modules/social/social.linkedin.test.ts +0 -277
- package/src/modules/social/social.linkedin.ts +0 -593
- package/src/modules/social/social.service.ts +0 -112
- package/src/modules/social/social.types.ts +0 -43
- package/src/modules/tag/tag.db.ts +0 -41
- package/src/modules/tag/tag.dto.ts +0 -18
- package/src/modules/tag/tag.repository.ts +0 -222
- package/src/modules/tag/tag.service.ts +0 -48
- package/src/modules/tag/tag.trpc.ts +0 -62
- package/src/modules/uploads/0581796b-8845-420d-bd95-cd7de79f6d37.webm +0 -0
- package/src/modules/uploads/33b1e649-6727-4bd0-94d0-a0b363646865.webm +0 -0
- package/src/modules/uploads/49a8c4c0-54d7-4c94-bef4-c93c029f9ed0.webm +0 -0
- package/src/modules/uploads/50e31e38-a2f0-47ca-8b7d-2d7fcad9267d.webm +0 -0
- package/src/modules/uploads/72ac8cf9-c3a7-4cd8-8a78-6d8e137a4c7e.webm +0 -0
- package/src/modules/uploads/75293649-d966-46cd-a675-67518958ae9c.png +0 -0
- package/src/modules/uploads/88b7b867-ce15-4891-bf73-81305a7de1f7.wav +0 -0
- package/src/modules/uploads/a5d6fee8-6a59-42c6-9d4a-ac8a3c5e7245.webm +0 -0
- package/src/modules/uploads/c13a9785-ca5a-4983-af30-b338ed76d370.webm +0 -0
- package/src/modules/uploads/caa1a5a7-71ba-4381-902d-7e2cafdf6dcb.webm +0 -0
- package/src/modules/uploads/cbeb0b81-374d-445b-914b-40ace7c8e031.webm +0 -0
- package/src/modules/uploads/d626aa82-b10f-493f-aee7-87bfb3361dfc.webm +0 -0
- package/src/modules/uploads/d7de4c16-de0c-495d-9612-e72260a6ecca.png +0 -0
- package/src/modules/uploads/e532e38a-6421-400e-8a5f-8e7bc8ce411b.wav +0 -0
- package/src/modules/uploads/e86ec867-6adf-4c51-84e0-00b0836625e8.webm +0 -0
- package/src/modules/utils/applyPagination.ts +0 -13
- package/src/modules/utils/applySorting.ts +0 -21
- package/src/modules/utils/getConditionsFromFilters.ts +0 -216
- package/src/modules/video/video.service.ts +0 -89
- package/src/modules/webhook/webhook.constants.ts +0 -9
- package/src/modules/webhook/webhook.db.ts +0 -15
- package/src/modules/webhook/webhook.dto.ts +0 -9
- package/src/modules/webhook/webhook.repository.ts +0 -68
- package/src/modules/webhook/webhook.router.ts +0 -29
- package/src/modules/webhook/webhook.service.ts +0 -78
- package/src/modules/workflow/workflow.db.ts +0 -29
- package/src/modules/workflow/workflow.repository.ts +0 -171
- package/src/modules/workflow/workflow.service.ts +0 -56
- package/src/modules/workflow/workflow.trpc.ts +0 -26
- package/src/modules/workflow/workflow.types.ts +0 -30
- package/src/modules/workflow/workflow.utils.ts +0 -259
- package/src/test/stubs/utils.ts +0 -2
- package/src/trpc/context.ts +0 -21
- package/src/trpc/index.ts +0 -3
- package/src/trpc/procedures.ts +0 -43
- package/src/trpc/utils.ts +0 -20
- package/src/types.ts +0 -22
- package/src/utils/errors.ts +0 -148
- package/src/utils/logger.ts +0 -8
- package/src/utils/posthog.ts +0 -43
- package/src/utils/types.ts +0 -5
- package/tsconfig.json +0 -21
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
import * as client from "openid-client";
|
|
2
|
-
import { logger as rootLogger } from "#utils/logger";
|
|
3
|
-
import type { ConnectProvider } from "./connect.types";
|
|
4
|
-
|
|
5
|
-
export interface OAuthState {
|
|
6
|
-
state: string;
|
|
7
|
-
codeVerifier: string;
|
|
8
|
-
codeChallenge: string;
|
|
9
|
-
sessionId: string;
|
|
10
|
-
provider: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// In-memory store for OAuth state (keyed by sessionId + provider)
|
|
14
|
-
// In production, consider using Redis with TTL
|
|
15
|
-
const oauthStateStore = new Map<string, OAuthState>();
|
|
16
|
-
|
|
17
|
-
const STATE_TTL_MS = 10 * 60 * 1000; // 10 minutes
|
|
18
|
-
|
|
19
|
-
function getStateKey(sessionId: string, provider: string): string {
|
|
20
|
-
return `${sessionId}:${provider}`;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export async function generateOAuthState(sessionId: string, provider: string): Promise<OAuthState> {
|
|
24
|
-
const state = client.randomState();
|
|
25
|
-
const codeVerifier = client.randomPKCECodeVerifier();
|
|
26
|
-
const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);
|
|
27
|
-
|
|
28
|
-
const oauthState: OAuthState = {
|
|
29
|
-
state,
|
|
30
|
-
codeVerifier,
|
|
31
|
-
codeChallenge,
|
|
32
|
-
sessionId,
|
|
33
|
-
provider,
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const key = getStateKey(sessionId, provider);
|
|
37
|
-
oauthStateStore.set(key, oauthState);
|
|
38
|
-
|
|
39
|
-
// Clean up after TTL
|
|
40
|
-
setTimeout(() => {
|
|
41
|
-
oauthStateStore.delete(key);
|
|
42
|
-
}, STATE_TTL_MS);
|
|
43
|
-
|
|
44
|
-
return oauthState;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function getOAuthState(
|
|
48
|
-
sessionId: string,
|
|
49
|
-
provider: string,
|
|
50
|
-
state: string
|
|
51
|
-
): OAuthState | null {
|
|
52
|
-
const key = getStateKey(sessionId, provider);
|
|
53
|
-
const stored = oauthStateStore.get(key);
|
|
54
|
-
|
|
55
|
-
if (!stored || stored.state !== state) {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Clean up after use
|
|
60
|
-
oauthStateStore.delete(key);
|
|
61
|
-
return stored;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export async function createConfiguration(
|
|
65
|
-
provider: ConnectProvider
|
|
66
|
-
): Promise<client.Configuration> {
|
|
67
|
-
// LinkedIn uses client_secret_post (form-encoded body parameters)
|
|
68
|
-
// The library's ClientSecretPost handles this correctly
|
|
69
|
-
const clientAuth = provider.clientSecret
|
|
70
|
-
? client.ClientSecretPost(provider.clientSecret)
|
|
71
|
-
: client.None();
|
|
72
|
-
|
|
73
|
-
if (provider.issuerConfig) {
|
|
74
|
-
// Use manual issuer config (e.g., LinkedIn) - create Configuration directly
|
|
75
|
-
// LinkedIn doesn't support OpenID Connect discovery
|
|
76
|
-
const serverMetadata: client.ServerMetadata = {
|
|
77
|
-
issuer: provider.issuerConfig.issuer,
|
|
78
|
-
authorization_endpoint: provider.issuerConfig.authorization_endpoint,
|
|
79
|
-
token_endpoint: provider.issuerConfig.token_endpoint,
|
|
80
|
-
...(provider.issuerConfig.userinfo_endpoint && {
|
|
81
|
-
userinfo_endpoint: provider.issuerConfig.userinfo_endpoint,
|
|
82
|
-
}),
|
|
83
|
-
// LinkedIn JWKS URI for ID token signature verification
|
|
84
|
-
...(provider.id === "linkedin" && {
|
|
85
|
-
jwks_uri: "https://www.linkedin.com/oauth/openid/jwks",
|
|
86
|
-
}),
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const clientMetadata: Partial<client.ClientMetadata> = {
|
|
90
|
-
client_id: provider.clientId,
|
|
91
|
-
...(provider.clientSecret && { client_secret: provider.clientSecret }),
|
|
92
|
-
redirect_uris: [provider.redirectUri],
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
return new client.Configuration(serverMetadata, provider.clientId, clientMetadata, clientAuth);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Auto-discovery from well-known endpoint
|
|
99
|
-
if (!provider.issuerUrl) {
|
|
100
|
-
throw new Error("Provider must have either issuerConfig or issuerUrl");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const serverUrl = new URL(provider.issuerUrl);
|
|
104
|
-
return await client.discovery(serverUrl, provider.clientId, undefined, clientAuth);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export async function buildAuthorizationUrl(
|
|
108
|
-
provider: ConnectProvider,
|
|
109
|
-
state: OAuthState
|
|
110
|
-
): Promise<string> {
|
|
111
|
-
const config = await createConfiguration(provider);
|
|
112
|
-
|
|
113
|
-
const parameters: Record<string, string> = {
|
|
114
|
-
scope: provider.scopes.join(" "),
|
|
115
|
-
state: state.state,
|
|
116
|
-
redirect_uri: provider.redirectUri,
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// Add PKCE parameters only if provider supports it
|
|
120
|
-
if (provider.supportsPKCE !== false) {
|
|
121
|
-
parameters.code_challenge = state.codeChallenge;
|
|
122
|
-
parameters.code_challenge_method = "S256";
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const url = client.buildAuthorizationUrl(config, parameters);
|
|
126
|
-
return url.toString();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export async function exchangeCodeForTokens(
|
|
130
|
-
provider: ConnectProvider,
|
|
131
|
-
code: string,
|
|
132
|
-
codeVerifier: string,
|
|
133
|
-
redirectUri: string,
|
|
134
|
-
state: string
|
|
135
|
-
): Promise<{
|
|
136
|
-
accessToken: string;
|
|
137
|
-
refreshToken?: string;
|
|
138
|
-
tokenType?: string;
|
|
139
|
-
expiresAt?: Date;
|
|
140
|
-
scope?: string;
|
|
141
|
-
}> {
|
|
142
|
-
const logger = rootLogger.child({ layer: "exchangeCodeForTokens" });
|
|
143
|
-
|
|
144
|
-
try {
|
|
145
|
-
// LinkedIn-specific workaround: Manual token exchange to bypass ID token validation
|
|
146
|
-
// LinkedIn's OpenID Connect ID token has non-standard claim format
|
|
147
|
-
if (provider.id === "linkedin" && provider.issuerConfig) {
|
|
148
|
-
const tokenEndpoint = provider.issuerConfig.token_endpoint;
|
|
149
|
-
const body = new URLSearchParams({
|
|
150
|
-
grant_type: "authorization_code",
|
|
151
|
-
code,
|
|
152
|
-
redirect_uri: provider.redirectUri,
|
|
153
|
-
client_id: provider.clientId,
|
|
154
|
-
client_secret: provider.clientSecret,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
const response = await fetch(tokenEndpoint, {
|
|
158
|
-
method: "POST",
|
|
159
|
-
headers: {
|
|
160
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
161
|
-
},
|
|
162
|
-
body: body.toString(),
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
if (!response.ok) {
|
|
166
|
-
const errorData = await response.json().catch(() => ({}));
|
|
167
|
-
throw new Error(
|
|
168
|
-
`LinkedIn token exchange failed: ${response.status} ${response.statusText} - ${JSON.stringify(errorData)}`
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const tokenData = (await response.json()) as {
|
|
173
|
-
access_token: string;
|
|
174
|
-
refresh_token?: string;
|
|
175
|
-
token_type?: string;
|
|
176
|
-
expires_in?: number;
|
|
177
|
-
scope?: string;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
accessToken: tokenData.access_token,
|
|
182
|
-
refreshToken: tokenData.refresh_token,
|
|
183
|
-
tokenType: tokenData.token_type || "bearer",
|
|
184
|
-
expiresAt: tokenData.expires_in
|
|
185
|
-
? new Date(Date.now() + tokenData.expires_in * 1000)
|
|
186
|
-
: undefined,
|
|
187
|
-
scope: tokenData.scope,
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Standard flow for other providers
|
|
192
|
-
const config = await createConfiguration(provider);
|
|
193
|
-
const currentUrl = new URL(redirectUri);
|
|
194
|
-
currentUrl.searchParams.set("code", code);
|
|
195
|
-
currentUrl.searchParams.set("state", state);
|
|
196
|
-
|
|
197
|
-
const checks: {
|
|
198
|
-
pkceCodeVerifier?: string;
|
|
199
|
-
expectedState: string;
|
|
200
|
-
} = {
|
|
201
|
-
expectedState: state,
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
// Only include PKCE verifier if provider supports it
|
|
205
|
-
if (provider.supportsPKCE !== false) {
|
|
206
|
-
checks.pkceCodeVerifier = codeVerifier;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const tokenSet = await client.authorizationCodeGrant(config, currentUrl, checks);
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
accessToken: tokenSet.access_token,
|
|
213
|
-
refreshToken: tokenSet.refresh_token,
|
|
214
|
-
tokenType: tokenSet.token_type,
|
|
215
|
-
expiresAt: tokenSet.expires_in
|
|
216
|
-
? new Date(Date.now() + tokenSet.expires_in * 1000)
|
|
217
|
-
: undefined,
|
|
218
|
-
scope: tokenSet.scope,
|
|
219
|
-
};
|
|
220
|
-
} catch (error: unknown) {
|
|
221
|
-
// Enhanced error logging for OAuth issues
|
|
222
|
-
logger.error("Token exchange error", { error, provider: provider.id });
|
|
223
|
-
|
|
224
|
-
if (error instanceof Error) {
|
|
225
|
-
const errorMessage = error.message || "Unknown error";
|
|
226
|
-
|
|
227
|
-
// Check if this is an ID token validation error for LinkedIn
|
|
228
|
-
// LinkedIn's ID token may have non-standard claim format, but we can still use the access token
|
|
229
|
-
if (
|
|
230
|
-
provider.id === "linkedin" &&
|
|
231
|
-
errorMessage.includes("JWT claim") &&
|
|
232
|
-
(error as { code?: string }).code === "OAUTH_JWT_CLAIM_COMPARISON_FAILED"
|
|
233
|
-
) {
|
|
234
|
-
// Try to extract access token from the error response if available
|
|
235
|
-
// This is a workaround for LinkedIn's ID token validation issues
|
|
236
|
-
logger.warn(
|
|
237
|
-
"LinkedIn ID token validation failed, but token exchange may have succeeded. Check if access token is available.",
|
|
238
|
-
{ error: errorMessage }
|
|
239
|
-
);
|
|
240
|
-
// Re-throw for now - we need the access token to continue
|
|
241
|
-
// In a production scenario, you might want to manually parse the token response
|
|
242
|
-
throw new Error(
|
|
243
|
-
`LinkedIn ID token validation failed: ${errorMessage}. This is a known issue with LinkedIn's OpenID Connect implementation.`
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// ResponseBodyError from oauth4webapi has a 'cause' property with the error details
|
|
248
|
-
const responseBodyError = error as { cause?: Record<string, unknown> };
|
|
249
|
-
const errorDetails = responseBodyError.cause;
|
|
250
|
-
|
|
251
|
-
// Extract error and error_description from LinkedIn's response
|
|
252
|
-
const linkedInError = errorDetails?.error as string | undefined;
|
|
253
|
-
const linkedInErrorDescription = errorDetails?.error_description as string | undefined;
|
|
254
|
-
|
|
255
|
-
const fullErrorMessage = linkedInError
|
|
256
|
-
? `LinkedIn OAuth error: ${linkedInError}${linkedInErrorDescription ? ` - ${linkedInErrorDescription}` : ""}`
|
|
257
|
-
: `Token exchange failed: ${errorMessage}${
|
|
258
|
-
errorDetails ? ` - Details: ${JSON.stringify(errorDetails)}` : ""
|
|
259
|
-
}`;
|
|
260
|
-
|
|
261
|
-
throw new Error(fullErrorMessage);
|
|
262
|
-
}
|
|
263
|
-
throw error;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
export async function refreshAccessToken(
|
|
268
|
-
provider: ConnectProvider,
|
|
269
|
-
refreshToken: string
|
|
270
|
-
): Promise<{
|
|
271
|
-
accessToken: string;
|
|
272
|
-
refreshToken?: string;
|
|
273
|
-
tokenType?: string;
|
|
274
|
-
expiresAt?: Date;
|
|
275
|
-
scope?: string;
|
|
276
|
-
}> {
|
|
277
|
-
const config = await createConfiguration(provider);
|
|
278
|
-
|
|
279
|
-
const tokenSet = await client.refreshTokenGrant(config, refreshToken);
|
|
280
|
-
|
|
281
|
-
return {
|
|
282
|
-
accessToken: tokenSet.access_token,
|
|
283
|
-
refreshToken: tokenSet.refresh_token || refreshToken,
|
|
284
|
-
tokenType: tokenSet.token_type,
|
|
285
|
-
expiresAt: tokenSet.expires_in ? new Date(Date.now() + tokenSet.expires_in * 1000) : undefined,
|
|
286
|
-
scope: tokenSet.scope,
|
|
287
|
-
};
|
|
288
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import type { InferInsertModel, InferSelectModel } from "drizzle-orm";
|
|
2
|
-
import { and, eq, inArray, isNull } from "drizzle-orm";
|
|
3
|
-
import type { LibSQLDatabase } from "drizzle-orm/libsql";
|
|
4
|
-
import { ok } from "neverthrow";
|
|
5
|
-
import { BaseTableRepository } from "#modules/base/base.repository";
|
|
6
|
-
import * as connect from "#modules/connect/connect.db";
|
|
7
|
-
import type { ConnectListInputSchema } from "#modules/connect/connect.dto";
|
|
8
|
-
|
|
9
|
-
const schema = { ...connect };
|
|
10
|
-
type Schema = typeof schema;
|
|
11
|
-
type Orm = LibSQLDatabase<Schema>;
|
|
12
|
-
|
|
13
|
-
export type ConnectRow = InferSelectModel<Schema["connect"]>;
|
|
14
|
-
export type ConnectInsert = InferInsertModel<Schema["connect"]>;
|
|
15
|
-
|
|
16
|
-
export class ConnectRepository extends BaseTableRepository<Orm, Schema, Record<string, never>, Schema["connect"]> {
|
|
17
|
-
async list(data: ConnectListInputSchema & { userId: string }, tx?: Orm) {
|
|
18
|
-
return this.throwableAsync(async () => {
|
|
19
|
-
const db = tx ?? this.orm;
|
|
20
|
-
const { ConditionBuilder } = this.helpers;
|
|
21
|
-
const conditions = new ConditionBuilder();
|
|
22
|
-
if (data.providers) conditions.push(inArray(this.schema.connect.provider, data.providers));
|
|
23
|
-
if (data.inactive) conditions.push(isNull(this.schema.connect.revokedAt));
|
|
24
|
-
conditions.push(eq(this.schema.connect.userId, data.userId));
|
|
25
|
-
const rows = await db.select().from(this.schema.connect).where(conditions.join());
|
|
26
|
-
return ok(rows);
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async upsert(data: ConnectInsert, tx?: Orm) {
|
|
31
|
-
return this.throwableAsync(async () => {
|
|
32
|
-
const db = tx ?? this.orm;
|
|
33
|
-
const [existing] = await db
|
|
34
|
-
.select()
|
|
35
|
-
.from(this.schema.connect)
|
|
36
|
-
.where(
|
|
37
|
-
and(
|
|
38
|
-
eq(this.schema.connect.userId, data.userId),
|
|
39
|
-
eq(this.schema.connect.provider, data.provider),
|
|
40
|
-
eq(this.schema.connect.providerAccountId, data.providerAccountId)
|
|
41
|
-
)
|
|
42
|
-
)
|
|
43
|
-
.limit(1);
|
|
44
|
-
|
|
45
|
-
if (existing) {
|
|
46
|
-
// Update existing
|
|
47
|
-
const [updated] = await db
|
|
48
|
-
.update(this.schema.connect)
|
|
49
|
-
.set({
|
|
50
|
-
...data,
|
|
51
|
-
updatedAt: new Date(),
|
|
52
|
-
lastRefreshedAt: new Date(),
|
|
53
|
-
})
|
|
54
|
-
.where(eq(this.schema.connect.id, existing.id))
|
|
55
|
-
.returning();
|
|
56
|
-
return ok(updated);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Create new
|
|
60
|
-
const [created] = await db.insert(this.schema.connect).values(data).returning();
|
|
61
|
-
if (!created) return this.error("UNPROCESSABLE_CONTENT");
|
|
62
|
-
return ok(created);
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { Router } from "express";
|
|
2
|
-
import type { AuthMiddleware, AuthRequest } from "#modules/auth/auth.middleware";
|
|
3
|
-
import type { ConnectService } from "./connect.service";
|
|
4
|
-
|
|
5
|
-
export function createConnectRouter(
|
|
6
|
-
authMiddleware: AuthMiddleware,
|
|
7
|
-
connectService: ConnectService
|
|
8
|
-
): Router {
|
|
9
|
-
const connectRouter = Router();
|
|
10
|
-
|
|
11
|
-
connectRouter.get("/:provider/start", authMiddleware, async (req: AuthRequest, res) => {
|
|
12
|
-
const user = req.user;
|
|
13
|
-
const session = req.session;
|
|
14
|
-
if (!user || !session) {
|
|
15
|
-
return res.status(401).json({ message: "Unauthorized" });
|
|
16
|
-
}
|
|
17
|
-
const { provider } = req.params;
|
|
18
|
-
const { redirect } = req.query;
|
|
19
|
-
|
|
20
|
-
const result = await connectService.startAuth(user, session.id, provider);
|
|
21
|
-
|
|
22
|
-
if (result.isErr()) {
|
|
23
|
-
const errorUrl = redirect
|
|
24
|
-
? `${redirect}?error=${encodeURIComponent(result.error.message)}`
|
|
25
|
-
: `${process.env.VITE_APP_URL || process.env.VITE_SERVER_URL || "/"}?connect_error=${encodeURIComponent(result.error.message)}`;
|
|
26
|
-
return res.redirect(errorUrl);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return res.redirect(result.value.url);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
connectRouter.get("/:provider/callback", authMiddleware, async (req: AuthRequest, res) => {
|
|
33
|
-
const user = req.user;
|
|
34
|
-
const session = req.session;
|
|
35
|
-
if (!user || !session) {
|
|
36
|
-
return res.status(401).json({ message: "Unauthorized" });
|
|
37
|
-
}
|
|
38
|
-
const { provider } = req.params;
|
|
39
|
-
const { code, state, error, error_description } = req.query;
|
|
40
|
-
const { redirect } = req.query;
|
|
41
|
-
|
|
42
|
-
const successUrl =
|
|
43
|
-
redirect && typeof redirect === "string"
|
|
44
|
-
? redirect
|
|
45
|
-
: `${process.env.VITE_APP_URL || process.env.VITE_SERVER_URL || "/"}?connect_success=true`;
|
|
46
|
-
|
|
47
|
-
// Handle OAuth errors from provider
|
|
48
|
-
if (error) {
|
|
49
|
-
const errorMessage = error_description || error || "OAuth error";
|
|
50
|
-
const errorUrl = `${successUrl}&error=${encodeURIComponent(String(errorMessage))}`;
|
|
51
|
-
return res.redirect(errorUrl);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!code || !state) {
|
|
55
|
-
const errorUrl = `${successUrl}&error=${encodeURIComponent("Missing code or state")}`;
|
|
56
|
-
return res.redirect(errorUrl);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const result = await connectService.handleCallback(
|
|
60
|
-
user,
|
|
61
|
-
session.id,
|
|
62
|
-
provider,
|
|
63
|
-
String(code),
|
|
64
|
-
String(state)
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
if (result.isErr()) {
|
|
68
|
-
const errorUrl = `${successUrl}&error=${encodeURIComponent(result.error.message)}`;
|
|
69
|
-
return res.redirect(errorUrl);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return res.redirect(`${successUrl}&provider=${provider}`);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
return connectRouter;
|
|
76
|
-
}
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { err, ok } from "neverthrow";
|
|
2
|
-
import type { User } from "#modules/auth/auth.lib";
|
|
3
|
-
import type { ServerResultAsync } from "#modules/base/base.dto";
|
|
4
|
-
import { BaseService } from "#modules/base/base.service";
|
|
5
|
-
import type {
|
|
6
|
-
ConnectDeleteInputSchema,
|
|
7
|
-
ConnectListInputSchema,
|
|
8
|
-
} from "#modules/connect/connect.dto";
|
|
9
|
-
import {
|
|
10
|
-
buildAuthorizationUrl,
|
|
11
|
-
exchangeCodeForTokens,
|
|
12
|
-
generateOAuthState,
|
|
13
|
-
getOAuthState,
|
|
14
|
-
refreshAccessToken,
|
|
15
|
-
} from "./connect.oauth";
|
|
16
|
-
import type { ConnectRepository } from "./connect.repository";
|
|
17
|
-
import type { ConnectProvider } from "./connect.types";
|
|
18
|
-
|
|
19
|
-
export class ConnectService extends BaseService<{ connect: ConnectRepository }, never> {
|
|
20
|
-
private providers = new Map<string, ConnectProvider>();
|
|
21
|
-
|
|
22
|
-
constructor(repositories: { connect: ConnectRepository }, providers: ConnectProvider[]) {
|
|
23
|
-
super(repositories);
|
|
24
|
-
this.providers = new Map(providers.map((provider) => [provider.id, provider]));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
getProvider(id: string): ConnectProvider | null {
|
|
28
|
-
return this.providers.get(id) || null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async startAuth(
|
|
32
|
-
_user: User,
|
|
33
|
-
sessionId: string,
|
|
34
|
-
providerId: string
|
|
35
|
-
): ServerResultAsync<{ url: string }> {
|
|
36
|
-
return this.throwableAsync(async () => {
|
|
37
|
-
const provider = this.getProvider(providerId);
|
|
38
|
-
if (!provider) {
|
|
39
|
-
return this.error("BAD_REQUEST", `Unknown provider: ${providerId}`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const state = await generateOAuthState(sessionId, providerId);
|
|
43
|
-
const url = await buildAuthorizationUrl(provider, state);
|
|
44
|
-
|
|
45
|
-
return ok({ url });
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async handleCallback(
|
|
50
|
-
user: User,
|
|
51
|
-
sessionId: string,
|
|
52
|
-
providerId: string,
|
|
53
|
-
code: string,
|
|
54
|
-
state: string
|
|
55
|
-
) {
|
|
56
|
-
return this.throwableAsync(async () => {
|
|
57
|
-
const provider = this.getProvider(providerId);
|
|
58
|
-
if (!provider) {
|
|
59
|
-
return this.error("BAD_REQUEST", `Unknown provider: ${providerId}`);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const oauthState = getOAuthState(sessionId, providerId, state);
|
|
63
|
-
if (!oauthState) {
|
|
64
|
-
return this.error("BAD_REQUEST", "Invalid or expired state");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Exchange code for tokens
|
|
68
|
-
// Note: redirectUri must match exactly what was used in authorization request
|
|
69
|
-
const tokens = await exchangeCodeForTokens(
|
|
70
|
-
provider,
|
|
71
|
-
code,
|
|
72
|
-
oauthState.codeVerifier,
|
|
73
|
-
provider.redirectUri,
|
|
74
|
-
state
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
// Fetch profile
|
|
78
|
-
const profile = await provider.mapProfile(tokens.accessToken);
|
|
79
|
-
|
|
80
|
-
// Upsert connection
|
|
81
|
-
const connection = await this.repository.connect.upsert({
|
|
82
|
-
userId: user.id,
|
|
83
|
-
provider: providerId,
|
|
84
|
-
accountType: profile.accountType,
|
|
85
|
-
providerAccountId: profile.providerAccountId,
|
|
86
|
-
handle: profile.handle,
|
|
87
|
-
displayName: profile.displayName,
|
|
88
|
-
avatarUrl: profile.avatarUrl,
|
|
89
|
-
accessToken: tokens.accessToken,
|
|
90
|
-
refreshToken: tokens.refreshToken,
|
|
91
|
-
tokenType: tokens.tokenType || "bearer",
|
|
92
|
-
scope: tokens.scope,
|
|
93
|
-
expiresAt: tokens.expiresAt,
|
|
94
|
-
parentId: profile.parentId,
|
|
95
|
-
metadataJson: profile.metadata ? JSON.stringify(profile.metadata) : null,
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
if (connection.isErr()) {
|
|
99
|
-
return this.error("INTERNAL_SERVER_ERROR", "Failed to save connection", {
|
|
100
|
-
cause: connection.error,
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return ok(connection.value);
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async refreshToken(connectionId: string) {
|
|
109
|
-
return this.throwableAsync(async () => {
|
|
110
|
-
const connection = await this.repository.connect.findById(connectionId);
|
|
111
|
-
if (connection.isErr() || !connection.value) {
|
|
112
|
-
return this.error("NOT_FOUND", "Connection not found");
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const conn = connection.value;
|
|
116
|
-
if (!conn.refreshToken) {
|
|
117
|
-
return this.error("BAD_REQUEST", "No refresh token available");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const provider = this.getProvider(conn.provider);
|
|
121
|
-
if (!provider) {
|
|
122
|
-
return this.error("BAD_REQUEST", `Unknown provider: ${conn.provider}`);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const tokens = await refreshAccessToken(provider, conn.refreshToken);
|
|
126
|
-
|
|
127
|
-
const updateData: {
|
|
128
|
-
id: string;
|
|
129
|
-
accessToken: string;
|
|
130
|
-
refreshToken?: string | null;
|
|
131
|
-
tokenType?: string | null;
|
|
132
|
-
scope?: string | null;
|
|
133
|
-
expiresAt?: Date | null;
|
|
134
|
-
lastRefreshedAt: Date;
|
|
135
|
-
} = {
|
|
136
|
-
id: conn.id,
|
|
137
|
-
accessToken: tokens.accessToken,
|
|
138
|
-
refreshToken: tokens.refreshToken || null,
|
|
139
|
-
tokenType: tokens.tokenType || conn.tokenType || null,
|
|
140
|
-
scope: tokens.scope || conn.scope || null,
|
|
141
|
-
expiresAt: tokens.expiresAt || null,
|
|
142
|
-
lastRefreshedAt: new Date(),
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
const updated = await this.repository.connect.update(updateData);
|
|
146
|
-
|
|
147
|
-
if (updated.isErr()) {
|
|
148
|
-
return this.error("INTERNAL_SERVER_ERROR", "Failed to update tokens", {
|
|
149
|
-
cause: updated.error,
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return ok(updated.value);
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async list(input: ConnectListInputSchema, { user }: { user: User }) {
|
|
158
|
-
return this.repository.connect.list({ userId: user.id, ...input });
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async delete({ id }: ConnectDeleteInputSchema, { user }: { user: User }) {
|
|
162
|
-
const connection = await this.repository.connect.findById(id);
|
|
163
|
-
if (connection.isOk()) {
|
|
164
|
-
if (connection.value?.userId !== user.id) {
|
|
165
|
-
return this.error("FORBIDDEN", "Not your connection");
|
|
166
|
-
}
|
|
167
|
-
return this.repository.connect.deleteById(id);
|
|
168
|
-
}
|
|
169
|
-
return err(connection.error);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { handleTRPCResult, procedure, router } from "#trpc";
|
|
2
|
-
import {
|
|
3
|
-
connectDeleteInputSchema,
|
|
4
|
-
connectDeleteOutputSchema,
|
|
5
|
-
connectListInputSchema,
|
|
6
|
-
connectListOutputSchema,
|
|
7
|
-
} from "./connect.dto";
|
|
8
|
-
import type { ConnectService } from "./connect.service";
|
|
9
|
-
|
|
10
|
-
export function createConnectTRPC(connectService: ConnectService) {
|
|
11
|
-
return router({
|
|
12
|
-
list: procedure
|
|
13
|
-
.input(connectListInputSchema)
|
|
14
|
-
.output(connectListOutputSchema)
|
|
15
|
-
.query(async ({ ctx, input }) => {
|
|
16
|
-
return handleTRPCResult(await connectService.list(input, ctx));
|
|
17
|
-
}),
|
|
18
|
-
|
|
19
|
-
delete: procedure
|
|
20
|
-
.input(connectDeleteInputSchema)
|
|
21
|
-
.output(connectDeleteOutputSchema)
|
|
22
|
-
.mutation(async ({ ctx, input }) => {
|
|
23
|
-
return handleTRPCResult(await connectService.delete(input, ctx));
|
|
24
|
-
}),
|
|
25
|
-
});
|
|
26
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
export interface ConnectProvider {
|
|
2
|
-
id: string;
|
|
3
|
-
clientId: string;
|
|
4
|
-
clientSecret: string;
|
|
5
|
-
redirectUri: string;
|
|
6
|
-
scopes: string[];
|
|
7
|
-
issuerConfig?: {
|
|
8
|
-
issuer: string;
|
|
9
|
-
authorization_endpoint: string;
|
|
10
|
-
token_endpoint: string;
|
|
11
|
-
userinfo_endpoint?: string;
|
|
12
|
-
};
|
|
13
|
-
issuerUrl?: string; // For auto-discovery
|
|
14
|
-
supportsPKCE?: boolean; // Whether provider supports PKCE (default: true)
|
|
15
|
-
mapProfile: (accessToken: string) => Promise<ConnectProfile>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface ConnectProfile {
|
|
19
|
-
providerAccountId: string;
|
|
20
|
-
displayName?: string;
|
|
21
|
-
avatarUrl?: string;
|
|
22
|
-
handle?: string;
|
|
23
|
-
accountType: "user" | "page" | "org" | "channel";
|
|
24
|
-
parentId?: string;
|
|
25
|
-
metadata?: Record<string, unknown>;
|
|
26
|
-
}
|
|
27
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { integer, real, sqliteTable as table, text } from "drizzle-orm/sqlite-core";
|
|
2
|
-
import { v4 as uuidv4 } from "uuid";
|
|
3
|
-
|
|
4
|
-
export const cryptoPayments = table("crypto_payments", {
|
|
5
|
-
id: text("id").primaryKey().$default(uuidv4),
|
|
6
|
-
createdAt: integer("created_at", { mode: "timestamp" })
|
|
7
|
-
.notNull()
|
|
8
|
-
.$default(() => new Date()),
|
|
9
|
-
updatedAt: integer("updated_at", { mode: "timestamp" }),
|
|
10
|
-
address: text("address").notNull(),
|
|
11
|
-
referenceId: text("reference_id").notNull(),
|
|
12
|
-
status: text("status").notNull().default("pending"),
|
|
13
|
-
derivationIndex: integer("derivation_index").notNull(),
|
|
14
|
-
amountExpected: real("amount_expected").notNull(),
|
|
15
|
-
});
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { LibSQLDatabase } from "drizzle-orm/libsql";
|
|
2
|
-
import { BaseTableRepository } from "#modules/base/base.repository";
|
|
3
|
-
import * as crypto from "#modules/crypto/crypto.db";
|
|
4
|
-
|
|
5
|
-
const schema = { ...crypto };
|
|
6
|
-
type Schema = typeof schema;
|
|
7
|
-
|
|
8
|
-
export class CryptoRepository extends BaseTableRepository<
|
|
9
|
-
LibSQLDatabase<Schema>,
|
|
10
|
-
Schema,
|
|
11
|
-
Record<string, never>,
|
|
12
|
-
Schema["cryptoPayments"]
|
|
13
|
-
> {}
|