insforge 1.2.10 → 1.3.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/.claude-plugin/marketplace.json +20 -20
- package/.dockerignore +60 -60
- package/.env.example +83 -77
- package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -36
- package/.github/ISSUE_TEMPLATE/config.yml +11 -11
- package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -26
- package/.github/PULL_REQUEST_TEMPLATE.md +7 -7
- package/.github/copilot-instructions.md +146 -146
- package/.github/workflows/build-image.yml +65 -65
- package/.github/workflows/ci-premerge-check.yml +23 -23
- package/.github/workflows/e2e.yml +63 -63
- package/.github/workflows/lint-and-format.yml +32 -32
- package/.prettierignore +64 -64
- package/CHANGELOG.md +44 -44
- package/CLAUDE_PLUGIN.md +104 -104
- package/CODE_OF_CONDUCT.md +128 -128
- package/CONTRIBUTING.md +125 -125
- package/Dockerfile +30 -30
- package/GITHUB_OAUTH_SETUP.md +49 -49
- package/GOOGLE_OAUTH_SETUP.md +148 -148
- package/LICENSE +201 -201
- package/README.md +182 -182
- package/assets/Dark.svg +23 -23
- package/auth/package.json +28 -28
- package/auth/src/lib/broadcastService.ts +117 -115
- package/auth/src/pages/SignInPage.tsx +60 -57
- package/auth/src/pages/SignUpPage.tsx +60 -57
- package/auth/tsconfig.json +32 -32
- package/auth/tsconfig.node.json +11 -11
- package/backend/package.json +78 -75
- package/backend/src/api/routes/ai/index.routes.ts +3 -3
- package/backend/src/api/routes/auth/index.routes.ts +667 -570
- package/backend/src/api/routes/auth/oauth.routes.ts +473 -448
- package/backend/src/api/routes/database/advance.routes.ts +37 -16
- package/backend/src/api/routes/database/index.routes.ts +78 -1
- package/backend/src/api/routes/database/records.routes.ts +10 -10
- package/backend/src/api/routes/database/tables.routes.ts +0 -14
- package/backend/src/api/routes/docs/index.routes.ts +75 -76
- package/backend/src/api/routes/email/index.routes.ts +35 -0
- package/backend/src/api/routes/functions/index.routes.ts +18 -12
- package/backend/src/api/routes/metadata/index.routes.ts +12 -0
- package/backend/src/api/routes/realtime/channels.routes.ts +81 -0
- package/backend/src/api/routes/realtime/index.routes.ts +12 -0
- package/backend/src/api/routes/realtime/messages.routes.ts +48 -0
- package/backend/src/api/routes/realtime/permissions.routes.ts +19 -0
- package/backend/src/api/routes/storage/index.routes.ts +18 -12
- package/backend/src/api/routes/usage/index.routes.ts +6 -4
- package/backend/src/infra/database/database.manager.ts +14 -1
- package/backend/src/infra/database/migrations/000_create-base-tables.sql +141 -141
- package/backend/src/infra/database/migrations/001_create-helper-functions.sql +40 -40
- package/backend/src/infra/database/migrations/002_rename-auth-tables.sql +29 -29
- package/backend/src/infra/database/migrations/003_create-users-table.sql +55 -55
- package/backend/src/infra/database/migrations/004_add-reload-postgrest-func.sql +23 -23
- package/backend/src/infra/database/migrations/005_enable-project-admin-modify-users.sql +29 -29
- package/backend/src/infra/database/migrations/006_modify-ai-usage-table.sql +24 -24
- package/backend/src/infra/database/migrations/007_drop-metadata-table.sql +1 -1
- package/backend/src/infra/database/migrations/008_add-system-tables.sql +76 -76
- package/backend/src/infra/database/migrations/009_add-function-secrets.sql +23 -23
- package/backend/src/infra/database/migrations/010_modify-ai-config-modalities.sql +93 -93
- package/backend/src/infra/database/migrations/011_refactor-secrets-table.sql +15 -15
- package/backend/src/infra/database/migrations/012_add-storage-uploaded-by.sql +7 -7
- package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -44
- package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +7 -7
- package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +59 -59
- package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -24
- package/backend/src/infra/database/migrations/017_create-realtime-schema.sql +233 -0
- package/backend/src/infra/realtime/realtime.manager.ts +246 -0
- package/backend/src/infra/realtime/webhook-sender.ts +82 -0
- package/backend/src/infra/security/token.manager.ts +219 -125
- package/backend/src/infra/socket/socket.manager.ts +198 -64
- package/backend/src/providers/ai/openrouter.provider.ts +12 -9
- package/backend/src/providers/email/base.provider.ts +4 -7
- package/backend/src/providers/email/cloud.provider.ts +84 -0
- package/backend/src/providers/oauth/apple.provider.ts +266 -0
- package/backend/src/providers/oauth/index.ts +1 -0
- package/backend/src/server.ts +317 -284
- package/backend/src/services/ai/ai-model.service.ts +5 -5
- package/backend/src/services/ai/chat-completion.service.ts +4 -4
- package/backend/src/services/ai/image-generation.service.ts +3 -3
- package/backend/src/services/auth/auth.service.ts +14 -0
- package/backend/src/services/database/database-table.service.ts +0 -9
- package/backend/src/services/database/database.service.ts +127 -0
- package/backend/src/services/email/email.service.ts +5 -7
- package/backend/src/services/realtime/index.ts +3 -0
- package/backend/src/services/realtime/realtime-auth.service.ts +104 -0
- package/backend/src/services/realtime/realtime-channel.service.ts +237 -0
- package/backend/src/services/realtime/realtime-message.service.ts +260 -0
- package/backend/src/types/auth.ts +11 -0
- package/backend/src/types/realtime.ts +18 -0
- package/backend/src/types/socket.ts +7 -31
- package/backend/src/utils/cookies.ts +35 -0
- package/backend/src/utils/s3-config-loader.ts +64 -0
- package/backend/src/utils/seed.ts +301 -298
- package/backend/src/utils/sql-parser.ts +90 -0
- package/backend/tests/README.md +133 -133
- package/backend/tests/cleanup-all-test-data.sh +230 -230
- package/backend/tests/cloud/test-s3-multitenant.sh +131 -131
- package/backend/tests/local/comprehensive-curl-tests.sh +155 -155
- package/backend/tests/local/test-ai-config.sh +129 -129
- package/backend/tests/local/test-ai-usage.sh +80 -80
- package/backend/tests/local/test-auth-router.sh +143 -143
- package/backend/tests/local/test-database-router.sh +222 -222
- package/backend/tests/local/test-e2e.sh +240 -240
- package/backend/tests/local/test-fk-errors.sh +96 -96
- package/backend/tests/local/test-functions.sh +123 -123
- package/backend/tests/local/test-id-field.sh +200 -200
- package/backend/tests/local/test-logs.sh +132 -132
- package/backend/tests/local/test-public-bucket.sh +264 -264
- package/backend/tests/local/test-secrets.sh +249 -249
- package/backend/tests/local/test-serverless-functions.sh.disabled +325 -325
- package/backend/tests/local/test-traditional-rest.sh +208 -208
- package/backend/tests/manual/README.md +50 -50
- package/backend/tests/manual/create-large-table-simple.sql +10 -10
- package/backend/tests/manual/seed-large-table.sql +100 -100
- package/backend/tests/manual/setup-large-table-extras.sql +33 -33
- package/backend/tests/manual/test-bulk-upsert.sh +409 -409
- package/backend/tests/manual/test-database-advance.sh +296 -296
- package/backend/tests/manual/test-postgrest-stability.sh +191 -191
- package/backend/tests/manual/test-rawsql-export-import.sh +411 -411
- package/backend/tests/manual/test-rawsql-modes.sh +244 -244
- package/backend/tests/manual/test-universal-storage.sh +263 -263
- package/backend/tests/manual/test-users.sql +17 -17
- package/backend/tests/run-all-tests.sh +139 -139
- package/backend/tests/setup.ts +0 -0
- package/backend/tests/test-config.sh +338 -338
- package/backend/tests/unit/analyze-query.test.ts +697 -0
- package/backend/tsconfig.json +22 -22
- package/claude-plugin/.claude-plugin/plugin.json +24 -24
- package/claude-plugin/README.md +133 -133
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +270 -270
- package/docker-compose.prod.yml +204 -200
- package/docker-compose.yml +232 -228
- package/docker-init/db/db-init.sql +97 -97
- package/docker-init/db/jwt.sql +5 -5
- package/docker-init/db/postgresql.conf +16 -16
- package/docker-init/logs/vector.yml +236 -236
- package/docs/README.md +44 -44
- package/docs/agent-docs/real-time.md +269 -0
- package/docs/changelog.mdx +119 -67
- package/docs/core-concepts/ai/architecture.mdx +372 -372
- package/docs/core-concepts/ai/sdk.mdx +213 -213
- package/docs/core-concepts/authentication/architecture.mdx +278 -278
- package/docs/core-concepts/authentication/sdk.mdx +414 -414
- package/docs/core-concepts/authentication/ui-components/customization.mdx +529 -529
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +221 -221
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +184 -184
- package/docs/core-concepts/authentication/ui-components/react.mdx +129 -129
- package/docs/core-concepts/database/architecture.mdx +255 -255
- package/docs/core-concepts/database/sdk.mdx +382 -382
- package/docs/core-concepts/email/architecture.mdx +101 -0
- package/docs/core-concepts/email/sdk.mdx +53 -0
- package/docs/core-concepts/functions/architecture.mdx +105 -105
- package/docs/core-concepts/functions/sdk.mdx +184 -184
- package/docs/core-concepts/realtime/architecture.mdx +446 -0
- package/docs/core-concepts/realtime/sdk.mdx +409 -0
- package/docs/core-concepts/storage/architecture.mdx +243 -243
- package/docs/core-concepts/storage/sdk.mdx +253 -253
- package/docs/deployment/README.md +94 -94
- package/docs/deployment/deploy-to-aws-ec2.md +564 -564
- package/docs/deployment/deploy-to-azure-virtual-machines.md +312 -312
- package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -613
- package/docs/deployment/deploy-to-render.md +441 -441
- package/docs/deprecated/insforge-auth-api.md +214 -214
- package/docs/deprecated/insforge-auth-sdk.md +99 -99
- package/docs/deprecated/insforge-db-api.md +358 -358
- package/docs/deprecated/insforge-db-sdk.md +139 -139
- package/docs/deprecated/insforge-debug-sdk.md +156 -156
- package/docs/deprecated/insforge-debug.md +64 -64
- package/docs/deprecated/insforge-instructions.md +123 -123
- package/docs/deprecated/insforge-project.md +117 -117
- package/docs/deprecated/insforge-storage-api.md +278 -278
- package/docs/deprecated/insforge-storage-sdk.md +158 -158
- package/docs/docs.json +232 -210
- package/docs/examples/framework-guides/nextjs.mdx +131 -131
- package/docs/examples/framework-guides/nuxt.mdx +165 -165
- package/docs/examples/framework-guides/react.mdx +165 -165
- package/docs/examples/framework-guides/svelte.mdx +153 -153
- package/docs/examples/framework-guides/vue.mdx +159 -159
- package/docs/examples/overview.mdx +67 -67
- package/docs/favicon.svg +19 -19
- package/docs/images/changelog/dec-2025/ai-integration.png +0 -0
- package/docs/images/changelog/dec-2025/ai-models.webp +0 -0
- package/docs/images/changelog/dec-2025/alipay-payment.webp +0 -0
- package/docs/images/changelog/dec-2025/apple-login.jpg +0 -0
- package/docs/images/changelog/dec-2025/mcp-installer.png +0 -0
- package/docs/images/changelog/dec-2025/realtime-module.jpg +0 -0
- package/docs/images/icons/ai.svg +4 -4
- package/docs/images/logos/nextjs.svg +4 -4
- package/docs/images/logos/nuxt.svg +4 -4
- package/docs/images/logos/react.svg +5 -5
- package/docs/images/logos/svelte.svg +4 -4
- package/docs/images/logos/vue.svg +5 -5
- package/docs/insforge-instructions-sdk.md +89 -88
- package/docs/introduction.mdx +45 -45
- package/docs/logo/dark.svg +22 -22
- package/docs/logo/light.svg +20 -20
- package/docs/partnership.mdx +651 -646
- package/docs/quickstart.mdx +82 -82
- package/docs/showcase.mdx +52 -52
- package/docs/snippets/sdk-installation.mdx +21 -21
- package/docs/snippets/service-icons.mdx +27 -27
- package/examples/oauth/frontend-oauth-example.html +250 -250
- package/examples/response-examples.md +443 -443
- package/frontend/components.json +17 -17
- package/frontend/package.json +69 -69
- package/frontend/src/assets/icons/checkbox_checked.svg +6 -6
- package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -6
- package/frontend/src/assets/icons/checked.svg +3 -3
- package/frontend/src/assets/icons/connected.svg +3 -3
- package/frontend/src/assets/icons/error.svg +3 -3
- package/frontend/src/assets/icons/loader.svg +9 -9
- package/frontend/src/assets/icons/pencil.svg +4 -4
- package/frontend/src/assets/icons/refresh.svg +4 -4
- package/frontend/src/assets/icons/step_active.svg +3 -3
- package/frontend/src/assets/icons/step_inactive.svg +11 -11
- package/frontend/src/assets/icons/warning.svg +3 -3
- package/frontend/src/assets/logos/apple.svg +3 -3
- package/frontend/src/assets/logos/claude_code.svg +3 -3
- package/frontend/src/assets/logos/cline.svg +6 -6
- package/frontend/src/assets/logos/cursor.svg +20 -20
- package/frontend/src/assets/logos/discord.svg +8 -8
- package/frontend/src/assets/logos/facebook.svg +3 -3
- package/frontend/src/assets/logos/gemini.svg +19 -19
- package/frontend/src/assets/logos/github.svg +5 -5
- package/frontend/src/assets/logos/google.svg +13 -13
- package/frontend/src/assets/logos/grok.svg +10 -10
- package/frontend/src/assets/logos/insforge_dark.svg +15 -15
- package/frontend/src/assets/logos/insforge_light.svg +15 -15
- package/frontend/src/assets/logos/instagram.svg +1 -1
- package/frontend/src/assets/logos/linkedin.svg +3 -3
- package/frontend/src/assets/logos/openai.svg +10 -10
- package/frontend/src/assets/logos/roo_code.svg +9 -9
- package/frontend/src/assets/logos/spotify.svg +16 -16
- package/frontend/src/assets/logos/tiktok.svg +5 -5
- package/frontend/src/assets/logos/trae.svg +3 -3
- package/frontend/src/assets/logos/windsurf.svg +10 -10
- package/frontend/src/assets/logos/x.svg +3 -3
- package/frontend/src/components/layout/AppHeader.tsx +9 -10
- package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +1 -0
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +6 -0
- package/frontend/src/features/auth/helpers.tsx +8 -0
- package/frontend/src/features/auth/{page → pages}/UsersPage.tsx +0 -28
- package/frontend/src/features/database/components/SQLModal.tsx +75 -0
- package/frontend/src/features/database/components/TableForm.tsx +0 -4
- package/frontend/src/features/database/hooks/useDatabase.ts +66 -0
- package/frontend/src/features/database/hooks/useTables.ts +32 -28
- package/frontend/src/features/database/index.ts +1 -0
- package/frontend/src/features/database/{page → pages}/FunctionsPage.tsx +29 -37
- package/frontend/src/features/database/{page → pages}/IndexesPage.tsx +35 -47
- package/frontend/src/features/database/{page → pages}/PoliciesPage.tsx +43 -54
- package/frontend/src/features/database/{page → pages}/TablesPage.tsx +0 -42
- package/frontend/src/features/database/{page → pages}/TriggersPage.tsx +35 -47
- package/frontend/src/features/database/services/advance.service.ts +0 -26
- package/frontend/src/features/database/services/database.service.ts +55 -0
- package/frontend/src/features/database/services/table.service.ts +0 -6
- package/frontend/src/features/functions/{page → pages}/FunctionsPage.tsx +21 -44
- package/frontend/src/features/functions/{page → pages}/SecretsPage.tsx +11 -9
- package/frontend/src/features/logs/hooks/useMcpUsage.ts +13 -66
- package/frontend/src/features/realtime/components/ChannelRow.tsx +83 -0
- package/frontend/src/features/realtime/components/EditChannelModal.tsx +246 -0
- package/frontend/src/features/realtime/components/MessageRow.tsx +85 -0
- package/frontend/src/features/realtime/components/RealtimeEmptyState.tsx +30 -0
- package/frontend/src/features/realtime/hooks/useRealtime.ts +218 -0
- package/frontend/src/features/realtime/index.ts +11 -0
- package/frontend/src/features/realtime/pages/RealtimeChannelsPage.tsx +172 -0
- package/frontend/src/features/realtime/pages/RealtimeMessagesPage.tsx +211 -0
- package/frontend/src/features/realtime/pages/RealtimePermissionsPage.tsx +191 -0
- package/frontend/src/features/realtime/services/realtime.service.ts +107 -0
- package/frontend/src/features/storage/{page → pages}/StoragePage.tsx +1 -29
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +3 -3
- package/frontend/src/features/visualizer/{page → pages}/VisualizerPage.tsx +1 -35
- package/frontend/src/lib/contexts/SocketContext.tsx +119 -75
- package/frontend/src/lib/routing/AppRoutes.tsx +35 -20
- package/frontend/src/lib/utils/cloudMessaging.ts +1 -1
- package/frontend/src/lib/utils/menuItems.ts +24 -0
- package/frontend/src/lib/utils/utils.ts +14 -1
- package/frontend/tsconfig.json +25 -25
- package/frontend/tsconfig.node.json +9 -9
- package/functions/deno.json +24 -24
- package/functions/server.ts +315 -315
- package/i18n/README.ar.md +130 -130
- package/i18n/README.de.md +130 -130
- package/i18n/README.es.md +154 -154
- package/i18n/README.fr.md +134 -134
- package/i18n/README.hi.md +129 -129
- package/i18n/README.ja.md +174 -174
- package/i18n/README.ko.md +136 -136
- package/i18n/README.pt-BR.md +131 -131
- package/i18n/README.ru.md +129 -129
- package/i18n/README.zh-CN.md +133 -133
- package/openapi/ai.yaml +715 -715
- package/openapi/auth.yaml +1244 -1244
- package/openapi/email.yaml +158 -0
- package/openapi/functions.yaml +475 -475
- package/openapi/health.yaml +29 -29
- package/openapi/logs.yaml +223 -223
- package/openapi/metadata.yaml +177 -177
- package/openapi/realtime.yaml +699 -0
- package/openapi/records.yaml +381 -381
- package/openapi/secrets.yaml +370 -370
- package/openapi/storage.yaml +875 -875
- package/openapi/tables.yaml +463 -463
- package/package.json +97 -97
- package/shared-schemas/package.json +31 -31
- package/shared-schemas/src/ai.schema.ts +63 -59
- package/shared-schemas/src/auth-api.schema.ts +352 -339
- package/shared-schemas/src/auth.schema.ts +1 -1
- package/shared-schemas/src/database-api.schema.ts +32 -1
- package/shared-schemas/src/database.schema.ts +39 -0
- package/shared-schemas/src/docs.schema.ts +26 -0
- package/shared-schemas/src/email-api.schema.ts +30 -0
- package/shared-schemas/src/index.ts +4 -0
- package/shared-schemas/src/metadata.schema.ts +9 -0
- package/shared-schemas/src/realtime-api.schema.ts +111 -0
- package/shared-schemas/src/realtime.schema.ts +143 -0
- package/shared-schemas/tsconfig.json +21 -21
- package/tsconfig.json +7 -7
- package/zeabur/README.md +13 -13
- package/zeabur/template.yml +1032 -1032
- package/.cursor/rules/cursor-rules.mdc +0 -94
- package/frontend/src/features/database/hooks/useFullMetadata.ts +0 -18
- package/test-gemini.sh +0 -35
- package/test-usage-admin.sh +0 -57
- package/test-usage.sh +0 -50
- /package/frontend/src/features/ai/{page → pages}/AIPage.tsx +0 -0
- /package/frontend/src/features/auth/{page → pages}/AuthMethodsPage.tsx +0 -0
- /package/frontend/src/features/auth/{page → pages}/ConfigurationPage.tsx +0 -0
- /package/frontend/src/features/dashboard/{page → pages}/DashboardPage.tsx +0 -0
- /package/frontend/src/features/database/{page → pages}/SQLEditorPage.tsx +0 -0
- /package/frontend/src/features/database/{page → pages}/TemplatesPage.tsx +0 -0
- /package/frontend/src/features/login/{page → pages}/CloudLoginPage.tsx +0 -0
- /package/frontend/src/features/login/{page → pages}/LoginPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/AuditsPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/LogsPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/MCPLogsPage.tsx +0 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import logger from '@/utils/logger.js';
|
|
3
|
+
import { getApiBaseUrl } from '@/utils/environment.js';
|
|
4
|
+
import { OAuthConfigService } from '@/services/auth/oauth-config.service.js';
|
|
5
|
+
import type { AppleUserInfo, OAuthUserData } from '@/types/auth.js';
|
|
6
|
+
import { OAuthProvider } from './base.provider.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Apple OAuth Service
|
|
10
|
+
* Handles all Apple Sign In operations including URL generation, token exchange, and user info verification
|
|
11
|
+
*
|
|
12
|
+
* Apple OAuth specifics:
|
|
13
|
+
* - Uses OIDC with JWT id_token
|
|
14
|
+
* - Callback receives POST request with code, id_token, and user data
|
|
15
|
+
* - User info (name, email) is only provided on first authorization
|
|
16
|
+
* - client_secret is a JWT signed with Apple's private key
|
|
17
|
+
*/
|
|
18
|
+
export class AppleOAuthProvider implements OAuthProvider {
|
|
19
|
+
private static instance: AppleOAuthProvider;
|
|
20
|
+
|
|
21
|
+
private constructor() {
|
|
22
|
+
// No initialization needed - jose handles JWKS caching internally
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public static getInstance(): AppleOAuthProvider {
|
|
26
|
+
if (!AppleOAuthProvider.instance) {
|
|
27
|
+
AppleOAuthProvider.instance = new AppleOAuthProvider();
|
|
28
|
+
}
|
|
29
|
+
return AppleOAuthProvider.instance;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generate Apple OAuth authorization URL
|
|
34
|
+
*/
|
|
35
|
+
async generateOAuthUrl(state?: string): Promise<string> {
|
|
36
|
+
const oAuthConfigService = OAuthConfigService.getInstance();
|
|
37
|
+
const config = await oAuthConfigService.getConfigByProvider('apple');
|
|
38
|
+
|
|
39
|
+
if (!config) {
|
|
40
|
+
throw new Error('Apple OAuth not configured');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const selfBaseUrl = getApiBaseUrl();
|
|
44
|
+
|
|
45
|
+
if (config?.useSharedKey) {
|
|
46
|
+
if (!state) {
|
|
47
|
+
logger.warn('Shared Apple OAuth called without state parameter');
|
|
48
|
+
throw new Error('State parameter is required for shared Apple OAuth');
|
|
49
|
+
}
|
|
50
|
+
// Use shared keys if configured
|
|
51
|
+
const cloudBaseUrl = process.env.CLOUD_API_HOST || 'https://api.insforge.dev';
|
|
52
|
+
const redirectUri = `${selfBaseUrl}/api/auth/oauth/shared/callback/${state}`;
|
|
53
|
+
const response = await axios.get(
|
|
54
|
+
`${cloudBaseUrl}/auth/v1/shared/apple?redirect_uri=${encodeURIComponent(redirectUri)}`,
|
|
55
|
+
{
|
|
56
|
+
headers: {
|
|
57
|
+
'Content-Type': 'application/json',
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
return response.data.auth_url || response.data.url || '';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
logger.debug('Apple OAuth Config (fresh from DB):', {
|
|
65
|
+
clientId: config.clientId ? 'SET' : 'NOT SET',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const authUrl = new URL('https://appleid.apple.com/auth/authorize');
|
|
69
|
+
authUrl.searchParams.set('client_id', config.clientId ?? '');
|
|
70
|
+
authUrl.searchParams.set('redirect_uri', `${selfBaseUrl}/api/auth/oauth/apple/callback`);
|
|
71
|
+
authUrl.searchParams.set('response_type', 'code id_token');
|
|
72
|
+
authUrl.searchParams.set('response_mode', 'form_post');
|
|
73
|
+
authUrl.searchParams.set(
|
|
74
|
+
'scope',
|
|
75
|
+
config.scopes && config.scopes.length > 0 ? config.scopes.join(' ') : 'name email'
|
|
76
|
+
);
|
|
77
|
+
if (state) {
|
|
78
|
+
authUrl.searchParams.set('state', state);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return authUrl.toString();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Generate Apple client secret (JWT signed with private key)
|
|
86
|
+
* Apple requires a dynamically generated client_secret
|
|
87
|
+
*/
|
|
88
|
+
private async generateClientSecret(): Promise<string> {
|
|
89
|
+
const oAuthConfigService = OAuthConfigService.getInstance();
|
|
90
|
+
const config = await oAuthConfigService.getConfigByProvider('apple');
|
|
91
|
+
|
|
92
|
+
if (!config) {
|
|
93
|
+
throw new Error('Apple OAuth not configured');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Get additional config from client secret (stored as JSON with teamId, keyId, privateKey)
|
|
97
|
+
const secretData = await oAuthConfigService.getClientSecretByProvider('apple');
|
|
98
|
+
if (!secretData) {
|
|
99
|
+
throw new Error('Apple OAuth client secret not configured');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let appleConfig: { teamId: string; keyId: string; privateKey: string };
|
|
103
|
+
try {
|
|
104
|
+
appleConfig = JSON.parse(secretData);
|
|
105
|
+
} catch {
|
|
106
|
+
throw new Error(
|
|
107
|
+
'Apple OAuth client secret must be a JSON object with teamId, keyId, and privateKey'
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const { teamId, keyId, privateKey } = appleConfig;
|
|
112
|
+
|
|
113
|
+
if (!teamId || !keyId || !privateKey) {
|
|
114
|
+
throw new Error('Apple OAuth requires teamId, keyId, and privateKey in client secret');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Use jose to sign the client secret JWT
|
|
118
|
+
const { SignJWT, importPKCS8 } = await import('jose');
|
|
119
|
+
|
|
120
|
+
const key = await importPKCS8(privateKey, 'ES256');
|
|
121
|
+
|
|
122
|
+
const clientSecret = await new SignJWT({})
|
|
123
|
+
.setProtectedHeader({ alg: 'ES256', kid: keyId })
|
|
124
|
+
.setIssuer(teamId)
|
|
125
|
+
.setSubject(config.clientId ?? '')
|
|
126
|
+
.setAudience('https://appleid.apple.com')
|
|
127
|
+
.setIssuedAt()
|
|
128
|
+
.setExpirationTime('180d') // 180 days (Apple allows up to 6 months)
|
|
129
|
+
.sign(key);
|
|
130
|
+
|
|
131
|
+
return clientSecret;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Exchange Apple authorization code for tokens
|
|
136
|
+
*/
|
|
137
|
+
async exchangeCodeToToken(code: string): Promise<{ access_token: string; id_token: string }> {
|
|
138
|
+
const oAuthConfigService = OAuthConfigService.getInstance();
|
|
139
|
+
const config = await oAuthConfigService.getConfigByProvider('apple');
|
|
140
|
+
|
|
141
|
+
if (!config) {
|
|
142
|
+
throw new Error('Apple OAuth not configured');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
logger.info('Exchanging Apple code for tokens', {
|
|
147
|
+
hasCode: !!code,
|
|
148
|
+
clientId: config.clientId?.substring(0, 10) + '...',
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const clientSecret = await this.generateClientSecret();
|
|
152
|
+
const selfBaseUrl = getApiBaseUrl();
|
|
153
|
+
|
|
154
|
+
const body = new URLSearchParams({
|
|
155
|
+
client_id: config.clientId ?? '',
|
|
156
|
+
client_secret: clientSecret,
|
|
157
|
+
code,
|
|
158
|
+
grant_type: 'authorization_code',
|
|
159
|
+
redirect_uri: `${selfBaseUrl}/api/auth/oauth/apple/callback`,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const response = await axios.post('https://appleid.apple.com/auth/token', body.toString(), {
|
|
163
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (!response.data.id_token) {
|
|
167
|
+
throw new Error('Failed to get id_token from Apple');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
access_token: response.data.access_token || '',
|
|
172
|
+
id_token: response.data.id_token,
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (axios.isAxiosError(error) && error.response) {
|
|
176
|
+
logger.error('Apple token exchange failed', {
|
|
177
|
+
status: error.response.status,
|
|
178
|
+
error: error.response.data,
|
|
179
|
+
});
|
|
180
|
+
throw new Error(`Apple OAuth error: ${JSON.stringify(error.response.data)}`);
|
|
181
|
+
}
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Verify Apple ID token and extract user info
|
|
188
|
+
*/
|
|
189
|
+
async verifyIdToken(idToken: string): Promise<AppleUserInfo> {
|
|
190
|
+
const oAuthConfigService = OAuthConfigService.getInstance();
|
|
191
|
+
const config = await oAuthConfigService.getConfigByProvider('apple');
|
|
192
|
+
|
|
193
|
+
if (!config) {
|
|
194
|
+
throw new Error('Apple OAuth not configured');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const { createRemoteJWKSet, jwtVerify } = await import('jose');
|
|
199
|
+
const JWKS = createRemoteJWKSet(new URL('https://appleid.apple.com/auth/keys'));
|
|
200
|
+
|
|
201
|
+
const { payload } = await jwtVerify(idToken, JWKS, {
|
|
202
|
+
issuer: 'https://appleid.apple.com',
|
|
203
|
+
audience: config.clientId,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
sub: String(payload.sub),
|
|
208
|
+
email: (payload.email as string) || '',
|
|
209
|
+
email_verified: payload.email_verified === 'true' || payload.email_verified === true,
|
|
210
|
+
is_private_email: payload.is_private_email === 'true' || payload.is_private_email === true,
|
|
211
|
+
};
|
|
212
|
+
} catch (error) {
|
|
213
|
+
logger.error('Apple ID token verification failed:', error);
|
|
214
|
+
throw new Error(`Apple token verification failed: ${error}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Handle Apple OAuth callback
|
|
220
|
+
* Note: Apple sends a POST request with form data
|
|
221
|
+
*/
|
|
222
|
+
async handleCallback(payload: { code?: string; token?: string }): Promise<OAuthUserData> {
|
|
223
|
+
let appleUserInfo: AppleUserInfo;
|
|
224
|
+
|
|
225
|
+
if (payload.token) {
|
|
226
|
+
// Token provided directly (e.g., from mobile app)
|
|
227
|
+
appleUserInfo = await this.verifyIdToken(payload.token);
|
|
228
|
+
} else if (payload.code) {
|
|
229
|
+
// Exchange code for tokens
|
|
230
|
+
const tokens = await this.exchangeCodeToToken(payload.code);
|
|
231
|
+
appleUserInfo = await this.verifyIdToken(tokens.id_token);
|
|
232
|
+
} else {
|
|
233
|
+
throw new Error('No authorization code or token provided');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Transform Apple user info to generic format
|
|
237
|
+
// Note: Apple only provides name on first authorization, so we use email as fallback
|
|
238
|
+
const userName = appleUserInfo.name || appleUserInfo.email.split('@')[0];
|
|
239
|
+
return {
|
|
240
|
+
provider: 'apple',
|
|
241
|
+
providerId: appleUserInfo.sub,
|
|
242
|
+
email: appleUserInfo.email,
|
|
243
|
+
userName,
|
|
244
|
+
avatarUrl: '', // Apple doesn't provide avatar
|
|
245
|
+
identityData: appleUserInfo,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Handle shared callback payload transformation
|
|
251
|
+
*/
|
|
252
|
+
handleSharedCallback(payloadData: Record<string, unknown>): OAuthUserData {
|
|
253
|
+
const providerId = String(payloadData.providerId ?? '');
|
|
254
|
+
const email = String(payloadData.email ?? '');
|
|
255
|
+
const name = String(payloadData.name ?? '');
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
provider: 'apple',
|
|
259
|
+
providerId,
|
|
260
|
+
email,
|
|
261
|
+
userName: name || email.split('@')[0],
|
|
262
|
+
avatarUrl: '',
|
|
263
|
+
identityData: payloadData,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -5,3 +5,4 @@ export { LinkedInOAuthProvider } from './linkedin.provider.js';
|
|
|
5
5
|
export { FacebookOAuthProvider } from './facebook.provider.js';
|
|
6
6
|
export { MicrosoftOAuthProvider } from './microsoft.provider.js';
|
|
7
7
|
export { XOAuthProvider } from './x.provider.js';
|
|
8
|
+
export { AppleOAuthProvider } from './apple.provider.js';
|