-
-
Notifications
You must be signed in to change notification settings - Fork 67
Feature/sso 03 frontend #1379
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Feature/sso 03 frontend #1379
Conversation
Consolidates 12 incremental SSO migrations (20251224022658 through 20260106000000) into a single comprehensive migration. Schema includes: - Tables: org_saml_connections, saml_domain_mappings, sso_audit_logs - Functions: check_org_sso_configured, lookup_sso_provider_*, auto_join_* - Triggers: auto_join_sso_user_trigger, check_sso_domain_on_signup_trigger - RLS policies for all tables - Indexes for performance - Single SSO per org constraint (UNIQUE org_id, entity_id) - auto_join_enabled flag for controlling enrollment This is PR #1 of the SSO feature split (schema foundation only). No backend endpoints, no frontend, no tests included yet. Related: feature/sso-saml-authentication
|
Warning Rate limit exceeded@jokabuyasina has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 49 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds end-to-end SSO/SAML support: frontend SSO pages and composable, backend endpoints and Edge function for mock SSO, DB migration with SSO tables/functions/triggers/policies, updates to backend utilities and Supabase types, plus related seed, gitignore, and i18n changes. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Frontend as Frontend (sso-login.vue / useSSODetection)
participant Functions as Supabase Functions (/functions/v1/sso_check)
participant SSO as Identity Provider
User->>Frontend: Enter email & continue
Frontend->>Frontend: validate email & filter public domains
Frontend->>Functions: POST /functions/v1/sso_check (domain)
Functions-->>Frontend: provider_id, entity_id (or none)
alt SSO configured
Frontend->>Frontend: store provider/entity, compute redirect
Frontend->>SSO: initiate SSO (redirect with RelayState/email)
SSO-->>User: SSO auth flow and callback redirect
User->>Frontend: arrives via callback (session established)
else Not configured
Frontend-->>User: show standard login or error
end
sequenceDiagram
participant Admin
participant SettingsUI as Settings UI (sso.vue)
participant Backend as API/Functions
participant DB as Database
participant IdP as Identity Provider
Admin->>SettingsUI: Open SSO settings
SettingsUI->>Backend: GET org SSO config
Backend->>DB: query org_saml_connections & mappings
DB-->>Backend: return config
Backend-->>SettingsUI: present config
Admin->>SettingsUI: Submit IdP metadata & domains
SettingsUI->>Backend: POST /private/sso_configure
Backend->>IdP: fetch/validate metadata (if URL)
IdP-->>Backend: metadata
Backend->>DB: insert/update org_saml_connections and saml_domain_mappings
Backend-->>SettingsUI: saved confirmation
Admin->>SettingsUI: Click test
SettingsUI->>Backend: POST /private/sso_test
Backend->>DB: validate config & return checks/results
Backend-->>SettingsUI: display test results
Estimated code review effort🎯 4 (Complex) | ⏱️ ~70 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In @messages/en.json:
- Line 1200: Update the localization entry for the key
"remove-bundle-from-this-channel" to correct the typo by changing "budnle" to
"bundle" in the messages/en.json translations so the displayed text reads
"Remove bundle from this channel".
- Line 439: The translation key "choose-which-channel-to-link-this-bundle-to"
contains typos in its value; update the string value to correct "witch" ->
"which" and "channek" -> "channel" so it reads "Choose which channel to link
this bundle to" while keeping the same JSON key unchanged.
In @src/pages/sso-login.vue:
- Line 11: The TypeScript error comes from passing a string literal to useRoute;
remove the hardcoded '/sso-login' argument and call useRoute() with no
parameters, or ensure the route name is present in your typed router definitions
by rebuilding/generating types (check RouteNamedMap in src/typed-router.d.ts and
confirm the auto-generated file-based route includes the sso-login entry);
either update the route types so useRoute('sso-login') is valid or switch to
useRoute() without arguments to fix the type error.
🧹 Nitpick comments (5)
src/composables/useSSODetection.ts (2)
31-33: Consider defensive handling for edge cases.While the
@check on line 40 prevents callingextractDomainwith invalid emails, the function itself is fragile. If called independently,email.split('@')[1]could returnundefined.♻️ Strengthen the function defensively
function extractDomain(email: string): string { - return email.split('@')[1]?.toLowerCase() || '' + const parts = email.split('@') + return parts.length === 2 ? parts[1].toLowerCase() : '' }
49-50: Consider externalizing the public domain list.Hardcoding the public email providers makes maintenance harder. Consider moving this to a configuration file or environment variable, especially as this list may need updates (e.g., adding protonmail.com, aol.com, etc.).
src/pages/sso-login.vue (1)
27-27: Remove console.log statements before production.Multiple console.log statements are present throughout the SSO flow (lines 27, 32, 34, 37, 48, 50, 53). While helpful for debugging, these should be removed or converted to proper logging before merging to production.
🧹 Remove debug logs
- console.log('🔵 SSO Login - Starting flow for:', form.email) isLoading.value = true try { // Check if SSO is available for this domain - console.log('🔵 SSO Login - Checking SSO availability...') const hasSSO = await checkSSO(form.email) - console.log('🔵 SSO Login - SSO available:', hasSSO) if (!hasSSO) { - console.error('❌ SSO Login - SSO not configured for this email domain') toast.error(t('sso-not-configured', 'SSO is not configured for this email domain. Please contact your administrator.')) isLoading.value = false return } // ... rest of function ... - console.log('🔵 SSO Login - Initiating SSO with redirectTo:', redirectTo) await initiateSSO(redirectTo, form.email) - console.log('🔵 SSO Login - initiateSSO completed (should have redirected)') } catch (error: any) { - console.error('❌ SSO login error:', error) toast.error(t('sso-login-failed', 'Failed to initiate SSO login')) isLoading.value = false }Also applies to: 32-32, 34-34, 37-37, 48-48, 50-50, 53-53
src/pages/settings/organization/sso.vue (2)
343-349: Domain validation could be more robust.The domain validation (lines 343-349) performs basic checks:
- Strips leading
@symbols- Checks for
.presence- Checks minimum length of 3 characters
Consider adding:
- TLD validation (e.g., ensure at least 2 characters after last dot)
- Regex pattern for valid domain characters
- Check for invalid patterns like
..or leading/trailing dots🔒 Strengthen domain validation
- // Basic validation - if (!domain.includes('.') || domain.length < 3) { + // Validate domain format + const domainRegex = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*\.[a-z]{2,}$/i + if (!domainRegex.test(domain)) { toast.error(t('invalid-domain-format', 'Invalid domain format. Please enter a valid domain like "company.com"')) return }
1-50: Consider splitting this large component.At 1321 lines, this component is quite large and handles multiple concerns:
- Configuration state management
- API interactions for CRUD operations
- Multi-step wizard UI
- Toggle state management
- Test results display
While the current implementation works, consider extracting:
- API calls into a composable (e.g.,
useSSOConfig)- Wizard steps into separate components
- Test results into a dedicated component
This would improve maintainability and testability. However, given this is initial SSO feature development and the "Chill" review setting, this can be deferred to future refactoring.
Also applies to: 788-1321
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
messages/en.jsonsrc/composables/useSSODetection.tssrc/constants/organizationTabs.tssrc/pages/login.vuesrc/pages/settings/organization/sso.vuesrc/pages/sso-login.vuesrc/types/supabase.types.tssupabase/functions/_backend/utils/supabase.types.ts
🧰 Additional context used
📓 Path-based instructions (10)
src/**/*.vue
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.vue: Use Vue 3 with Composition API and<script setup>syntax for frontend components
Style components using TailwindCSS with DaisyUI components
src/**/*.vue: Use Vue 3<script setup>syntax exclusively for all Vue component scripts
Use Tailwind utility classes for layout and spacing in Vue components
Use DaisyUI components (d-btn,d-input,d-card) for interactive elements in Vue components
Use Konsta components ONLY for safe area helpers (top/bottom insets) in Vue components; avoid other uses
UseuseRoute()fromvue-routerto access route parameters anduseRouter()for programmatic navigation in Vue componentsUse DaisyUI (
d-prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent
Files:
src/pages/settings/organization/sso.vuesrc/pages/sso-login.vuesrc/pages/login.vue
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use single quotes and no semicolons per @antfu/eslint-config
Files:
src/pages/settings/organization/sso.vuesrc/constants/organizationTabs.tssrc/composables/useSSODetection.tssrc/pages/sso-login.vuesrc/pages/login.vue
src/pages/**/*.vue
📄 CodeRabbit inference engine (CLAUDE.md)
Use file-based routing with unplugin-vue-router for frontend pages
Frontend file-based routing uses
src/pages/directory structure; routes auto-generate withunplugin-vue-routerand types are available insrc/typed-router.d.ts
Files:
src/pages/settings/organization/sso.vuesrc/pages/sso-login.vuesrc/pages/login.vue
{capacitor.config.{ts,js},src/**/*.{ts,tsx,vue}}
📄 CodeRabbit inference engine (CLAUDE.md)
Mobile apps should use Capacitor with app ID
ee.forgr.capacitor_gofor native mobile functionality
Files:
src/pages/settings/organization/sso.vuesrc/constants/organizationTabs.tssrc/composables/useSSODetection.tssrc/pages/sso-login.vuesrc/pages/login.vue
src/**/*.{ts,tsx,vue,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use
~/alias for imports fromsrc/directory in frontend TypeScript and Vue components
Files:
src/pages/settings/organization/sso.vuesrc/constants/organizationTabs.tssrc/composables/useSSODetection.tssrc/pages/sso-login.vuesrc/pages/login.vue
src/**/*.{vue,ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Frontend ESLint must pass before commit; run
bun lint:fixto auto-fix issues in frontend files
Files:
src/pages/settings/organization/sso.vuesrc/constants/organizationTabs.tssrc/composables/useSSODetection.tssrc/pages/sso-login.vuesrc/pages/login.vue
src/**/*.{vue,css,scss}
📄 CodeRabbit inference engine (AGENTS.md)
The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS
Files:
src/pages/settings/organization/sso.vuesrc/pages/sso-login.vuesrc/pages/login.vue
src/**/*.{vue,ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Konsta components are reserved for the safe area helpers; avoid importing
konstaanywhere else in the app
Files:
src/pages/settings/organization/sso.vuesrc/constants/organizationTabs.tssrc/composables/useSSODetection.tssrc/pages/sso-login.vuesrc/pages/login.vue
src/**/*.{css,scss,vue}
📄 CodeRabbit inference engine (AGENTS.md)
Mirror the Capgo design palette from
src/styles/style.css(e.g.,--color-primary-500: #515271,--color-azure-500: #119eff) when introducing new UI, using deep slate bases with the Extract azure highlight and soft radii
Files:
src/pages/settings/organization/sso.vuesrc/pages/sso-login.vuesrc/pages/login.vue
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript strict mode with path aliases mapping
~/tosrc/
Files:
src/constants/organizationTabs.tssrc/composables/useSSODetection.ts
🧠 Learnings (5)
📚 Learning: 2025-12-23T01:19:04.593Z
Learnt from: riderx
Repo: Cap-go/capgo PR: 1297
File: src/components/dashboard/DeploymentBanner.vue:77-79
Timestamp: 2025-12-23T01:19:04.593Z
Learning: In the Cap-go codebase, ensure that app permission checks never include the role 'owner'. App-level permissions should be based on the user_min_right enum with values: read, upload, write, admin, super_admin (and NOT owner). This pattern applies across Vue components that perform permission checks; if you find a check referencing 'owner' for app-level access, replace it with the appropriate user_min_right value and keep organization-level owner handling in organization.ts.
Applied to files:
src/pages/settings/organization/sso.vuesrc/pages/sso-login.vuesrc/pages/login.vue
📚 Learning: 2025-10-30T14:58:37.007Z
Learnt from: Dalanir
Repo: Cap-go/capgo PR: 1238
File: src/layouts/settings.vue:7-13
Timestamp: 2025-10-30T14:58:37.007Z
Learning: In the Cap-go/capgo repository, heroicons are imported using the pattern `~icons/heroicons/icon-name` (e.g., `~icons/heroicons/user`, `~icons/heroicons/bell`) without requiring `/outline/` or `/solid/` path segments. Solid variants use a `-solid` suffix in the icon name itself (e.g., `~icons/heroicons/arrow-path-solid`).
Applied to files:
src/constants/organizationTabs.ts
📚 Learning: 2025-12-27T03:51:23.575Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-27T03:51:23.575Z
Learning: Applies to src/**/*.{vue,css,scss} : The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-05T17:34:25.556Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T17:34:25.556Z
Learning: Applies to src/pages/**/*.vue : Use file-based routing with unplugin-vue-router for frontend pages
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to src/pages/**/*.vue : Frontend file-based routing uses `src/pages/` directory structure; routes auto-generate with `unplugin-vue-router` and types are available in `src/typed-router.d.ts`
Applied to files:
src/pages/sso-login.vue
🧬 Code graph analysis (1)
src/composables/useSSODetection.ts (1)
src/services/supabase.ts (1)
useSupabase(58-71)
🪛 GitHub Actions: Run tests
src/pages/sso-login.vue
[error] 11-11: TS2345: Argument of type '"/sso-login"' is not assignable to parameter of type 'keyof RouteNamedMap | undefined'.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (java-kotlin)
🔇 Additional comments (7)
src/constants/organizationTabs.ts (1)
9-14: LGTM!The SSO tab addition follows the correct heroicon import pattern and is appropriately positioned in the navigation hierarchy. The route key aligns with the new SSO configuration page.
src/pages/login.vue (1)
33-34: LGTM!The SSO redirect handling is well-structured:
- State management with
isFromSSOcorrectly tracks SSO flow- Loading UI provides clear feedback during authentication
- Error recovery resets state appropriately
- The SSO login link integrates cleanly into the existing UI
Also applies to: 269-274, 283-283, 345-357, 360-360, 408-417
src/pages/sso-login.vue (1)
21-61: SSO flow logic looks solid.The
continueWithSSOfunction properly:
- Validates email input before proceeding
- Checks SSO availability for the domain
- Handles the redirect with query parameter preservation
- Provides clear error feedback via toasts
- Manages loading state appropriately
src/pages/settings/organization/sso.vue (2)
149-238: Comprehensive SSO configuration implementation.The SSO wizard implementation is thorough and well-structured:
- ✓ Proper permission checks before allowing configuration
- ✓ Multi-step wizard with clear progress indicators
- ✓ Backend error handling with user-friendly messages
- ✓ SSRF protection mentioned for metadata URLs
- ✓ Domain uniqueness validation
- ✓ Separate toggles for SSO and Auto-Join with dependency logic
- ✓ Test functionality before enabling
- ✓ Danger zone for deletion with clear warnings
The implementation follows security best practices and provides excellent UX for organization admins.
Also applies to: 243-334, 339-467, 483-611, 616-706, 711-766
126-128: Include 'owner' role in SSO configuration permission check.The
super_admincheck is incomplete. The organization store'sdeleteOrganizationmethod (line 351 in organization.ts) checks bothsuper_adminandownerroles for sensitive operations. The SSO permission check should follow the same pattern by including['super_admin', 'owner']to ensure organization owners can also configure SSO, consistent with other privileged organization settings.⛔ Skipped due to learnings
Learnt from: riderx Repo: Cap-go/capgo PR: 1297 File: src/components/dashboard/DeploymentBanner.vue:77-79 Timestamp: 2025-12-23T01:19:15.067Z Learning: In the Cap-go codebase, the 'owner' role exists only at the organization level (used in src/stores/organization.ts), not as an app-level permission. App-level permissions use the user_min_right database enum which includes: read, upload, write, admin, super_admin (but NOT owner). When checking app permissions, never include 'owner' in the role checks.Learnt from: WcaleNieWolny Repo: Cap-go/capgo PR: 1300 File: supabase/migrations/20251224103713_2fa_enforcement.sql:409-539 Timestamp: 2025-12-24T14:11:10.256Z Learning: In supabase/migrations for get_orgs_v6 and get_orgs_v7: The inner functions with user_id parameter (get_orgs_v6(uuid) and get_orgs_v7(uuid)) should NOT be granted to anon/authenticated roles as this allows any user to query other users' organizations; only the no-argument wrapper functions should be public as they perform authentication checks.Learnt from: WcaleNieWolny Repo: Cap-go/capgo PR: 1300 File: supabase/migrations/20251224103713_2fa_enforcement.sql:85-96 Timestamp: 2025-12-25T11:22:19.594Z Learning: In the 2FA enforcement implementation for supabase/migrations: When an org has enforcing_2fa=true, all users including super_admins must have 2FA enabled before accessing any org functions (including check_org_members_2fa_enabled); this is intentional behavior to ensure consistent security enforcement without exceptions for admins.src/composables/useSSODetection.ts (1)
59-60: The URL manipulation is defensively designed and reliable.The
.replace('/rest/v1', '')pattern is safe: if the path component exists, it gets stripped; if absent, the replace is a no-op. The fallback|| ''handles the empty case. This is necessary because Supabase REST API URLs include/rest/v1, but Edge Functions endpoints are at/functions/v1, not/rest/v1/functions/v1. However, note the inconsistency: other files (sso.vue, ChangePassword.vue) construct function endpoints without stripping, suggestingVITE_SUPABASE_URLmay already exclude/rest/v1in some environments. Consider adding a clarifying comment about this defensive pattern or standardizing URL handling across the codebase.messages/en.json (1)
22-1495: LGTM: Comprehensive SSO and webhook localization added.The localization keys for SSO configuration (wizard steps, IdP metadata, auto-join, testing), webhooks (CRUD operations, deliveries, signatures), audit logs, credits pricing, and password policies are well-organized and cover all necessary user-facing scenarios. The key naming follows consistent conventions and the messages are clear.
| async function initiateSSO(redirectTo?: string, email?: string): Promise<void> { | ||
| if (!ssoProviderId.value) | ||
| return | ||
|
|
||
| try { | ||
| // Check if Supabase URL is local (meaning truly local testing) | ||
| const supabaseUrl = import.meta.env.VITE_SUPABASE_URL || '' | ||
| const isLocalSupabase = supabaseUrl.includes('localhost') || supabaseUrl.includes('127.0.0.1') | ||
|
|
||
| if (isLocalSupabase && email) { | ||
| // Use mock SSO endpoint ONLY when Supabase is local | ||
| const relayState = redirectTo || '/dashboard' | ||
| const mockSSOUrl = `${supabaseUrl}/functions/v1/mock-sso-callback?email=${encodeURIComponent(email)}&RelayState=${encodeURIComponent(relayState)}` | ||
|
|
||
| window.location.href = mockSSOUrl | ||
| return | ||
| } | ||
|
|
||
| // Production/Development/Preprod: Use real Supabase SAML SSO | ||
| const options: any = { | ||
| provider: 'saml', | ||
| options: { | ||
| providerId: ssoProviderId.value, | ||
| }, | ||
| } | ||
|
|
||
| // Add redirect URL if provided | ||
| if (redirectTo) { | ||
| options.options.redirectTo = `${window.location.origin}${redirectTo}` | ||
| } | ||
|
|
||
| const { data, error } = await supabase.auth.signInWithSSO(options) | ||
|
|
||
| if (error) | ||
| throw error | ||
|
|
||
| // Redirect to SSO provider (Okta) | ||
| if (data?.url) { | ||
| window.location.href = data.url | ||
| } | ||
| } | ||
| catch (error) { | ||
| console.error('Exception initiating SSO:', error) | ||
| throw error | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for mock-sso-callback endpoint implementation
echo "=== Searching for mock-sso-callback endpoint implementation ==="
rg -n 'mock-sso-callback' -C 10
echo ""
echo "=== Searching for mock SSO related functions ==="
rg -n 'mock.*sso|sso.*mock' -i --type ts --type js -C 3Repository: Cap-go/capgo
Length of output: 3255
🏁 Script executed:
#!/bin/bash
# Search for Supabase functions directory
echo "=== Looking for Supabase functions directory ==="
find . -type d -name 'functions' 2>/dev/null | head -20
echo ""
echo "=== Listing files in supabase/functions if exists ==="
if [ -d "supabase/functions" ]; then
find supabase/functions -type f -name "*.ts" -o -name "*.js" | head -30
fi
echo ""
echo "=== Searching for files containing 'sso' or 'callback' in functions ==="
find . -path "*/functions/*" -type f \( -name "*.ts" -o -name "*.js" \) 2>/dev/null | xargs grep -l 'sso\|callback' -i 2>/dev/nullRepository: Cap-go/capgo
Length of output: 2054
🏁 Script executed:
#!/bin/bash
# List all top-level directories in supabase/functions
echo "=== Supabase functions structure ==="
ls -la supabase/functions/ | grep -E '^d'
echo ""
echo "=== All files directly in supabase/functions (excluding _backend) ==="
find supabase/functions -maxdepth 1 -type f
echo ""
echo "=== Looking for auth or sso related directories/files ==="
find supabase/functions -type d -name "*auth*" -o -type d -name "*sso*" 2>/dev/null
echo ""
echo "=== Search for all files with 'auth' in supabase/functions ==="
find supabase/functions -type f \( -name "*.ts" -o -name "*.js" \) | xargs grep -l 'auth\|sso' -i 2>/dev/null | head -20
echo ""
echo "=== Check for mock-related functions ==="
find supabase/functions -type f \( -name "*.ts" -o -name "*.js" \) | xargs grep -l 'mock' 2>/dev/nullRepository: Cap-go/capgo
Length of output: 2669
🏁 Script executed:
#!/bin/bash
# Search entire repo for mock-sso
echo "=== Searching entire repo for 'mock-sso' ==="
rg -i 'mock-sso' -C 2
echo ""
echo "=== Searching for test files related to SSO ==="
find . -type f \( -name "*.test.ts" -o -name "*.spec.ts" -o -name "*.test.js" -o -name "*.spec.js" \) | xargs grep -l 'sso' -i 2>/dev/null
echo ""
echo "=== Check if useSSODetection.ts has tests ==="
find . -type f \( -name "*useSSODetection*" \)
echo ""
echo "=== Search for Vitest or Jest mocks ==="
rg -i 'vi.mock|jest.mock' -C 3 | head -50Repository: Cap-go/capgo
Length of output: 929
🏁 Script executed:
#!/bin/bash
# Look for README or documentation about SSO/local development
echo "=== Searching for SSO or local dev documentation ==="
find . -type f \( -name "README*" -o -name "*.md" \) -exec grep -l 'mock.*sso\|sso.*mock\|local.*dev' -i {} \; 2>/dev/null | head -10
echo ""
echo "=== Check supabase.json or local dev config ==="
find . -type f -name "supabase.json" -o -name ".env*" | head -10
echo ""
echo "=== Search for inline comments about mock-sso endpoint ==="
rg -i 'mock.*sso|sso.*mock' -B 5 -A 5
echo ""
echo "=== Check if there are pending/TODO comments about this ==="
rg -i 'TODO|FIXME|XXX' src/composables/useSSODetection.tsRepository: Cap-go/capgo
Length of output: 2614
🏁 Script executed:
#!/bin/bash
# Check README and CLAUDE.md for SSO information
echo "=== Checking README.md for SSO/mock mentions ==="
grep -i 'sso\|mock.*endpoint' README.md
echo ""
echo "=== Checking CLAUDE.md ==="
grep -i 'sso\|mock' CLAUDE.md
echo ""
echo "=== Full context from useSSODetection file ==="
wc -l src/composables/useSSODetection.ts
head -20 src/composables/useSSODetection.tsRepository: Cap-go/capgo
Length of output: 676
🏁 Script executed:
#!/bin/bash
# Read the complete useSSODetection.ts file
cat -n src/composables/useSSODetection.tsRepository: Cap-go/capgo
Length of output: 5710
Implement the missing mock-sso-callback endpoint for local development SSO testing.
The code at lines 110-117 references a Supabase Edge Function endpoint (/functions/v1/mock-sso-callback) that does not exist in the repository. This will cause the local development SSO authentication flow to fail. The endpoint needs to be created in supabase/functions/ and should:
- Accept
emailandRelayStatequery parameters - Validate and sanitize the email parameter to prevent injection attacks
- Return a valid authentication response that integrates with Supabase Auth
…undefined fix(migrations): remove redundant revoke on get_customer_counts for service_role
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* fix: make is_allowed_capgkey support hashed API keys Update is_allowed_capgkey and get_user_id functions to support both plain-text and hashed API keys using find_apikey_by_value(). Add expiration checks to prevent expired keys from passing validation. Add comprehensive tests for hashed key validation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <[email protected]> * refactor: use find_apikey_by_value RPC in checkKey Refactor checkKey function to use the find_apikey_by_value SQL function instead of duplicating the hashing logic in JavaScript. This ensures consistent key lookup behavior between SQL functions and TypeScript code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <[email protected]> * refactor: remove isSafeAlphanumeric check from checkKey Remove the isSafeAlphanumeric validation as it's no longer needed for security. The RPC call to find_apikey_by_value uses parameterized queries, which prevents SQL injection regardless of input characters. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <[email protected]> * refactor: remove isSafeAlphanumeric function Remove the isSafeAlphanumeric validation function as it's no longer needed. Both Supabase RPC calls and Drizzle ORM use parameterized queries which prevent SQL injection regardless of input characters. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <[email protected]> * refactor: use find_apikey_by_value in checkKeyPg Refactor checkKeyPg to use the find_apikey_by_value SQL function instead of manually hashing and querying. This ensures consistent key lookup behavior between all code paths. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <[email protected]> * perf: optimize find_apikey_by_value to use single query Replace sequential two-query approach with a single query using OR. This reduces database round-trips and allows PostgreSQL to potentially use index union optimization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <[email protected]> * refactor: merge find_apikey_by_value optimization into main migration Consolidate the find_apikey_by_value query optimization (single query with OR instead of two sequential queries) into the original migration file for cleaner PR history. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: add index signature to FindApikeyByValueResult type Drizzle's execute method requires the generic type to satisfy Record<string, unknown>, so added intersection with index signature. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Haiku 4.5 <[email protected]>
…es across components and stores
- Introduced new keys for allowing and helping with bundle previews in Japanese, Korean, Polish, Brazilian Portuguese, Russian, Turkish, Vietnamese, and Simplified Chinese. - Added error messages for changing preview settings across all supported languages. - Included success messages for changing preview settings in all relevant languages. - Updated device names and other related strings to enhance user experience in the respective languages.
…oints - Updated password compliance validation to use authenticated Supabase client. - Refactored app deletion logic to utilize API key-based access for data queries. - Modified build request handling to enforce RLS with API key authentication. - Enhanced organization member management to ensure proper access control. - Streamlined webhook operations to leverage API key authentication for data access. - Improved statistics retrieval to support authenticated access based on user roles. - Adjusted tests to ensure proper error handling and access control scenarios.
Add comprehensive test coverage for CLI operations using hashed API keys. Tests verify that all core SDK operations (upload, list, channel management) work correctly with hashed API keys stored as SHA-256 hashes in the database. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Haiku 4.5 <[email protected]>
* security: remove passwords from all logs Ensure passwords are never logged to Cloudflare, Supabase, or Discord by: - Removing password field from cloudlog calls in accept_invitation and validate_password_compliance - Sanitizing Discord alerts to completely remove password field and partially redact other sensitive fields 🤖 Generated with Claude Code Co-Authored-By: Claude Haiku 4.5 <[email protected]> * fix: move password redaction after validation to handle null body Address PR feedback - if a client sends JSON null, destructuring before validation throws TypeError (500) instead of returning 400. Move cloudlog calls after safeParse validation to ensure body is valid before destructuring. 🤖 Generated with Claude Code Co-Authored-By: Claude Haiku 4.5 <[email protected]> * chore: remove deno.lock from commit 🤖 Generated with Claude Code Co-Authored-By: Claude Haiku 4.5 <[email protected]> --------- Co-authored-by: Claude Haiku 4.5 <[email protected]>
- Add Webhook type alias to webhooks store and Vue components - Fix apikey.key null check in build/start.ts
The generateEncryptionKeysSDK() function was modifying the project's capacitor.config.ts without cleanup. The SDK asynchronously writes the public key to the config during key generation, which was leaving the test environment dirty. Now we back up and restore the config in a finally block to ensure clean state regardless of test outcome. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Haiku 4.5 <[email protected]>
This adds a dynamic link to the Supabase dashboard for each Stripe customer, allowing easy navigation from Stripe to the customer's org record in Supabase. The link is parameterized with the SUPABASE_URL environment variable and supports both production and local Supabase instances. 🤖 Generated with Claude Code Co-authored-by: Claude Haiku 4.5 <[email protected]>
- Refactored webhook GET, POST, and PUT functions to utilize the capgkey from the context instead of the API key for authenticated client access. - Added new RLS policies to support anon role for webhooks and webhook deliveries, allowing API key-based authentication. - Updated seed data to include dedicated users and API keys for testing, ensuring isolation between tests. - Enhanced tests for CLI hashed API keys and RLS to prevent interference with other tests, using dedicated test data.
Add SSO configuration wizard, login detection, and UI: - SSO settings page with multi-step wizard - SSO login page with email detection - Login page SSO redirect handling and link - SSO tab in organization settings - 65 i18n messages for SSO UI This is PR #3 of SSO split (frontend only).
5d90f96 to
bc3bd8d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
supabase/functions/_backend/private/download_link.ts (2)
40-55: Potential null reference onowner_orgaccess.On line 47,
bundle?.owner_org.created_bywill throw a TypeError ifbundleexists butowner_orgis null (e.g., if the foreign key relationship fails). The null check on line 53 comes too late.🐛 Suggested fix
const { data: bundle, error: getBundleError } = await supabase .from('app_versions') .select('*, owner_org ( created_by )') .eq('app_id', body.app_id) .eq('id', body.id) .single() - const ownerOrg = bundle?.owner_org.created_by + const ownerOrg = bundle?.owner_org?.created_by if (getBundleError) { return simpleError('cannot_get_bundle', 'Cannot get bundle', { getBundleError }) }
57-68: Fix manifest query to use correct column reference.The manifest query on lines 61-62 uses
.eq('app_id', body.app_id)and.eq('id', body.id), but the manifest table does not have these columns. According to the schema, the manifest table hasapp_version_idas the foreign key to app_versions. Replace the query with.eq('app_version_id', body.id).Suggested change
if (body.isManifest) { const { data: manifest, error: getManifestError } = await supabase .from('manifest') .select('*') - .eq('app_id', body.app_id) - .eq('id', body.id) + .eq('app_version_id', body.id) if (getManifestError) { return simpleError('cannot_get_manifest', 'Cannot get manifest', { getManifestError }) }supabase/functions/_backend/public/organization/members/delete.ts (1)
24-29: Inconsistent with the new AGENTS.md guidance you're adding in this PR.This endpoint uses the admin SDK for user lookup but an authenticated client for the delete operation. The AGENTS.md guidance (added in this same PR) states: "Never use the Supabase admin SDK (with service key) for user-facing APIs."
Since this is a user-facing organization management endpoint, both operations should use the authenticated client. The proper approach is to ensure the RLS policy on the
userstable allows organization admins to query users within their organization scope, then usesupabaseApikeyfor both the lookup and delete operations.♻️ Suggested approach
- // Use admin client to lookup user by email - RLS on users table prevents cross-user lookups - const { data: userData, error: userError } = await supabaseAdmin(c) + // Use authenticated client for lookup - RLS should allow org admins to query users in their org + const supabase = supabaseApikey(c, c.get('capgkey') as string) + const { data: userData, error: userError } = await supabase .from('users') .select('id') .eq('email', body.email) .single() if (userError || !userData) { throw quickError(404, 'user_not_found', 'User not found', { error: userError }) } - // Use authenticated client for the delete operation - RLS will enforce org access - const supabase = supabaseApikey(c, c.get('capgkey') as string) + // Continue using authenticated client for the delete operation cloudlog({ requestId: c.get('requestId'), message: 'userData.id', data: userData.id }) cloudlog({ requestId: c.get('requestId'), message: 'body.orgId', data: body.orgId }) const { error } = await supabaseThen verify that the RLS policy on
userstable permits this query for organization admins.Based on learnings and coding guidelines about using authenticated clients for user-facing endpoints.
supabase/functions/_backend/public/webhooks/post.ts (1)
46-56: Add webhooks table definition to Drizzle ORM schema to restore type safetyThe
(supabase as any)cast is necessary because the webhooks table definition is missing frompostgres_schema.ts. Per the project guidelines, all schema definitions must be placed inutils/postgres_schema.tsusing Drizzle ORM. Add the webhooks table definition there (matching the structure in the migration: id, org_id, name, url, secret, enabled, events, created_at, updated_at, created_by), then runbun typesto regenerate TypeScript types and remove the type assertion.
🤖 Fix all issues with AI agents
In @.opencode/worktree-session-state.json:
- Around line 1-28: Remove the committed local session file
.opencode/worktree-session-state.json from the repository and add the .opencode/
directory to .gitignore so session state is no longer tracked; ensure you delete
the file from the repo index/history (stop tracking it) and commit the change,
and if a .gitignore does not exist at the repo root create one containing
".opencode/" to prevent future commits of files under that directory.
In @package.json:
- Line 4: Revert the manual change to the "version" field in package.json (reset
"version" back to its previous value) and remove any other manual version bumps
in the PR so that release automation/CI can handle version updates during tagged
releases; ensure no code or scripts rely on a hardcoded version string in this
branch and leave version management to the release pipeline.
In @src/pages/sso-login.vue:
- Line 11: The call useRoute('/sso-login') is causing a TypeScript type error;
change it to call useRoute() with no arguments (e.g., replace
useRoute('/sso-login') with useRoute()) and keep using the returned route object
(const route = useRoute()) so the TypeScript types align with the
composition-router API.
In @supabase/functions/_backend/private/invite_new_user_to_org.ts:
- Around line 51-54: The captcha result from verifyCaptchaToken is being
ignored; update the invite_new_user_to_org flow to handle its return value (or
change verifyCaptchaToken to throw) so failed verification stops execution:
capture the result of verifyCaptchaToken(c, body.captcha_token), check for the
returned simpleError/failure case and immediately return that error response (or
rethrow) before creating the Supabase client (supabaseClient) and proceeding;
ensure the handler uses the same simpleError shape that verifyCaptchaToken
returns so RLS/client creation is only reached on successful captcha.
- Around line 68-76: After confirming the org exists, call hasOrgRight(c,
body.org_id, authData.user.id, 'admin') to ensure the requester has admin
permission before proceeding; if that call returns false, return an appropriate
"forbidden" response (e.g., status 403 and message indicating insufficient
permissions) and do not create/update the invitation record; place this check
immediately after the org existence check and before any logic that upserts the
invitation.
In @supabase/functions/_backend/public/build/start.ts:
- Line 49: The non-null assertion on apikey.key (assigned to apikeyKey) can
throw for hashed API keys; change the assignment to pass apikey.key (allowing
null) rather than apikey.key! and update any downstream usages and function
signatures (e.g., the start handler and any calls into supabaseApikey) to accept
string | null so supabaseApikey can perform the fallback (effectiveApikey =
apikey ?? c.get('capgkey')) as intended; remove the forced non-null assertions
and ensure callers propagate nullable types to supabaseApikey.
In @supabase/functions/_backend/public/webhooks/deliveries.ts:
- Around line 40-41: The code forcibly casts c.get('capgkey') to string when
calling supabaseApikey, which can mask undefined and cause runtime issues;
change the call to pass the raw value (e.g., c.get('capgkey')) or explicitly
pass null instead of using "as string" so supabaseApikey can handle
undefined/null itself. Update the invocation of supabaseApikey to remove the
type assertion and allow its internal fallback (reference supabaseApikey and
c.get('capgkey')).
In @supabase/functions/_backend/triggers/logsnag_insights.ts:
- Line 477: Remove the nonexistent plan_enterprise field from the insert/upsert
payload to global_stats: delete the "plan_enterprise: plans.Enterprise || 0"
entry (or replace it with the correct fields plan_solo, plan_maker, plan_team,
plan_enterprise_monthly, plan_enterprise_yearly as intended) so the upsert
against the global_stats table no longer writes a column that isn’t in the
schema; update the payload in the same function where plan_enterprise is set.
In @supabase/functions/_backend/utils/stripe.ts:
- Around line 435-441: The metadata update call after customer creation
(buildSupabaseDashboardLink, metadata,
getStripe(c).customers.update(customer.id, { metadata })) lacks error handling;
wrap that await in a try/catch, log the failure with context (include
customer.id, metadata and the caught error) using the existing logger (or
console.error if no logger available) and rethrow or return an explicit failure
so the error is not silently swallowed and callers know the metadata update
failed.
- Around line 25-40: The hardcoded Supabase table ID 445780 in
buildSupabaseDashboardLink will break across projects; change the URL to
reference the table by stable name or make the table identifier configurable
(env var) instead of embedding 445780. Update buildSupabaseDashboardLink to
construct the Studio/dashboard URL using the orgs table name (e.g., "orgs"
view/path) or read a SUPABASE_ORGS_TABLE_ID environment variable (using getEnv)
and fall back to the table-name-based URL for portability, ensuring both the
local (isLocalSupabase) and production branches use the new approach.
In @tests/cli-hashed-apikey.test.ts:
- Around line 25-35: The CLI-hashed tests currently exercise only the plain-key
lookup because createHashedKeySDK() uses the CLI plain key mapped to
CLI_HASHED_USER_ID, so find_apikey_by_value() matches on WHERE key = apikey and
never tests the key_hash branch; fix by either updating supabase/seed.sql to add
a hashed-only API key row for CLI_HASHED_USER_ID (key = NULL and key_hash =
encode(digest('a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c03','sha256'),'hex')) or change
the test wiring/constants to point createHashedKeySDK() at the existing user
that has a hashed-only key (user id 6aa76066-55ef-4238-ade6-0b32334a4097) so the
tests exercise the key_hash lookup in find_apikey_by_value().
🧹 Nitpick comments (29)
supabase/migrations/20260107000000_add_anon_role_to_webhooks_rls.sql (1)
124-136: MissingWITH CHECKclause on UPDATE policy.The
webhooksUPDATE policy (lines 49-70) has bothUSINGandWITH CHECKclauses, but this one only hasUSING. While PostgreSQL defaults to using theUSINGexpression whenWITH CHECKis omitted, adding an explicitWITH CHECKwould:
- Maintain consistency with the
webhookstable pattern- Make the write constraint explicit
♻️ Suggested fix for consistency
CREATE POLICY "Allow admin to update webhook_deliveries" ON public.webhook_deliveries FOR UPDATE TO authenticated, anon USING ( public.check_min_rights( 'admin'::public.user_min_right, (SELECT public.get_identity('{read,upload,write,all}'::public.key_mode[])), org_id, null::CHARACTER VARYING, null::BIGINT ) -) +) +WITH CHECK ( + public.check_min_rights( + 'admin'::public.user_min_right, + (SELECT public.get_identity('{read,upload,write,all}'::public.key_mode[])), + org_id, + null::CHARACTER VARYING, + null::BIGINT + ) +);supabase/functions/_backend/private/credits.ts (1)
352-354: Move session ID validation earlier in the handler.This validation protects against injection in the
sourceMatchFiltersquery, but it runs after the Stripe API call at line 312. For consistency and fail-fast behavior, validate the format immediately after parsing the request body (around line 307).♻️ Suggested change
Move the validation to the beginning of the handler:
app.post('/complete-top-up', middlewareAuth, async (c) => { const body = await parseBody<CompleteTopUpRequest>(c) if (!body.orgId || !body.sessionId) throw simpleError('missing_parameters', 'orgId and sessionId are required') + // Validate sessionId format to prevent injection (Stripe session IDs: cs_test_* or cs_live_*) + if (!/^cs_(?:test|live)_[a-zA-Z0-9]+$/.test(body.sessionId)) + throw simpleError('invalid_session_id', 'Invalid session ID format') + const { customerId, token } = await resolveOrgStripeContext(c, body.orgId)And remove lines 352-354 from their current location.
supabase/functions/_backend/private/set_org_email.ts (1)
32-66: Consider migrating togetPgClient()orgetDrizzleClient()pattern.Per coding guidelines, database operations should use
getPgClient()orgetDrizzleClient()fromutils/pg.tsduring the active migration to Cloudflare D1. While Supabase client usage may still be acceptable during transition, consider whether this endpoint should be migrated to the new pattern for consistency.Based on coding guidelines for
supabase/functions/_backend/**/*.{ts,js}.supabase/functions/_backend/private/invite_new_user_to_org.ts (5)
47-48: Inconsistent error response structure.This error return uses a plain object
{ message, status }while other errors usesimpleError(). This inconsistency may cause issues in the caller at lines 103-104, which checks!res.inviteCreatorUserrather than checking for error properties.♻️ Suggested fix for consistency
- if (!authorization) - return { message: 'not authorized', status: 401 } + if (!authorization) + return simpleError('not_authorized', 'Not authorized', {}, 'Missing authorization header')Note: This will require updating the caller logic at lines 103-108 to handle
simpleErrorreturn type consistently.
113-114: Non-null assertion onauthorizationmay mask logic errors.The non-null assertion
res.authorization!assumes authorization is always present whenres.inviteCreatorUserexists, but this coupling is implicit and fragile. Consider restructuring the return type or adding an explicit check.♻️ Safer alternative
- const supabase = supabaseClient(c, res.authorization!) + if (!res.authorization) { + return quickError(401, 'not_authorized', 'Missing authorization') + } + const supabase = supabaseClient(c, res.authorization)
64-66: HTTP 500 is inappropriate for "User already exists" error.A 500 status code indicates a server error, but a user already existing is a client-side validation issue. Consider using 409 (Conflict) or 400 (Bad Request).
♻️ Suggested fix
if (existingUser || !userError) { - return { message: 'Failed to invite user', error: 'User already exists', status: 500 } + return { message: 'Failed to invite user', error: 'User already exists', status: 409 } }
98-111: Error handling doesn't propagate HTTP status fromvalidateInvite.When
validateInvitereturns an error with a specificstatus(e.g., 401, 409, 500), the caller always returns a generic error viasimpleErrororquickErrorwith hardcoded status codes, ignoring the actual status from the validation result.♻️ Suggested improvement
Consider checking
res.statusand using it in the error response, or restructuring to throw typed errors that carry status codes.
177-206:verifyCaptchaTokenreturns a value but is typed as returningPromise<void>.The function returns
simpleError(...)on failure but the implicit return type suggests it returns nothing. This contributes to the issue where the caller doesn't handle the return value. Consider either:
- Throwing an error on failure, or
- Explicitly typing the return and documenting that callers must check it.
♻️ Option 1: Throw on failure (cleaner pattern)
async function verifyCaptchaToken(c: Context, token: string) { const captchaSecret = getEnv(c, 'CAPTCHA_SECRET_KEY') if (!captchaSecret) { - return simpleError('captcha_secret_key_not_set', 'CAPTCHA_SECRET_KEY not set') + throw new Error('CAPTCHA_SECRET_KEY not set') } // ... fetch logic ... if (!captchaResultData.success) { - return simpleError('invalid_captcha', 'Invalid captcha result') + throw new Error('Invalid captcha result') } cloudlog({ requestId: c.get('requestId'), context: 'captcha_result', captchaResultData }) if (captchaResultData.data.success !== true) { - return simpleError('invalid_captcha', 'Invalid captcha result') + throw new Error('Invalid captcha') } }Then wrap the call in try-catch in
validateInvite.supabase/functions/_backend/utils/stripe.ts (1)
16-23: Prefer URL constructor over string splitting.The current string-splitting approach is fragile and may fail with unexpected URL formats. Using the built-in
URLconstructor provides more robust parsing.♻️ Proposed refactor
function getSupabaseProjectId(c: Context): string | null { const supabaseUrl = getEnv(c, 'SUPABASE_URL') if (!supabaseUrl) return null - return supabaseUrl.split('//')[1]?.split('.')[0]?.split(':')[0] || null + try { + const url = new URL(supabaseUrl) + // Extract subdomain from hostname (e.g., "xvwzpoazmxkqosrdewyv" from "xvwzpoazmxkqosrdewyv.supabase.co") + return url.hostname.split('.')[0] || null + } + catch { + return null + } }src/services/posthog.ts (1)
1-1: Consider adding typed global declarations instead of@ts-nocheck.While
@ts-nocheckworks for the dynamic PostHog loader pattern, it disables all TypeScript checking in this file. Consider adding a minimal type declaration for the globalposthogobject to retain type safety for the exported helper functions.💡 Suggested type declaration
// Instead of @ts-nocheck, add at the top of the file: declare global { interface Window { posthog: { init: (token: string, config: object) => void capture: (event: string) => void identify: (id: string, properties: object) => void setPersonProperties: (properties: object) => void reset: () => void _i: any[] __SV?: number } } } declare const posthog: Window['posthog']supabase/functions/_backend/public/webhooks/deliveries.ts (1)
165-165: Double type cast for JSON payload is acceptable but could be improved.The
as any as WebhookPayloaddouble cast is necessary because the database returns JSON as an unknown type. Consider validating the payload structure if payload integrity is critical.tests/private-error-cases.test.ts (1)
411-451: invite_new_user_to_org tests accurately cover captcha-first failure pathThese two tests clearly document and enforce the behavior that CAPTCHA validation runs before any org lookup, both for existing (
testOrgId) and nonexistent (randomUUID()) orgs, and they assert the newcaptcha_secret_key_not_seterror. If the test environment ever needs a globalCAPTCHA_SECRET_KEYfor other suites, consider explicitly overriding/unsetting it in thisdescribeblock to keep these expectations stable, but that’s optional.supabase/functions/_backend/files/preview.ts (1)
101-112: JWT/RLS-based preview flow is solid; consider hardening Authorization parsingThe updated handler correctly:
- Accepts a token via
Authorizationheader,tokenquery param, or cookie and persists it in an httpOnly,SameSite: 'Strict'cookie for subsequent asset requests.- Uses
supabaseClient(c, \Bearer ${token}`)so allapps,app_versions, andmanifest` reads go through RLS tied to that JWT.- Enforces
allow_preview, rejects encrypted bundles, and requiresmanifest_count > 0, which aligns with the new preview-* error keys.One small robustness improvement you could make (non-blocking) is to parse the
Authorizationheader case‑insensitively and trim it, so unusual casing or extra whitespace from upstream proxies doesn’t break token extraction.Optional header parsing tweak
- const authorization = c.req.header('authorization') - const tokenFromQuery = c.req.query('token') - const tokenFromCookie = getCookie(c, TOKEN_COOKIE_NAME) - const token = authorization?.split('Bearer ')[1] || tokenFromQuery || tokenFromCookie + const authHeader = c.req.header('authorization') || '' + const bearerMatch = authHeader.match(/^Bearer\s+(.+)$/i) + const tokenFromHeader = bearerMatch?.[1] + const tokenFromQuery = c.req.query('token') + const tokenFromCookie = getCookie(c, TOKEN_COOKIE_NAME) + const token = tokenFromHeader || tokenFromQuery || tokenFromCookieAlso applies to: 114-120, 128-138, 150-185
supabase/functions/_backend/public/app/delete.ts (1)
5-6: Good split between apikey and admin clients; consider tightening error handling andorg_usersfilterThe refactor improves security by:
- Checking
hasAppRightApikeyup front, and- Using
supabaseApikeyfor user-facing tables (app_versions_meta,channel_devices,channels,devices, etc.) while reservingsupabaseAdminfor internal stats tables and the finalapp_versions/appsdeletes.A couple of things are worth revisiting:
Silent failures from intermediate deletes
All the deletes inside
Promise.all([...])ignore their{ error }results. If RLS or constraints prevent some rows from being deleted (especially on thesupabaseclient), you could end up with partial cleanup without any signal. Even a lightweight pattern like logging non‑nullerrorfor each operation would make debugging much easier.
org_usersfiltered byapp_idThe
org_usersdeletion uses.eq('app_id', appId). In most schemasorg_usersis keyed byorg_id/user_idand doesn’t carry anapp_idcolumn; if that’s true here, this delete is effectively a no‑op. If you genuinely need to clean up per‑app rows inorg_users, double‑check the schema and adjust the filter (or table) accordingly; otherwise you might want to drop this delete entirely.These aren’t blockers for the current change, but tightening them up would make
deleteAppmore robust and predictable.Also applies to: 15-17, 19-24, 32-55, 57-145, 147-160
messages/zh-cn.json (1)
180-181: Preview-related zh-CN strings look consistent and safeThe new preview, device, and fullscreen translations are coherent, match the expected English semantics, and introduce no interpolation or JSON issues. Optional: consider keeping brand names like “Pixel” untranslated if you want stricter consistency with the device label in other locales, but current wording is acceptable.
Also applies to: 316-316, 371-371, 678-681, 738-738, 766-766, 1131-1139, 1199-1199
supabase/functions/_backend/public/statistics/index.ts (2)
79-118: Minor cleanup opportunity ingetNormalStatsaggregationFunctionally the aggregation logic looks unchanged and compatible with the new client type, but there’s a small readability/perf nit:
- You sort
arrItemtwice: once intosortedArrItem(which you log) and then immediately callarrItem?.sort(...).forEach(...)again. SinceArray.prototype.sortis in-place, a single sort is enough; you can reusesortedArrItemin the forEach, or skip the first assignment entirely.This doesn’t break behaviour, but simplifying it will make the code clearer and slightly cheaper to run.
Also applies to: 128-163
330-388: GuardgetLatestVersionagainst empty input for future callers
getLatestVersionassumesversionsis non-empty and seedsreducewithversions[0]. IfversionNamesever happens to be empty (e.g. ifdaily_versionreturns no rows for a given range) andshouldGetLatestVersionis set totruein future, this will throw at runtime.Right now
/app/:app_id/bundle_usagealways passesshouldGetLatestVersion = false, so the path is unreachable, but it’s cheap to make this safe for future use:Suggested defensive guard
-function getLatestVersion(versions: VersionName[]) { - return versions.reduce((latest, current) => - new Date(current.created_at ?? '') > new Date(latest.created_at ?? '') ? current : latest, versions[0]) -} +function getLatestVersion(versions: VersionName[]) { + if (versions.length === 0) + return undefined + + return versions.reduce((latest, current) => + new Date(current.created_at ?? '') > new Date(latest.created_at ?? '') ? current : latest, versions[0]) +} + +function getLatestVersionPercentage(datasets: any[], latestVersion?: { name?: string }) { + if (!latestVersion?.name) + return 0 + const latestVersionDataset = datasets.find(dataset => dataset.label === latestVersion.name) + return latestVersionDataset ? latestVersionDataset.data[latestVersionDataset.data.length - 1] : 0 +}Then adapt the call site to handle
undefinedsafely.Also applies to: 529-539
src/pages/app/[package].channel.[channel].devices.vue (1)
127-135: Consider validating owner_org before sending to backend.The empty string fallback (
channel.value?.owner_org ?? '') could mask bugs whereowner_orgis unexpectedly missing. Consider adding explicit validation:if (!channel.value?.owner_org) { toast.error(t('cannot-create-empty-device')) return } const { error: addDeviceError } = await supabase.functions.invoke('private/create_device', { body: { device_id: deviceId, app_id: route.params.package as string, org_id: channel.value.owner_org, platform, version_name: channel.value?.version.name ?? 'unknown', }, })This provides clearer feedback to users and prevents sending invalid requests to the backend.
To confirm the backend properly handles this case, verify that the create_device function validates org_id and returns a meaningful error for empty or invalid values.
supabase/functions/_backend/public/webhooks/post.ts (1)
45-45: Useapikey.keyfor consistency with other endpointsLine 45 passes
c.get('capgkey') as stringtosupabaseApikey, but other endpoints in this PR useapikey.key(e.g., build/request.ts line 91, organization/members/get.ts line 43). SincesupabaseApikeyalready falls back toc.get('capgkey')when the key parameter is null/undefined, prefer the consistent pattern.♻️ Suggested fix
- const supabase = supabaseApikey(c, c.get('capgkey') as string) + const supabase = supabaseApikey(c, apikey.key)Based on learnings and as per coding guidelines.
supabase/functions/_backend/public/webhooks/get.ts (1)
36-37: Minor inconsistency in API key parameter passing.This file uses
c.get('capgkey') as stringwhilecancel.tsin this PR usesapikey.key. Both work becausesupabaseApikeyfalls back toc.get('capgkey')when the first parameter is null/undefined, but using a consistent pattern across all webhook endpoints would improve maintainability.The current approach is functionally correct since
supabaseApikeyhandles the fallback internally.supabase/functions/_backend/utils/hono_middleware.ts (1)
63-84: Consider pushing mode and expiration filtering to the SQL function for consistency.The
checkKeyByIdPgfunction (lines 117-127) filters by mode and expiration in SQL usingnotExpiredCondition, butcheckKeyPgperforms these checks in JavaScript after fetching the key. This creates an inconsistency:
- Clock skew risk: The JS expiration check (
new Date(apiKey.expires_at) < new Date()) may differ from the DB'snow()if clocks are not synchronized.- Unnecessary data transfer: Keys with wrong modes or expired keys are still fetched from the DB.
If the
find_apikey_by_valueSQL function cannot be modified to accept mode/expiration filters, this approach is acceptable as a fallback, but documenting the rationale would help future maintainers.supabase/functions/_backend/public/webhooks/test.ts (1)
78-81: Consider error handling for the delivery update.The
attempt_countupdate doesn't check for errors. While this is a non-critical update, silent failures could make debugging difficult.♻️ Suggested improvement
- await (supabase as any) + const { error: updateError } = await (supabase as any) .from('webhook_deliveries') .update({ attempt_count: 1 }) .eq('id', delivery.id) + + if (updateError) { + console.warn('Failed to update attempt_count:', updateError) + }SSO_PR_SPLIT_PLAN.md (1)
22-25: Add language specifiers to fenced code blocks for better rendering.Per markdownlint, fenced code blocks should have language specifiers. This applies to lines 22, 88, 164, 236, and 285.
♻️ Example fix
-``` +```text supabase/migrations/20260107_sso_saml_complete.sql</details> </blockquote></details> <details> <summary>src/composables/useSSODetection.ts (3)</summary><blockquote> `49-53`: **Consider externalizing or extending the public domain list.** The hardcoded list of public email domains is limited. Common domains like `protonmail.com`, `aol.com`, `live.com`, `mail.com` are not included. Consider: 1. Extracting to a constants file for easier maintenance 2. Adding more common public email providers --- `58-66`: **URL construction could be fragile.** The pattern `replace('/rest/v1', '')` assumes the Supabase URL always contains `/rest/v1`. This may not hold for all configurations. Consider using `URL` API for safer manipulation. <details> <summary>♻️ Safer URL construction</summary> ```diff - const apiUrl = import.meta.env.VITE_SUPABASE_URL?.replace('/rest/v1', '') || '' + // Extract base URL from Supabase URL (remove /rest/v1 if present) + let apiUrl = import.meta.env.VITE_SUPABASE_URL || '' + try { + const url = new URL(apiUrl) + url.pathname = '' + apiUrl = url.origin + } + catch { + apiUrl = apiUrl.replace('/rest/v1', '') + }
120-125: Avoidanytype for SSO options.Using
anyloses type safety. The Supabase SDK provides types for SSO options.♻️ Type-safe options
- const options: any = { - provider: 'saml', - options: { - providerId: ssoProviderId.value, - }, - } + const options = { + provider: 'saml' as const, + options: { + providerId: ssoProviderId.value, + redirectTo: redirectTo ? `${window.location.origin}${redirectTo}` : undefined, + }, + }Then remove the separate redirectTo assignment block.
src/pages/sso-login.vue (1)
52-56: Consider more specific error handling.The catch block logs the error and shows a generic message. Since
initiateSSOcan throw specific errors, consider providing more helpful user feedback based on the error type.supabase/functions/_backend/utils/webhook.ts (1)
70-89: Return type inference is acceptable but consider explicit typing for API stability.The removal of explicit return types relies on Supabase's type inference. While this works, explicit return types on exported functions provide better API documentation and catch breaking changes earlier.
♻️ Optional: Add explicit return types for documentation
export async function findWebhooksForEvent( c: Context, orgId: string, tableName: string, -) { +): Promise<Database['public']['Tables']['webhooks']['Row'][]> {src/stores/webhooks.ts (1)
406-419: Local state update in retryDelivery may cause stale data.When retrying a delivery, the code optimistically updates local state (lines 408-412) but the actual retry happens asynchronously via an edge function. If the retry fails or the status changes differently, the local state won't reflect reality until the next fetch.
Consider either:
- Re-fetching deliveries after a successful retry request
- Adding a comment documenting this is an optimistic update
| { | ||
| "sessions": { | ||
| "ses_46bc1d6ffffeljgiVpepptQJpo": { | ||
| "sessionId": "ses_46bc1d6ffffeljgiVpepptQJpo", | ||
| "createdAt": 1767718660391 | ||
| }, | ||
| "ses_46ba7fa47ffeBntHbdn9H2Q1zZ": { | ||
| "sessionId": "ses_46ba7fa47ffeBntHbdn9H2Q1zZ", | ||
| "createdAt": 1767720355306 | ||
| }, | ||
| "ses_46ba7dd2cffeJ97QlIv6YZPNU5": { | ||
| "sessionId": "ses_46ba7dd2cffeJ97QlIv6YZPNU5", | ||
| "createdAt": 1767720362750 | ||
| }, | ||
| "ses_46ba7d141ffeHeloQHLposBOMp": { | ||
| "sessionId": "ses_46ba7d141ffeHeloQHLposBOMp", | ||
| "createdAt": 1767720365780 | ||
| }, | ||
| "ses_46ba317c2ffezf9crrwEIUl8QN": { | ||
| "sessionId": "ses_46ba317c2ffezf9crrwEIUl8QN", | ||
| "createdAt": 1767720675420 | ||
| }, | ||
| "ses_46ba2f84effe4l1eoBWyYUSSox": { | ||
| "sessionId": "ses_46ba2f84effe4l1eoBWyYUSSox", | ||
| "createdAt": 1767720683481 | ||
| } | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this file and add .opencode/ to .gitignore.
This file contains local development tool session state that should not be committed to version control. It will cause merge conflicts, pollute the repository history, and potentially expose sensitive session identifiers.
🔧 Recommended fix
- Remove this file from the commit:
git rm .opencode/worktree-session-state.json- Add the
.opencodedirectory to.gitignoreto prevent future commits:
+# OpenCode IDE
+.opencode/- If
.gitignoredoesn't exist at the repository root, create it with the above content.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In @.opencode/worktree-session-state.json around lines 1 - 28, Remove the
committed local session file .opencode/worktree-session-state.json from the
repository and add the .opencode/ directory to .gitignore so session state is no
longer tracked; ensure you delete the file from the repo index/history (stop
tracking it) and commit the change, and if a .gitignore does not exist at the
repo root create one containing ".opencode/" to prevent future commits of files
under that directory.
package.json
Outdated
| "name": "capgo-app", | ||
| "type": "module", | ||
| "version": "12.89.4", | ||
| "version": "12.89.14", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Version bump should be handled by release automation.
The version field was incremented from 12.89.4 to 12.89.14 while the PR is still open. Based on learnings, version updates should be left to CI/CD during tagged releases.
🤖 Prompt for AI Agents
In @package.json at line 4, Revert the manual change to the "version" field in
package.json (reset "version" back to its previous value) and remove any other
manual version bumps in the PR so that release automation/CI can handle version
updates during tagged releases; ensure no code or scripts rely on a hardcoded
version string in this branch and leave version management to the release
pipeline.
| const { error: appError } = await supabase.from('apps') | ||
| .select('owner_org') | ||
| .eq('app_id', safeBody.app_id) | ||
| .single() | ||
|
|
||
| if (appError) { | ||
| return quickError(404, 'app_not_found', 'App not found', { app_id: safeBody.app_id }) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
App existence check could be more precise.
Two concerns with this check:
- Error handling: Any database error (not just "not found") returns a 404, which could mask other issues.
- Missing validation: The check doesn't verify that the app belongs to the
org_idprovided in the request. A user could potentially create a device for an app they have access to but associate it with a different org_id.
🐛 Suggested improvement
- const { error: appError } = await supabase.from('apps')
+ const { data: app, error: appError } = await supabase.from('apps')
.select('owner_org')
.eq('app_id', safeBody.app_id)
.single()
- if (appError) {
+ if (appError || !app) {
return quickError(404, 'app_not_found', 'App not found', { app_id: safeBody.app_id })
}
+
+ if (app.owner_org !== safeBody.org_id) {
+ return quickError(403, 'org_mismatch', 'App does not belong to the specified organization', { app_id: safeBody.app_id, org_id: safeBody.org_id })
+ }| // Add supabase dashboard link with the real customer ID after creation | ||
| const supabaseLink = buildSupabaseDashboardLink(c, customer.id) | ||
| if (supabaseLink) { | ||
| metadata.supabase = supabaseLink | ||
| await getStripe(c).customers.update(customer.id, { metadata }) | ||
| } | ||
| return customer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for metadata update after customer creation.
The Supabase dashboard link update occurs after customer creation without error handling. If this update fails, the error is swallowed silently, and the returned customer object won't reflect the updated metadata.
🛡️ Suggested fix with error handling
const customer = await getStripe(c).customers.create({
email,
name,
metadata,
})
// Add supabase dashboard link with the real customer ID after creation
const supabaseLink = buildSupabaseDashboardLink(c, customer.id)
if (supabaseLink) {
metadata.supabase = supabaseLink
- await getStripe(c).customers.update(customer.id, { metadata })
+ try {
+ const updatedCustomer = await getStripe(c).customers.update(customer.id, { metadata })
+ return updatedCustomer
+ }
+ catch (error) {
+ cloudlogErr({ requestId: c.get('requestId'), message: 'Failed to update customer with Supabase link', customerId: customer.id, error })
+ // Return original customer if metadata update fails
+ }
}
return customer📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Add supabase dashboard link with the real customer ID after creation | |
| const supabaseLink = buildSupabaseDashboardLink(c, customer.id) | |
| if (supabaseLink) { | |
| metadata.supabase = supabaseLink | |
| await getStripe(c).customers.update(customer.id, { metadata }) | |
| } | |
| return customer | |
| // Add supabase dashboard link with the real customer ID after creation | |
| const supabaseLink = buildSupabaseDashboardLink(c, customer.id) | |
| if (supabaseLink) { | |
| metadata.supabase = supabaseLink | |
| try { | |
| const updatedCustomer = await getStripe(c).customers.update(customer.id, { metadata }) | |
| return updatedCustomer | |
| } | |
| catch (error) { | |
| cloudlogErr({ requestId: c.get('requestId'), message: 'Failed to update customer with Supabase link', customerId: customer.id, error }) | |
| // Return original customer if metadata update fails | |
| } | |
| } | |
| return customer |
🤖 Prompt for AI Agents
In @supabase/functions/_backend/utils/stripe.ts around lines 435 - 441, The
metadata update call after customer creation (buildSupabaseDashboardLink,
metadata, getStripe(c).customers.update(customer.id, { metadata })) lacks error
handling; wrap that await in a try/catch, log the failure with context (include
customer.id, metadata and the caught error) using the existing logger (or
console.error if no logger available) and rethrow or return an explicit failure
so the error is not silently swallowed and callers know the metadata update
failed.
- Fix 'witch channek' -> 'which channel' in choose-which-channel-to-link-this-bundle-to - Fix 'budnle' -> 'bundle' in remove-bundle-from-this-channel - Remove hardcoded '/sso-login' argument from useRoute() call
- Remove .opencode/ session state from tracking and add to .gitignore - Create mock-sso-callback endpoint for local SSO testing with email validation - Remove debug console.log statements from sso-login.vue - Fix NULL comparisons in SQL migration (use NULLIF pattern instead of direct comparisons) - Remove type assertion on capgkey in webhooks/deliveries.ts - Remove nonexistent plan_enterprise field from logsnag_insights.ts - Replace hardcoded Supabase table ID 445780 with table name in stripe.ts - Add error handling for Stripe customer metadata updates - Improve app ownership validation in create_device.ts - Fix regex pattern in discord.ts to handle JSON-escaped quotes - Update seed.sql to use hashed-only API key for CLI tests
- Add redactKey() helper function to show only first 8 characters - Redact capgkeyString in 'Capgkey provided' and 'Invalid apikey' logs - Redact JWT in 'JWT provided' and 'Setting apikey' logs - Log only subkey.id instead of full subkey object to avoid exposing key/key_hash - Prevents CodeQL high severity vulnerability: Clear-text logging of sensitive information Fixes clear-text logging of sensitive data returned by access to apikey/apiKey
- Restore Database type export from commit b719d45 - Copy types to both src/types/ and supabase/functions/_backend/utils/ - Fixes TypeScript error in hono_middleware.ts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
supabase/functions/_backend/private/validate_password_compliance.ts (1)
70-79: Critical: Admin client bypasses RLS on public endpoint.This endpoint has no authentication middleware, making it publicly accessible. Using the admin client to query org password policies bypasses RLS, allowing anyone to retrieve any organization's password policy configuration by providing an arbitrary
org_id.Based on learnings, the admin SDK should only be used for internal operations, not user-facing APIs. While password policies may not be highly sensitive, this violates the principle of least privilege and could expose organizational metadata.
Recommendations:
- Add authentication middleware to this endpoint, OR
- If the endpoint must be public (e.g., for pre-auth policy display), ensure RLS policies on the
orgstable restrictpassword_policy_configvisibility appropriately, OR- Create a dedicated public RPC function with controlled access to policy data
🤖 Fix all issues with AI agents
In @messages/en.json:
- Line 715: The messages/en.json is missing seven i18n keys used by
sso-login.vue; add the keys "sso-login-title", "sso-login-subtitle",
"work-email", "sso-not-configured", "sso-login-failed", "sso-info", and
"back-to-login" to messages/en.json (and mirror them into other locale files)
with the appropriate English strings used in the SSO login UI so the component's
t() calls resolve to localized text rather than falling back to hardcoded
strings.
In @src/pages/sso-login.vue:
- Around line 145-147: The template uses double quotes in the i18n call
t("support"); update the template expression to use single quotes (t('support'))
to conform to the project's linting/quote style—locate the button template
containing @click="openSupport" and replace t("support") with t('support') only.
- Around line 21-50: In continueWithSSO, prevent open redirects by constraining
route.query.to to a safe internal path (must be a string that startsWith('/')
and not include protocol/host); fall back to '/dashboard' otherwise, and use
that sanitized value when calling initiateSSO. Also ensure isLoading.value is
always cleared after initiateSSO returns or fails — wrap the async work in a
try/finally (or set isLoading.value = false immediately after await initiateSSO
if it returns) so the UI never remains stuck loading; reference continueWithSSO,
route.query.to, initiateSSO, and isLoading.value when making these changes.
In @supabase/functions/_backend/utils/discord.ts:
- Around line 21-47: The regex construction in sanitizeSensitiveFromString uses
raw field names from REMOVED_FIELDS and PARTIALLY_REDACTED_FIELDS and must
escape regex metacharacters to avoid ReDoS; add a small helper like escapeRegex
that replaces characters like . * + ? ^ $ { } ( ) | [ ] \ / with escaped
versions, then use escapeRegex(field) when building the two RegExp instances
(the jsonRegexWithComma and jsonRegex) so patterns are safe even if field names
change or become configurable; keep the existing logic and calls to
partialRedact and the replacement flows unchanged.
In @supabase/functions/mock-sso-callback/index.ts:
- Around line 4-13: The mock SSO callback endpoint in
supabase/functions/mock-sso-callback/index.ts must be hard-blocked outside
local/dev; add an environment guard (e.g., check NODE_ENV !==
'development'/'local' or a dedicated flag like MOCK_SSO_ENABLED) at the start of
the exported handler for this function and return an early 403/404 response with
a brief log when the guard fails. Reuse the same environment threading approach
your backend uses (middleware or env helper) to read the env flag, and ensure
the guard runs before any email validation or response generation so the
simulated SSO cannot be invoked in production.
- Around line 21-47: The current handler in app.get('/', async (c: Context) => {
... }) builds redirectUrl from c.req.url which causes the endpoint to redirect
back to itself (infinite loop) and allows an open-redirect via RelayState; fix
by validating and normalizing relayState before use (ensure it is either a
relative path starting with '/' and not containing '//' or a full URL only if it
matches a strict allowlist) and then construct redirectUrl to point to the
validated relayState (not c.req.url) with success and email query params; update
the redirectUrl construction (the symbol to change is redirectUrl) to use
encodeURIComponent for params and return c.redirect to that safe, normalized
path to eliminate the loop and close the open-redirect risk.
- Around line 15-17: Import the version value and pass it plus the Sentry DSN as
the second and third arguments to createHono (replace createHono(functionName,
import.meta.url) with createHono(functionName, version,
Deno.env.get('SENTRY_DSN_SUPABASE'))), and update the route handler signature to
use the generic Context<MiddlewareKeyVariables> (change the app.get('/', async
(c: Context) => ...) to app.get('/', async (c: Context<MiddlewareKeyVariables>)
=> ...)) so middleware-provided keys like requestId and auth are correctly
typed.
In @supabase/seed.sql:
- Around line 487-492: The INSERT row for the CLI hashed key tuple starting with
(110, NOW(), 'e5f6a7b8-...') is missing the key_hash column in its column list,
causing value misalignment; update the INSERT to include key_hash (and set
key=NULL) so the encode(extensions.digest(...), 'hex') expression maps to
key_hash and the subsequent 'all' and NOW() map to mode and updated_at
respectively, matching the pattern used for other hashed-key rows.
🧹 Nitpick comments (3)
supabase/functions/_backend/utils/discord.ts (1)
109-110: Remove type assertion for better type safety.The
as anycast bypasses TypeScript's type checking. Thec.req.raw.headersshould be a standardHeadersobject from the Web API, which has an.entries()method.♻️ Proposed fix: Use proper Headers typing
- const rawHeaders = Object.fromEntries((c.req.raw.headers as any).entries()) + const rawHeaders = Object.fromEntries(c.req.raw.headers.entries()) const headers = sanitizeSensitiveHeaders(rawHeaders)If TypeScript still complains, the Headers interface should already include
entries(). If not, consider using:- const rawHeaders = Object.fromEntries((c.req.raw.headers as any).entries()) + const rawHeaders = Object.fromEntries(Array.from(c.req.raw.headers.entries())) const headers = sanitizeSensitiveHeaders(rawHeaders)supabase/functions/_backend/utils/hono_middleware.ts (1)
74-95: Consider moving expiration check to the SQL function for consistency and efficiency.The expiration check at lines 92-95 is performed in JavaScript, while
checkKeyByIdPgperforms the same check in SQL usingnotExpiredCondition(line 135). This inconsistency means:
- Less efficient: The database returns expired keys that are immediately rejected
- Inconsistent pattern: Similar operations use different approaches
- Extra network overhead: Transferring expired keys from DB to application
Consider updating the
find_apikey_by_valueSQL function to include the expiration condition directly:-- In the SQL function WHERE (expires_at IS NULL OR expires_at > now())This would align with
checkKeyByIdPgand eliminate the need for the JavaScript check at lines 92-95.Based on coding guidelines requiring consistent database operation patterns with Drizzle ORM.
src/pages/sso-login.vue (1)
73-148: UI primitives: consider aligning buttons with DaisyUI (d-components/classes)The primary CTA button is custom Tailwind; if the rest of the app standardizes on DaisyUI for interactive elements, consider swapping to keep spacing/hover/focus consistent (per coding guidelines).
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
.gitignoremessages/en.jsonsrc/pages/sso-login.vuesupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/private/validate_password_compliance.tssupabase/functions/_backend/public/webhooks/deliveries.tssupabase/functions/_backend/utils/discord.tssupabase/functions/_backend/utils/hono_middleware.tssupabase/functions/_backend/utils/stripe.tssupabase/functions/mock-sso-callback/index.tssupabase/migrations/20260107210800_sso_saml_complete.sqlsupabase/seed.sql
✅ Files skipped from review due to trivial changes (1)
- .gitignore
🚧 Files skipped from review as they are similar to previous changes (3)
- supabase/functions/_backend/public/webhooks/deliveries.ts
- supabase/functions/_backend/utils/stripe.ts
- supabase/migrations/20260107210800_sso_saml_complete.sql
🧰 Additional context used
📓 Path-based instructions (16)
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use single quotes and no semicolons per @antfu/eslint-config
Files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/utils/discord.tssrc/pages/sso-login.vuesupabase/functions/_backend/private/validate_password_compliance.ts
supabase/functions/_backend/**
📄 CodeRabbit inference engine (CLAUDE.md)
Backend logic should be organized in
supabase/functions/_backend/with subdirectories for plugins, private endpoints, public endpoints, triggers, and utilities
Files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/utils/discord.tssupabase/functions/_backend/private/validate_password_compliance.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript strict mode with path aliases mapping
~/tosrc/
Files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/utils/discord.tssupabase/functions/_backend/private/validate_password_compliance.ts
supabase/functions/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Supabase Edge Functions use Deno runtime
Files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/utils/discord.tssupabase/functions/_backend/private/validate_password_compliance.ts
supabase/functions/_backend/**/*.{ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
supabase/functions/_backend/**/*.{ts,js}: Backend code must be placed insupabase/functions/_backend/as shared code deployed to Cloudflare Workers (API/Plugin/Files workers), Supabase Edge Functions, and other platforms
UsecreateHonofromutils/hono.tsfor all Hono framework application initialization and routing
All database operations must usegetPgClient()orgetDrizzleClient()fromutils/pg.tsfor PostgreSQL access during active migration to Cloudflare D1
All Hono endpoint handlers must acceptContext<MiddlewareKeyVariables>and usec.get('requestId'),c.get('apikey'), andc.get('auth')for request context
Use structured logging withcloudlog({ requestId: c.get('requestId'), message: '...' })for all backend logging
UsemiddlewareAPISecretfor internal API endpoints andmiddlewareKeyfor external API keys; validate againstowner_orgin theapikeystable
Checkc.get('auth')?.authTypeto determine authentication type ('apikey' vs 'jwt') in backend endpoints
Use Drizzle ORM query patterns withschemafrompostgress_schema.tsfor all database operations; usealiasV2()for self-joins or multiple table references
Files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/utils/discord.tssupabase/functions/_backend/private/validate_password_compliance.ts
supabase/functions/**/*.{ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Backend ESLint must pass before commit; run
bun lint:backendfor backend files
Files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/utils/discord.tssupabase/functions/_backend/private/validate_password_compliance.ts
src/**/*.vue
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.vue: Use Vue 3 with Composition API and<script setup>syntax for frontend components
Style components using TailwindCSS with DaisyUI components
src/**/*.vue: Use Vue 3<script setup>syntax exclusively for all Vue component scripts
Use Tailwind utility classes for layout and spacing in Vue components
Use DaisyUI components (d-btn,d-input,d-card) for interactive elements in Vue components
Use Konsta components ONLY for safe area helpers (top/bottom insets) in Vue components; avoid other uses
UseuseRoute()fromvue-routerto access route parameters anduseRouter()for programmatic navigation in Vue components
Files:
src/pages/sso-login.vue
src/pages/**/*.vue
📄 CodeRabbit inference engine (CLAUDE.md)
Use file-based routing with unplugin-vue-router for frontend pages
Frontend file-based routing uses
src/pages/directory structure; routes auto-generate withunplugin-vue-routerand types are available insrc/typed-router.d.ts
Files:
src/pages/sso-login.vue
{capacitor.config.{ts,js},src/**/*.{ts,tsx,vue}}
📄 CodeRabbit inference engine (CLAUDE.md)
Mobile apps should use Capacitor with app ID
ee.forgr.capacitor_gofor native mobile functionality
Files:
src/pages/sso-login.vue
src/**/*.{ts,tsx,vue,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use
~/alias for imports fromsrc/directory in frontend TypeScript and Vue components
Files:
src/pages/sso-login.vue
src/**/*.{vue,ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Frontend ESLint must pass before commit; run
bun lint:fixto auto-fix issues in frontend files
Files:
src/pages/sso-login.vue
**/{src,app}/{components,views,pages}/**/*.{vue,css,scss}
📄 CodeRabbit inference engine (AGENTS.md)
The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS.
Files:
src/pages/sso-login.vue
**/{src,app}/{components,views,pages}/**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
Use DaisyUI (
d-prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent.
Files:
src/pages/sso-login.vue
**/{src,app}/**/*.{vue,ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Konsta components are reserved for the safe area helpers. Avoid importing
konstaanywhere else in the app.
Files:
src/pages/sso-login.vue
**/{src,app}/**/*.{css,scss,vue}
📄 CodeRabbit inference engine (AGENTS.md)
Capgo's design uses deep slate bases with the 'Extract' azure highlight (
--color-azure-500: #119eff) and soft radii; mirror the palette fromsrc/styles/style.css(e.g.,--color-primary-500: #515271) when introducing new UI.
Files:
src/pages/sso-login.vue
supabase/seed.sql
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Seed database with
supabase db resetto apply all migrations and test data fromsupabase/seed.sqlUpdating
supabase/seed.sqlto back new or evolved tests is expected; keep fixtures focused on current behavior while leaving committed migrations unchanged.
Files:
supabase/seed.sql
🧠 Learnings (20)
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Use `middlewareAPISecret` for internal API endpoints and `middlewareKey` for external API keys; validate against `owner_org` in the `apikeys` table
Applied to files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/utils/discord.tssupabase/seed.sql
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Check `c.get('auth')?.authType` to determine authentication type ('apikey' vs 'jwt') in backend endpoints
Applied to files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/private/validate_password_compliance.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : All Hono endpoint handlers must accept `Context<MiddlewareKeyVariables>` and use `c.get('requestId')`, `c.get('apikey')`, and `c.get('auth')` for request context
Applied to files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/create_device.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Use `createHono` from `utils/hono.ts` for all Hono framework application initialization and routing
Applied to files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/private/validate_password_compliance.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Use structured logging with `cloudlog({ requestId: c.get('requestId'), message: '...' })` for all backend logging
Applied to files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/_backend/utils/discord.ts
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to **/{api,server,src/server,backend}/**/*.{ts,tsx,js} : Never use the Supabase admin SDK (with service key) for user-facing APIs. Always use the client SDK with user authentication so RLS policies are enforced. The admin SDK should only be used for internal operations (triggers, CRON jobs, etc.), and when unavoidable, sanitize all user inputs carefully to prevent PostgREST query injection.
Applied to files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/_backend/private/create_device.tssupabase/functions/_backend/private/validate_password_compliance.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Use Drizzle ORM query patterns with `schema` from `postgress_schema.ts` for all database operations; use `aliasV2()` for self-joins or multiple table references
Applied to files:
supabase/functions/_backend/utils/hono_middleware.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : All database operations must use `getPgClient()` or `getDrizzleClient()` from `utils/pg.ts` for PostgreSQL access during active migration to Cloudflare D1
Applied to files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/_backend/private/validate_password_compliance.ts
📚 Learning: 2025-12-05T17:34:25.556Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T17:34:25.556Z
Learning: Applies to supabase/functions/**/*.ts : Supabase Edge Functions use Deno runtime
Applied to files:
supabase/functions/_backend/utils/hono_middleware.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/validate_password_compliance.ts
📚 Learning: 2025-12-24T14:11:10.256Z
Learnt from: WcaleNieWolny
Repo: Cap-go/capgo PR: 1300
File: supabase/migrations/20251224103713_2fa_enforcement.sql:409-539
Timestamp: 2025-12-24T14:11:10.256Z
Learning: In supabase/migrations for get_orgs_v6 and get_orgs_v7: The inner functions with user_id parameter (get_orgs_v6(uuid) and get_orgs_v7(uuid)) should NOT be granted to anon/authenticated roles as this allows any user to query other users' organizations; only the no-argument wrapper functions should be public as they perform authentication checks.
Applied to files:
supabase/functions/_backend/private/create_device.tssupabase/seed.sql
📚 Learning: 2025-12-23T01:19:15.067Z
Learnt from: riderx
Repo: Cap-go/capgo PR: 1297
File: src/components/dashboard/DeploymentBanner.vue:77-79
Timestamp: 2025-12-23T01:19:15.067Z
Learning: In the Cap-go codebase, the 'owner' role exists only at the organization level (used in src/stores/organization.ts), not as an app-level permission. App-level permissions use the user_min_right database enum which includes: read, upload, write, admin, super_admin (but NOT owner). When checking app permissions, never include 'owner' in the role checks.
Applied to files:
supabase/functions/_backend/private/create_device.ts
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to **/{src,app}/{components,views,pages}/**/*.{vue,css,scss} : The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS.
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to src/**/*.vue : Use `useRoute()` from `vue-router` to access route parameters and `useRouter()` for programmatic navigation in Vue components
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-05T17:34:25.556Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T17:34:25.556Z
Learning: Applies to src/pages/**/*.vue : Use file-based routing with unplugin-vue-router for frontend pages
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to src/pages/**/*.vue : Frontend file-based routing uses `src/pages/` directory structure; routes auto-generate with `unplugin-vue-router` and types are available in `src/typed-router.d.ts`
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-23T01:19:04.593Z
Learnt from: riderx
Repo: Cap-go/capgo PR: 1297
File: src/components/dashboard/DeploymentBanner.vue:77-79
Timestamp: 2025-12-23T01:19:04.593Z
Learning: In the Cap-go codebase, ensure that app permission checks never include the role 'owner'. App-level permissions should be based on the user_min_right enum with values: read, upload, write, admin, super_admin (and NOT owner). This pattern applies across Vue components that perform permission checks; if you find a check referencing 'owner' for app-level access, replace it with the appropriate user_min_right value and keep organization-level owner handling in organization.ts.
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to supabase/seed.sql : Updating `supabase/seed.sql` to back new or evolved tests is expected; keep fixtures focused on current behavior while leaving committed migrations unchanged.
Applied to files:
supabase/seed.sql
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to supabase/migrations/**/*.sql : A migration that introduces a new table may include seed inserts for that table, but treat that seeding as part of the current feature and do not modify previously committed migrations.
Applied to files:
supabase/seed.sql
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/seed.sql : Seed database with `supabase db reset` to apply all migrations and test data from `supabase/seed.sql`
Applied to files:
supabase/seed.sql
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Test accounts available after `supabase db reset`: `testcapgo.app` / `testtest` (demo user) and `admincapgo.app` / `adminadmin` (admin user)
Applied to files:
supabase/seed.sql
🧬 Code graph analysis (4)
supabase/functions/mock-sso-callback/index.ts (1)
supabase/functions/_backend/utils/hono.ts (1)
createHono(137-196)
supabase/functions/_backend/private/create_device.ts (2)
supabase/functions/_backend/utils/supabase.ts (1)
supabaseWithAuth(50-61)supabase/functions/_backend/utils/hono.ts (1)
quickError(224-237)
supabase/functions/_backend/utils/discord.ts (1)
scripts/snippet/cloudflare-snippet-filter-appid.js (1)
body(65-65)
supabase/functions/_backend/private/validate_password_compliance.ts (3)
supabase/functions/_backend/utils/logging.ts (1)
cloudlog(3-15)supabase/functions/_backend/utils/hono.ts (1)
quickError(224-237)supabase/functions/_backend/utils/supabase.ts (1)
supabaseClient(38-48)
🪛 ast-grep (0.40.4)
supabase/functions/_backend/utils/discord.ts
[warning] 28-28: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${field}"\\s*:\\s*"(?:[^"\\\\]|\\\\.)*"\\s*,?\\s*, 'gi')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 39-39: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(("${field}"\\s*:\\s*)"((?:[^"\\\\]|\\\\.)*)", 'gi')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
🔇 Additional comments (25)
supabase/functions/_backend/utils/discord.ts (6)
8-11: LGTM! Well-defined redaction rules.The separation between completely removed fields (
password) and partially redacted fields (tokens, secrets, etc.) is appropriate and follows security best practices.
13-19: LGTM! Safe partial redaction logic.The function correctly handles short values by fully redacting them, and provides useful partial visibility for longer values while maintaining security.
49-66: LGTM! Proper header sanitization logic.The function correctly handles case-insensitive header matching and applies appropriate redaction rules. The use of
continuefor removed fields ensures they're completely excluded from the sanitized output.
115-115: LGTM! Effective defense-in-depth sanitization.The body sanitization prevents sensitive data from being exposed in Discord error alerts. The comment clearly indicates this is a defensive measure.
137-137: LGTM! Sanitized body correctly used in Discord embed.The request body field now uses the sanitized version, preventing exposure of passwords, tokens, and API keys in error alerts.
142-142: LGTM! Headers properly sanitized before logging.The headers are now sanitized before being included in the Discord embed, ensuring sensitive authentication tokens and credentials are redacted.
supabase/seed.sql (5)
2-2: LGTM: Test user additions follow existing patterns.The new test users (stats, rls, cli_hashed) are properly structured with consistent UUIDs and follow the established seeding pattern for isolated test scenarios.
Also applies to: 54-56
233-235: LGTM: Stripe info entries are consistent.The new subscription records for the test organizations follow the same structure and settings as existing test data.
243-245: LGTM: Organization and user relationships are correctly established.The new test organizations and their associated users maintain proper referential integrity across auth.users, public.users, public.orgs, and public.org_users tables.
Also applies to: 254-256, 467-469
494-505: LGTM: Hashed and expired API key test fixtures are well-structured.The separate INSERT statements for hashed and expired API keys correctly include the necessary columns (
key_hash,expires_at) and provide comprehensive test coverage for different authentication scenarios.
510-511: LGTM: Apps and sequence setting are correct.The new test apps for statistics and RLS testing are properly linked to their respective organizations. The sequence is correctly set to 111 to account for the highest used ID (110), with the intentional gap from 15-100 providing logical separation between standard test keys and specialized test fixtures (hashed, expired).
Also applies to: 559-559
supabase/functions/_backend/private/create_device.ts (4)
40-41: Excellent migration to authenticated client.The switch from admin client to
supabaseWithAuthcorrectly enforces RLS policies and follows security best practices for user-facing APIs.
45-51: RPC authorization check correctly updated.The
check_min_rightscall now includesorg_id, maintaining proper authorization while using the authenticated client for RLS enforcement.
61-72: Strong security improvement with ownership validation.The explicit app existence check and org ownership validation provide defense-in-depth against cross-organization access attempts. The authenticated client ensures RLS policies are also enforced.
12-12: Breaking change appears intentional and properly coordinated with callers.Adding
org_idas a required field in the request body is a breaking change, but evidence from tests and the frontend caller (src/pages/app/[package].channel.[channel].devices.vueat lines 128–132) shows that both have been updated to passorg_id. This suggests the breaking change is intentional and coordinated across the codebase.supabase/functions/_backend/private/validate_password_compliance.ts (5)
66-67: Excellent security practice for password logging.Stripping the password from logged request data prevents credential exposure in logs. This is an important security improvement.
95-104: Admin Auth API usage in public endpoint requires scrutiny.While the Auth Admin API's
signInWithPasswordmay be necessary for credential verification in this flow, using admin authentication on a public endpoint is concerning. The added session check on line 101 is a good improvement.Context from the larger security issue:
This endpoint lacks authentication middleware, allowing unrestricted public access. If authentication middleware were added, the standard auth flow could be used instead of the admin client.Recommendation: Consider whether this endpoint truly needs to be public or if it should require pre-authentication.
108-109: Correct pattern for post-authentication operations.After credential verification, the code correctly switches to an authenticated client using the session token. This ensures RLS policies are enforced for all subsequent database operations.
112-121: Security-critical membership verification implemented correctly.The org membership check uses the authenticated client (RLS enforced) and properly validates that the user belongs to the organization before proceeding with password validation.
140-141: Remaining operations correctly use authenticated client.The policy hash RPC call and compliance record upsert both use the authenticated client, ensuring RLS policies are enforced throughout the remaining operations.
Also applies to: 149-149
supabase/functions/_backend/utils/hono_middleware.ts (3)
11-20: LGTM! Good security practice for sensitive data in logs.The redaction helper properly masks keys while preserving enough information (first 8 characters) for debugging correlation. This aligns with the PR's security objectives to prevent clear-text exposure of API keys and JWTs in logs.
167-292: Excellent security improvement with comprehensive redaction.All sensitive values (API keys at lines 167, 170, 232, 292 and JWTs at lines 210, 232) are now properly redacted using the
redactKeyhelper. Non-sensitive identifiers (user IDs, subkey IDs) are correctly left unredacted for debugging purposes.The consistent application of redaction across all log statements effectively prevents clear-text exposure of credentials.
48-61: Fix type accuracy for FindApikeyByValueResult to match the actual apikeys table schema.The type has several inaccuracies:
created_atandupdated_atshould not be nullable (SQL columns areNOT NULLwith defaults)limited_to_orgsshould beuuid[]notstring[](stores UUID arrays, not string arrays)limited_to_appsshould not be nullable (SQL column hasDEFAULT '{}'::character varying[])Update the type definition to reflect the actual schema returned by the SQL function:
type FindApikeyByValueResult = { id: number created_at: string user_id: string key: string | null key_hash: string | null mode: Database['public']['Enums']['key_mode'] updated_at: string name: string limited_to_orgs: string[] limited_to_apps: string[] expires_at: string | null } & Record<string, unknown>⛔ Skipped due to learnings
Learnt from: CR Repo: Cap-go/capgo PR: 0 File: .github/copilot-instructions.md:0-0 Timestamp: 2025-12-23T02:53:12.055Z Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Check `c.get('auth')?.authType` to determine authentication type ('apikey' vs 'jwt') in backend endpointsmessages/en.json (1)
22-27: SSO/2FA strings: consistent tone/capitalization is goodThe new SSO and 2FA-related messages read clear and user-facing (e.g., ACS URL help text).
Also applies to: 53-55
src/pages/sso-login.vue (1)
141-148: No action needed.<LangSelector />is globally auto-registered viaunplugin-vue-components, which generates and maintainssrc/components.d.ts. The component is correctly available without explicit imports in the file.Likely an incorrect or invalid review comment.
messages/en.json
Outdated
| "discover-your-bundle": "See your bundle appear in the list", | ||
| "discover-your-dashbo": "Discover your dashboard !", | ||
| "documentation": "documentation", | ||
| "domain-sso-help": "Add email domains that should be allowed to authenticate via SSO. Users with these email domains will be able to sign in using your identity provider", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, check if the suggested keys exist in messages/en.json
echo "=== Checking for specified keys in messages/en.json ==="
rg -n "\"(sso-login-title|sso-login-subtitle|sso-info|work-email|back-to-login|sso-not-configured|sso-login-failed)\"\\s*:" messages/en.json -S
echo ""
echo "=== Checking for any sso-related keys in messages/en.json ==="
rg -n "\"sso-[^\"]+\"\\s*:" messages/en.json | head -20Repository: Cap-go/capgo
Length of output: 718
🏁 Script executed:
#!/bin/bash
# Find and examine the sso-login.vue file
echo "=== Searching for sso-login.vue ==="
find . -name "sso-login.vue" -type f
echo ""
echo "=== Checking for key references in sso-login.vue ==="
# Once we find it, let's see what it contains
find . -name "sso-login.vue" -type f -exec cat {} \;Repository: Cap-go/capgo
Length of output: 6765
🏁 Script executed:
#!/bin/bash
# Check all keys referenced in sso-login.vue
echo "=== Checking for all keys used in sso-login.vue ==="
rg -n "\"(sso-login-title|sso-login-subtitle|invalid-email|sso-not-configured|sso-login-failed|work-email|email|sso-info|back-to-login|continue|support)\"\\s*:" messages/en.json
echo ""
echo "=== Total count of SSO-related keys ==="
rg "\"sso-" messages/en.json | wc -lRepository: Cap-go/capgo
Length of output: 286
🏁 Script executed:
#!/bin/bash
# Let's also check the lines around 715, 726-729, 1309-1336, 1355-1357 mentioned in the review
echo "=== Content around line 715 ==="
sed -n '710,720p' messages/en.json
echo ""
echo "=== Content around lines 726-729 ==="
sed -n '724,731p' messages/en.json
echo ""
echo "=== Content around lines 1309-1336 ==="
sed -n '1309,1336p' messages/en.json
echo ""
echo "=== Content around lines 1355-1357 ==="
sed -n '1355,1357p' messages/en.jsonRepository: Cap-go/capgo
Length of output: 2941
Add missing i18n keys for SSO login page to messages/en.json
The sso-login.vue component references 7 keys that do not exist in messages/en.json:
sso-login-titlesso-login-subtitlework-emailsso-not-configuredsso-login-failedsso-infoback-to-login
These keys must be added to messages/en.json (and any other required locales) for proper localization. Without them, the component will fall back to the hardcoded text in the t() function calls.
🤖 Prompt for AI Agents
In @messages/en.json at line 715, The messages/en.json is missing seven i18n
keys used by sso-login.vue; add the keys "sso-login-title",
"sso-login-subtitle", "work-email", "sso-not-configured", "sso-login-failed",
"sso-info", and "back-to-login" to messages/en.json (and mirror them into other
locale files) with the appropriate English strings used in the SSO login UI so
the component's t() calls resolve to localized text rather than falling back to
hardcoded strings.
- Export org_saml_connections table schema - Export saml_domain_mappings table schema - Export sso_audit_logs table schema - Fixes TypeScript errors in sso_management.ts and sso_test.ts
Resolved 25 merge conflicts: - 15 translation files: accepted upstream i18n updates from main - package.json: accepted main version (managed by CI) - seed.sql: preserved hashed-only API key test data - preview.ts: accepted main implementation (backgroundTask, better caching) - create_device.ts: kept ownership validation logic - request.ts, deliveries.ts: merged admin client imports - discord.ts: kept enhanced regex for escaped quotes security fix - hono_middleware.ts: preserved redactKey() security function - stripe.ts: kept table name approach + error handling - version.ts: accepted main version number Backend linting passes cleanly. All SSO security fixes preserved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/types/supabase.types.ts (2)
1-34: Consider consolidating duplicate type files.This file is identical to
supabase/functions/_backend/utils/supabase.types.ts. Maintaining two copies of ~3400 lines increases the risk of drift and maintenance burden.Consider either:
- Using a symlink or build step to generate one from the other
- Publishing types to a shared location imported by both frontend and backend
- At minimum, documenting which file is the source of truth
This is a recommended refactor for long-term maintainability but not blocking for this PR.
3193-3195: Same stale type reference as backend file.
DatabaseWithoutInternalsreferences"__InternalSupabase"which no longer exists in the Database type. See comment on the corresponding backend file.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/types/supabase.types.tssupabase/functions/_backend/utils/postgres_schema.tssupabase/functions/_backend/utils/supabase.types.ts
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use single quotes and no semicolons per @antfu/eslint-config
Files:
supabase/functions/_backend/utils/postgres_schema.tssrc/types/supabase.types.tssupabase/functions/_backend/utils/supabase.types.ts
supabase/functions/_backend/**
📄 CodeRabbit inference engine (CLAUDE.md)
Backend logic should be organized in
supabase/functions/_backend/with subdirectories for plugins, private endpoints, public endpoints, triggers, and utilities
Files:
supabase/functions/_backend/utils/postgres_schema.tssupabase/functions/_backend/utils/supabase.types.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript strict mode with path aliases mapping
~/tosrc/
Files:
supabase/functions/_backend/utils/postgres_schema.tssrc/types/supabase.types.tssupabase/functions/_backend/utils/supabase.types.ts
supabase/functions/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Supabase Edge Functions use Deno runtime
Files:
supabase/functions/_backend/utils/postgres_schema.tssupabase/functions/_backend/utils/supabase.types.ts
supabase/functions/_backend/**/*.{ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
supabase/functions/_backend/**/*.{ts,js}: Backend code must be placed insupabase/functions/_backend/as shared code deployed to Cloudflare Workers (API/Plugin/Files workers), Supabase Edge Functions, and other platforms
UsecreateHonofromutils/hono.tsfor all Hono framework application initialization and routing
All database operations must usegetPgClient()orgetDrizzleClient()fromutils/pg.tsfor PostgreSQL access during active migration to Cloudflare D1
All Hono endpoint handlers must acceptContext<MiddlewareKeyVariables>and usec.get('requestId'),c.get('apikey'), andc.get('auth')for request context
Use structured logging withcloudlog({ requestId: c.get('requestId'), message: '...' })for all backend logging
UsemiddlewareAPISecretfor internal API endpoints andmiddlewareKeyfor external API keys; validate againstowner_orgin theapikeystable
Checkc.get('auth')?.authTypeto determine authentication type ('apikey' vs 'jwt') in backend endpoints
Use Drizzle ORM query patterns withschemafrompostgress_schema.tsfor all database operations; usealiasV2()for self-joins or multiple table references
Files:
supabase/functions/_backend/utils/postgres_schema.tssupabase/functions/_backend/utils/supabase.types.ts
supabase/functions/**/*.{ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Backend ESLint must pass before commit; run
bun lint:backendfor backend files
Files:
supabase/functions/_backend/utils/postgres_schema.tssupabase/functions/_backend/utils/supabase.types.ts
{capacitor.config.{ts,js},src/**/*.{ts,tsx,vue}}
📄 CodeRabbit inference engine (CLAUDE.md)
Mobile apps should use Capacitor with app ID
ee.forgr.capacitor_gofor native mobile functionality
Files:
src/types/supabase.types.ts
src/**/*.{ts,tsx,vue,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use
~/alias for imports fromsrc/directory in frontend TypeScript and Vue components
Files:
src/types/supabase.types.ts
src/**/*.{vue,ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Frontend ESLint must pass before commit; run
bun lint:fixto auto-fix issues in frontend files
Files:
src/types/supabase.types.ts
**/{src,app}/**/*.{vue,ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Konsta components are reserved for the safe area helpers. Avoid importing
konstaanywhere else in the app.
Files:
src/types/supabase.types.ts
🧠 Learnings (11)
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/utils/postgress_schema.ts : Schema definitions must be placed in `utils/postgress_schema.ts` using Drizzle ORM and never edited in committed migration files
Applied to files:
supabase/functions/_backend/utils/postgres_schema.tssrc/types/supabase.types.tssupabase/functions/_backend/utils/supabase.types.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Use Drizzle ORM query patterns with `schema` from `postgress_schema.ts` for all database operations; use `aliasV2()` for self-joins or multiple table references
Applied to files:
supabase/functions/_backend/utils/postgres_schema.tssrc/types/supabase.types.tssupabase/functions/_backend/utils/supabase.types.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : All database operations must use `getPgClient()` or `getDrizzleClient()` from `utils/pg.ts` for PostgreSQL access during active migration to Cloudflare D1
Applied to files:
supabase/functions/_backend/utils/postgres_schema.tssrc/types/supabase.types.tssupabase/functions/_backend/utils/supabase.types.ts
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to supabase/migrations/**/*.sql : A migration that introduces a new table may include seed inserts for that table, but treat that seeding as part of the current feature and do not modify previously committed migrations.
Applied to files:
supabase/functions/_backend/utils/postgres_schema.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Use `middlewareAPISecret` for internal API endpoints and `middlewareKey` for external API keys; validate against `owner_org` in the `apikeys` table
Applied to files:
supabase/functions/_backend/utils/postgres_schema.ts
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to supabase/seed.sql : Updating `supabase/seed.sql` to back new or evolved tests is expected; keep fixtures focused on current behavior while leaving committed migrations unchanged.
Applied to files:
supabase/functions/_backend/utils/postgres_schema.tssrc/types/supabase.types.ts
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to **/{tests,__tests__,test,spec}/**/*.{ts,tsx,js} : Always cover database changes with Postgres-level tests and complement them with end-to-end tests for affected user flows.
Applied to files:
supabase/functions/_backend/utils/postgres_schema.tssrc/types/supabase.types.tssupabase/functions/_backend/utils/supabase.types.ts
📚 Learning: 2025-12-24T14:11:10.256Z
Learnt from: WcaleNieWolny
Repo: Cap-go/capgo PR: 1300
File: supabase/migrations/20251224103713_2fa_enforcement.sql:409-539
Timestamp: 2025-12-24T14:11:10.256Z
Learning: In supabase/migrations for get_orgs_v6 and get_orgs_v7: The inner functions with user_id parameter (get_orgs_v6(uuid) and get_orgs_v7(uuid)) should NOT be granted to anon/authenticated roles as this allows any user to query other users' organizations; only the no-argument wrapper functions should be public as they perform authentication checks.
Applied to files:
supabase/functions/_backend/utils/postgres_schema.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Check `c.get('auth')?.authType` to determine authentication type ('apikey' vs 'jwt') in backend endpoints
Applied to files:
supabase/functions/_backend/utils/supabase.types.ts
📚 Learning: 2025-12-05T17:34:25.556Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T17:34:25.556Z
Learning: Applies to supabase/functions/**/*.ts : Supabase Edge Functions use Deno runtime
Applied to files:
supabase/functions/_backend/utils/supabase.types.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Run `bun types` after schema changes to regenerate TypeScript types from Supabase
Applied to files:
supabase/functions/_backend/utils/supabase.types.ts
🧬 Code graph analysis (2)
src/types/supabase.types.ts (1)
supabase/functions/_backend/utils/supabase.types.ts (1)
Json(1-7)
supabase/functions/_backend/utils/supabase.types.ts (1)
src/types/supabase.types.ts (1)
Json(1-7)
🔇 Additional comments (8)
supabase/functions/_backend/utils/postgres_schema.ts (3)
141-159: LGTM - SSO connection table schema is well-defined.The
org_saml_connectionstable schema properly captures the SAML SSO configuration with appropriate field types. Thesso_provider_iduniqueness constraint ensures one-to-one mapping with Supabase's internal SSO provider.One observation: the
org_idandcreated_byfields don't have.references()constraints defined here, unlike other tables in this file (e.g.,app_versionsreferencesapps.name). This may be intentional if the foreign keys are handled in the SQL migration, but verify consistency with the migration file.
161-171: LGTM - Domain mappings table follows expected structure.The
saml_domain_mappingstable correctly captures domain-to-SSO-connection mappings with priority ordering support and verification workflow fields.
173-190: LGTM - SSO audit logs table captures comprehensive event data.The
sso_audit_logstable includes all essential fields for security auditing: user context, event details, network metadata (IP, user agent, country), and SAML-specific session tracking. Themetadatafield as text (presumably for JSON) provides flexibility for additional event-specific data.supabase/functions/_backend/utils/supabase.types.ts (5)
10-34: LGTM - GraphQL public namespace follows Supabase conventions.The
graphql_publicnamespace with thegraphqlfunction signature is the standard Supabase structure for GraphQL API support. The empty Tables/Views/Enums/CompositeTypes with[_ in never]: neverpattern correctly indicates no custom entities in this namespace.
631-673: LGTM - Electron platform support added to channels.The
electron: booleanfield is correctly added to the channels table Row/Insert/Update types, consistent with the existingiosandandroidplatform fields.
3072-3136: LGTM - Platform and stats enums extended for Electron support.The
platform_osenum now includes"electron", andstats_actionincludes the corresponding"disablePlatformElectron"action, maintaining consistency with the existing iOS and Android patterns ("disablePlatformIos","disablePlatformAndroid").
3310-3420: LGTM - Constants object correctly synchronized with type definitions.The
Constantsobject includes the newgraphql_publicnamespace and all electron-related enum values, maintaining consistency with the type definitions above.
3193-3195:and
Security fixes: - Add environment guard to mock-sso-callback (block in production) - Fix open redirect vulnerability in mock-sso-callback RelayState handling - Add regex metacharacter escaping in discord.ts to prevent ReDoS - Prevent open redirect in sso-login.vue continueWithSSO function - Add captcha validation and admin permission check in invite_new_user_to_org.ts - Remove non-null assertion on apikey.key in build/start.ts Code quality improvements: - Fix SQL column alignment in seed.sql CLI hashed key INSERT - Add missing SSO i18n keys to messages/en.json - Fix typos: 'budnle' -> 'bundle', remove duplicate key with typos - Fix quote style in sso-login.vue (double -> single quotes) - Ensure isLoading is always cleared with try/finally pattern - Remove .opencode/worktree-session-state.json from tracking - Update mock-sso-callback to use proper Context type and imports All changes validated with linting and type checking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
supabase/functions/_backend/private/invite_new_user_to_org.ts (1)
113-119: Error status codes are not propagated to the response.The
validateInvitefunction returns differentiated status codes (401, 403, 500), but the caller ignores them and always returns a genericsimpleError. This means a 403 "Insufficient permissions" error becomes a generic 500 "Failed to invite user" to the client.Consider propagating the actual status:
♻️ Suggested improvement
const res = await validateInvite(c, rawBody) if (!res.inviteCreatorUser) { - return simpleError('failed_to_invite_user', 'Failed to invite user', { }, res.error ?? 'Failed to invite user') + return quickError(res.status ?? 500, res.message ?? 'failed_to_invite_user', res.error ?? 'Failed to invite user') }supabase/functions/mock-sso-callback/index.ts (3)
30-33: Consider simplifying the environment check logic.The double fallback pattern for environment variables (
getEnv(c, 'NODE_ENV') || Deno.env.get('NODE_ENV')) is redundant. Consider using just one method consistently, or document why both are needed.♻️ Simplified environment check
- const nodeEnv = getEnv(c, 'NODE_ENV') || Deno.env.get('NODE_ENV') - const mockSSOEnabled = getEnv(c, 'MOCK_SSO_ENABLED') || Deno.env.get('MOCK_SSO_ENABLED') + const nodeEnv = getEnv(c, 'NODE_ENV') + const mockSSOEnabled = getEnv(c, 'MOCK_SSO_ENABLED')
56-59: Injection protection could be more comprehensive.The email injection check only validates against three characters (
<,>,;). While this is a mock endpoint for development, consider adding validation for other potentially dangerous characters like newlines (\n,\r) or null bytes (\0) that could be used in header injection attacks.🔒 Enhanced injection protection
- // Prevent email injection attacks - if (sanitizedEmail.includes('<') || sanitizedEmail.includes('>') || sanitizedEmail.includes(';')) { + // Prevent email injection attacks (newlines, null bytes, angle brackets) + if (sanitizedEmail.includes('<') || sanitizedEmail.includes('>') || sanitizedEmail.includes(';') + || sanitizedEmail.includes('\n') || sanitizedEmail.includes('\r') || sanitizedEmail.includes('\0')) { return c.json({ error: 'Invalid characters in email' }, 400) }
62-85: Add structured logging for successful SSO operations.The endpoint only logs blocked requests (line 34-38) but not successful mock SSO callbacks. Consider adding a success log before the redirect to aid in debugging and monitoring.
Based on learnings, all backend operations should use structured logging with
cloudlog({ requestId: c.get('requestId'), ... }).📝 Add success logging
// Construct safe redirect URL pointing to the validated RelayState const redirectUrl = `${safeRelayState}${safeRelayState.includes('?') ? '&' : '?'}success=true&email=${encodeURIComponent(sanitizedEmail)}` + cloudlog({ + requestId: c.get('requestId'), + message: 'Mock SSO callback successful', + email: sanitizedEmail, + relayState: safeRelayState, + }) + return c.redirect(redirectUrl, 302)src/pages/sso-login.vue (1)
21-25: Redundant email validation.The manual email check at lines 22-25 is redundant since the FormKit input already has
validation="required:trim|email"(line 92). The FormKit validation will prevent submission of invalid emails, making this additional check unnecessary.♻️ Simplify by removing redundant validation
async function continueWithSSO(form: { email: string }) { - if (!form.email || !form.email.includes('@')) { - toast.error(t('invalid-email', 'Please enter a valid email address')) - return - } - isLoading.value = true try {FormKit's built-in email validation is sufficient.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
.opencode/worktree-session-state.jsonmessages/en.jsonsrc/pages/sso-login.vuesupabase/functions/_backend/private/invite_new_user_to_org.tssupabase/functions/_backend/public/build/start.tssupabase/functions/_backend/utils/discord.tssupabase/functions/mock-sso-callback/index.tssupabase/seed.sql
💤 Files with no reviewable changes (1)
- .opencode/worktree-session-state.json
🚧 Files skipped from review as they are similar to previous changes (1)
- supabase/seed.sql
🧰 Additional context used
📓 Path-based instructions (15)
**/*.{ts,tsx,js,jsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use single quotes and no semicolons per @antfu/eslint-config
Files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.tssrc/pages/sso-login.vuesupabase/functions/_backend/private/invite_new_user_to_org.tssupabase/functions/_backend/utils/discord.ts
supabase/functions/_backend/**
📄 CodeRabbit inference engine (CLAUDE.md)
Backend logic should be organized in
supabase/functions/_backend/with subdirectories for plugins, private endpoints, public endpoints, triggers, and utilities
Files:
supabase/functions/_backend/public/build/start.tssupabase/functions/_backend/private/invite_new_user_to_org.tssupabase/functions/_backend/utils/discord.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use TypeScript strict mode with path aliases mapping
~/tosrc/
Files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/invite_new_user_to_org.tssupabase/functions/_backend/utils/discord.ts
supabase/functions/**/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Supabase Edge Functions use Deno runtime
Files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/invite_new_user_to_org.tssupabase/functions/_backend/utils/discord.ts
supabase/functions/_backend/**/*.{ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
supabase/functions/_backend/**/*.{ts,js}: Backend code must be placed insupabase/functions/_backend/as shared code deployed to Cloudflare Workers (API/Plugin/Files workers), Supabase Edge Functions, and other platforms
UsecreateHonofromutils/hono.tsfor all Hono framework application initialization and routing
All database operations must usegetPgClient()orgetDrizzleClient()fromutils/pg.tsfor PostgreSQL access during active migration to Cloudflare D1
All Hono endpoint handlers must acceptContext<MiddlewareKeyVariables>and usec.get('requestId'),c.get('apikey'), andc.get('auth')for request context
Use structured logging withcloudlog({ requestId: c.get('requestId'), message: '...' })for all backend logging
UsemiddlewareAPISecretfor internal API endpoints andmiddlewareKeyfor external API keys; validate againstowner_orgin theapikeystable
Checkc.get('auth')?.authTypeto determine authentication type ('apikey' vs 'jwt') in backend endpoints
Use Drizzle ORM query patterns withschemafrompostgress_schema.tsfor all database operations; usealiasV2()for self-joins or multiple table references
Files:
supabase/functions/_backend/public/build/start.tssupabase/functions/_backend/private/invite_new_user_to_org.tssupabase/functions/_backend/utils/discord.ts
supabase/functions/**/*.{ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Backend ESLint must pass before commit; run
bun lint:backendfor backend files
Files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/invite_new_user_to_org.tssupabase/functions/_backend/utils/discord.ts
src/**/*.vue
📄 CodeRabbit inference engine (CLAUDE.md)
src/**/*.vue: Use Vue 3 with Composition API and<script setup>syntax for frontend components
Style components using TailwindCSS with DaisyUI components
src/**/*.vue: Use Vue 3<script setup>syntax exclusively for all Vue component scripts
Use Tailwind utility classes for layout and spacing in Vue components
Use DaisyUI components (d-btn,d-input,d-card) for interactive elements in Vue components
Use Konsta components ONLY for safe area helpers (top/bottom insets) in Vue components; avoid other uses
UseuseRoute()fromvue-routerto access route parameters anduseRouter()for programmatic navigation in Vue components
Files:
src/pages/sso-login.vue
src/pages/**/*.vue
📄 CodeRabbit inference engine (CLAUDE.md)
Use file-based routing with unplugin-vue-router for frontend pages
Frontend file-based routing uses
src/pages/directory structure; routes auto-generate withunplugin-vue-routerand types are available insrc/typed-router.d.ts
Files:
src/pages/sso-login.vue
{capacitor.config.{ts,js},src/**/*.{ts,tsx,vue}}
📄 CodeRabbit inference engine (CLAUDE.md)
Mobile apps should use Capacitor with app ID
ee.forgr.capacitor_gofor native mobile functionality
Files:
src/pages/sso-login.vue
src/**/*.{ts,tsx,vue,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Use
~/alias for imports fromsrc/directory in frontend TypeScript and Vue components
Files:
src/pages/sso-login.vue
src/**/*.{vue,ts,js}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Frontend ESLint must pass before commit; run
bun lint:fixto auto-fix issues in frontend files
Files:
src/pages/sso-login.vue
**/{src,app}/{components,views,pages}/**/*.{vue,css,scss}
📄 CodeRabbit inference engine (AGENTS.md)
The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS.
Files:
src/pages/sso-login.vue
**/{src,app}/{components,views,pages}/**/*.vue
📄 CodeRabbit inference engine (AGENTS.md)
Use DaisyUI (
d-prefixed classes) for buttons, inputs, and other interactive primitives to keep behavior and spacing consistent.
Files:
src/pages/sso-login.vue
**/{src,app}/**/*.{vue,ts,tsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Konsta components are reserved for the safe area helpers. Avoid importing
konstaanywhere else in the app.
Files:
src/pages/sso-login.vue
**/{src,app}/**/*.{css,scss,vue}
📄 CodeRabbit inference engine (AGENTS.md)
Capgo's design uses deep slate bases with the 'Extract' azure highlight (
--color-azure-500: #119eff) and soft radii; mirror the palette fromsrc/styles/style.css(e.g.,--color-primary-500: #515271) when introducing new UI.
Files:
src/pages/sso-login.vue
🧠 Learnings (18)
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Check `c.get('auth')?.authType` to determine authentication type ('apikey' vs 'jwt') in backend endpoints
Applied to files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/invite_new_user_to_org.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Use `middlewareAPISecret` for internal API endpoints and `middlewareKey` for external API keys; validate against `owner_org` in the `apikeys` table
Applied to files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/invite_new_user_to_org.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Backend code must be placed in `supabase/functions/_backend/` as shared code deployed to Cloudflare Workers (API/Plugin/Files workers), Supabase Edge Functions, and other platforms
Applied to files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.ts
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to **/{api,server,src/server,backend}/**/*.{ts,tsx,js} : Never use the Supabase admin SDK (with service key) for user-facing APIs. Always use the client SDK with user authentication so RLS policies are enforced. The admin SDK should only be used for internal operations (triggers, CRON jobs, etc.), and when unavoidable, sanitize all user inputs carefully to prevent PostgREST query injection.
Applied to files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/invite_new_user_to_org.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Use `createHono` from `utils/hono.ts` for all Hono framework application initialization and routing
Applied to files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/invite_new_user_to_org.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/**/*.{ts,js} : Backend ESLint must pass before commit; run `bun lint:backend` for backend files
Applied to files:
supabase/functions/_backend/public/build/start.ts
📚 Learning: 2025-12-05T17:34:25.556Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T17:34:25.556Z
Learning: Applies to supabase/functions/**/*.ts : Supabase Edge Functions use Deno runtime
Applied to files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/invite_new_user_to_org.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : All Hono endpoint handlers must accept `Context<MiddlewareKeyVariables>` and use `c.get('requestId')`, `c.get('apikey')`, and `c.get('auth')` for request context
Applied to files:
supabase/functions/_backend/public/build/start.tssupabase/functions/mock-sso-callback/index.tssupabase/functions/_backend/private/invite_new_user_to_org.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Use shared backend code from `supabase/functions/_backend/` across all deployment platforms; never create platform-specific implementations outside this directory
Applied to files:
supabase/functions/_backend/public/build/start.ts
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to supabase/functions/_backend/**/*.{ts,js} : Use structured logging with `cloudlog({ requestId: c.get('requestId'), message: '...' })` for all backend logging
Applied to files:
supabase/functions/mock-sso-callback/index.ts
📚 Learning: 2026-01-08T00:40:00.524Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-08T00:40:00.524Z
Learning: Applies to **/{src,app}/{components,views,pages}/**/*.{vue,css,scss} : The web client is built with Vue.js and Tailwind CSS; lean on utility classes and composition-friendly patterns rather than bespoke CSS.
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to src/**/*.vue : Use `useRoute()` from `vue-router` to access route parameters and `useRouter()` for programmatic navigation in Vue components
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-05T17:34:25.556Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T17:34:25.556Z
Learning: Applies to src/pages/**/*.vue : Use file-based routing with unplugin-vue-router for frontend pages
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-23T02:53:12.055Z
Learnt from: CR
Repo: Cap-go/capgo PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-23T02:53:12.055Z
Learning: Applies to src/pages/**/*.vue : Frontend file-based routing uses `src/pages/` directory structure; routes auto-generate with `unplugin-vue-router` and types are available in `src/typed-router.d.ts`
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-23T01:19:04.593Z
Learnt from: riderx
Repo: Cap-go/capgo PR: 1297
File: src/components/dashboard/DeploymentBanner.vue:77-79
Timestamp: 2025-12-23T01:19:04.593Z
Learning: In the Cap-go codebase, ensure that app permission checks never include the role 'owner'. App-level permissions should be based on the user_min_right enum with values: read, upload, write, admin, super_admin (and NOT owner). This pattern applies across Vue components that perform permission checks; if you find a check referencing 'owner' for app-level access, replace it with the appropriate user_min_right value and keep organization-level owner handling in organization.ts.
Applied to files:
src/pages/sso-login.vue
📚 Learning: 2025-12-24T14:11:10.256Z
Learnt from: WcaleNieWolny
Repo: Cap-go/capgo PR: 1300
File: supabase/migrations/20251224103713_2fa_enforcement.sql:409-539
Timestamp: 2025-12-24T14:11:10.256Z
Learning: In supabase/migrations for get_orgs_v6 and get_orgs_v7: The inner functions with user_id parameter (get_orgs_v6(uuid) and get_orgs_v7(uuid)) should NOT be granted to anon/authenticated roles as this allows any user to query other users' organizations; only the no-argument wrapper functions should be public as they perform authentication checks.
Applied to files:
supabase/functions/_backend/private/invite_new_user_to_org.ts
📚 Learning: 2025-12-25T11:22:19.594Z
Learnt from: WcaleNieWolny
Repo: Cap-go/capgo PR: 1300
File: supabase/migrations/20251224103713_2fa_enforcement.sql:85-96
Timestamp: 2025-12-25T11:22:19.594Z
Learning: In the 2FA enforcement implementation for supabase/migrations: When an org has enforcing_2fa=true, all users including super_admins must have 2FA enabled before accessing any org functions (including check_org_members_2fa_enabled); this is intentional behavior to ensure consistent security enforcement without exceptions for admins.
Applied to files:
supabase/functions/_backend/private/invite_new_user_to_org.ts
📚 Learning: 2025-12-23T01:19:15.067Z
Learnt from: riderx
Repo: Cap-go/capgo PR: 1297
File: src/components/dashboard/DeploymentBanner.vue:77-79
Timestamp: 2025-12-23T01:19:15.067Z
Learning: In the Cap-go codebase, the 'owner' role exists only at the organization level (used in src/stores/organization.ts), not as an app-level permission. App-level permissions use the user_min_right database enum which includes: read, upload, write, admin, super_admin (but NOT owner). When checking app permissions, never include 'owner' in the role checks.
Applied to files:
supabase/functions/_backend/private/invite_new_user_to_org.ts
🧬 Code graph analysis (1)
supabase/functions/_backend/private/invite_new_user_to_org.ts (1)
supabase/functions/_backend/utils/supabase.ts (1)
hasOrgRight(287-304)
🪛 ast-grep (0.40.4)
supabase/functions/_backend/utils/discord.ts
[warning] 34-34: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${escapedField}"\\s*:\\s*"(?:[^"\\\\]|\\\\.)*"\\s*,?\\s*, 'gi')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
[warning] 46-46: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(("${escapedField}"\\s*:\\s*)"((?:[^"\\\\]|\\\\.)*)", 'gi')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html
(regexp-from-variable)
🪛 GitHub Actions: Run tests
supabase/functions/_backend/public/build/start.ts
[error] 70-70: TS2345: Argument of type 'string | null' is not assignable to parameter of type 'string'.
[error] 95-95: TS2345: Argument of type 'string | null' is not assignable to parameter of type 'string'.
[error] 138-138: TS2345: Argument of type 'string | null' is not assignable to parameter of type 'string'.
🔇 Additional comments (10)
supabase/functions/_backend/private/invite_new_user_to_org.ts (2)
51-55: LGTM!Good hardening of the CAPTCHA flow. The early return on error ensures failed CAPTCHA verification is no longer silently ignored.
85-94: Good addition of admin permission check.The explicit admin permission verification before allowing invitations is a solid security improvement. Using
hasOrgRightwith the authenticated user's ID ensures only authorized admins can invite new users.Minor: Consider returning status 401 instead of 500 on line 87 since "Failed to get user" from an authenticated context is more of an authentication issue than a server error.
supabase/functions/_backend/utils/discord.ts (2)
13-16: LGTM! Well-implemented defense-in-depth measure.The
escapeRegexfunction correctly escapes all regex metacharacters, preventing any potential regex injection. This is a solid security practice even though the current field names come from hardcoded constants.
32-47: Static analysis false positive—patterns are safe.The ast-grep warnings about ReDoS on lines 35 and 47 are false positives:
- Field names are hardcoded constants (lines 9, 11), not user input—no injection risk exists.
- The pattern
(?:[^"\\\\]|\\\\.)*is safe: it alternates between a character class and a fixed-length sequence with no nested quantifiers or ambiguous backtracking. This has linear time complexity.The updated regex correctly handles escaped quotes within JSON values (e.g.,
"field":"val\"ue"), and the capture group logic on line 48 properly applies partial redaction.messages/en.json (2)
445-445: LGTM: New translation key added.The new translation key for channel-to-bundle linking is properly formatted and follows the existing naming convention.
1096-1096: LGTM: Typo corrected.Good catch fixing the typo "budnle" → "bundle".
supabase/functions/mock-sso-callback/index.ts (1)
1-20: LGTM: Proper Hono setup.The function correctly uses
createHonofrom utils/hono.ts and imports the necessary utilities. Setup follows backend coding guidelines.Based on learnings, this aligns with the pattern for Supabase Edge Functions using Hono framework.
src/pages/sso-login.vue (3)
1-20: LGTM: Proper Vue 3 Composition API setup.The component correctly uses
<script setup>, imports composables, and follows Vue 3 patterns. The use ofuseRoute()anduseRouter()aligns with frontend coding guidelines.
38-46: LGTM: Secure redirect validation.The redirect path validation properly prevents open redirect vulnerabilities by:
- Only allowing relative paths starting with
/- Blocking protocol/host sequences (
//)- Defaulting to
/dashboardfor invalid inputsThis is a good security practice.
63-163: LGTM: Well-structured Vue template.The template follows frontend guidelines:
- Uses Tailwind utility classes appropriately
- Implements proper loading states with visual feedback (spinner)
- Includes helpful informational content for users
- Has proper accessibility with semantic HTML
- Includes data-test attributes for testing
- Uses FormKit for form handling with proper validation
The component provides a good user experience with clear messaging and feedback.
- Add electron property to channel type in channel_self.ts - Add null checks for apikeyKey before calling markBuildAsFailed - Remove nonexistent plan_enterprise field from global_stats payload All backend TypeScript errors resolved.
Instead of conditionally checking for null throughout the function, add early validation that throws a clear error if the API key is null. This prevents hashed-only keys from starting builds and ensures all downstream code can safely assume apikeyKey is non-null. - Add validation check after apikeyKey assignment - Remove conditional null checks from markBuildAsFailed calls - Provide clear error message for unsupported hashed keys
|



Description
This PR adds the complete frontend UI for SAML SSO authentication, enabling organization administrators to configure SSO providers and users to authenticate via SSO.
What's Included
New Pages
SSO Configuration Wizard (
src/pages/settings/organization/sso.vue)SSO Login Page (
src/pages/sso-login.vue)New Composables
src/composables/useSSODetection.ts)Modified Components
Login Page (
src/pages/login.vue)from_sso=trueparam)Organization Settings (
src/constants/organizationTabs.ts)Internationalization
messages/en.json)User Flows
For Organization Admins
For End Users
/sso-loginSummary by CodeRabbit
New Features
Bug Fixes / Security
Localization
✏️ Tip: You can customize this high-level summary in your review settings.