Conversation
|
Warning Rate limit exceeded
⌛ 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. 📝 WalkthroughWalkthroughAdds an email OTP verification flow for 2FA: localization keys, UI/state and backend RPCs/types, a new user_security table with RLS and trigger enforcement to require recent email verification before MFA enrollment. Changes
Sequence DiagramsequenceDiagram
participant User as User
participant Vue as Vue Component
participant Supabase as Supabase Backend
participant Email as Email Service
User->>Vue: Open account settings
Vue->>Supabase: loadOtpVerification()
Supabase-->>Vue: Return otpVerifiedAt
Vue->>User: Display OTP status
User->>Vue: Click "Send verification code"
Vue->>Supabase: sendOtpVerification()
Supabase->>Email: Send OTP to user's email
Email-->>User: Deliver OTP
Supabase-->>Vue: Acknowledge sent
User->>Vue: Enter OTP code
Vue->>Supabase: verifyOtpForMfa(code)
Supabase->>Supabase: Validate code & record email_otp_verified_at
Supabase-->>Vue: Verification success
Vue->>User: Show verified status
User->>Vue: Enable MFA
Vue->>Supabase: handleMfa() (includes OTP check)
Supabase->>Supabase: auth.enforce_email_otp_for_mfa() verifies recent OTP
alt OTP recent
Supabase-->>Vue: MFA enabled
Vue->>User: Success
else OTP expired
Supabase-->>Vue: Reject enable request
Vue->>User: Prompt to reverify OTP
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
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.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 474d4a99ac
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| IF TG_OP = 'INSERT' THEN | ||
| otp_ok := public.is_recent_email_otp_verified(NEW.user_id); | ||
| IF NOT otp_ok THEN | ||
| RAISE EXCEPTION 'email otp verification required for mfa enrollment'; | ||
| END IF; |
There was a problem hiding this comment.
Preserve backward compatibility for MFA enroll
This trigger rejects any auth.mfa_factors insert/update unless there is a recent user_security.email_otp_verified_at row. The only code that writes user_security in this change is the new settings UI flow, so existing clients (mobile apps, scripts, or older web builds) that call supabase.auth.mfa.enroll() directly will now fail with email otp verification required for mfa enrollment. That’s a backward-incompatible change to the public MFA enrollment API and will block users on older clients from enabling 2FA unless you add a compatibility path (e.g., allow legacy clients or provide a backend endpoint that performs the OTP verification/write).
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/pages/settings/account/index.vue`:
- Around line 44-71: The computed otpVerificationValid is stale because it only
depends on otpVerifiedUntil.value; add a small reactive clock ref (e.g., nowRef)
that updates every X seconds with dayjs() and include nowRef.value in
otpVerificationValid so the computed re-evaluates as time passes; create the
interval in the component setup and clear it on unmount (use onUnmounted) to
avoid leaks, and ensure otpVerificationStatus and otpVerifiedUntilLabel keep
using otpVerificationValid/otpVerifiedUntil so the UI reflects expiry correctly.
- Around line 871-897: Replace the plain HTML buttons and input used for OTP
with DaisyUI components while preserving existing bindings and behavior: swap
the two <button> elements that call sendOtpVerification and verifyOtpForMfa (and
use otpSending, otpVerificationLoading, otpVerificationCode for :class and
:disabled) to use d-btn with the same :class, :disabled, `@click` and text
(t('email-otp-send-code') and t('verify')), and replace the input bound to
otpVerificationCode with a d-input preserving v-model="otpVerificationCode",
inputmode="numeric", :placeholder="t('verification-code')", class styling, and
`@keydown.enter.prevent`="verifyOtpForMfa"; ensure disabled styling and loading
states map to DaisyUI props/classes so behavior and accessibility remain
identical.
There was a problem hiding this comment.
Pull request overview
Adds an email OTP “step-up” verification that must be completed (and remain fresh for 1 hour) before a user can enroll TOTP-based MFA.
Changes:
- Adds a
public.user_securitytable plus helper function and anauth.mfa_factorstrigger to block MFA enrollment unless a recent email OTP verification is recorded. - Updates the account settings UI to send/verify an email OTP and gate the “Enable 2FA” action based on verification freshness.
- Extends i18n strings and updates generated Supabase DB types to include
user_security.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| supabase/migrations/20260204103000_mfa_email_otp_guard.sql | Adds user_security + helper function + trigger enforcing “recent email OTP required” for MFA factor creation/verification. |
| src/types/supabase.types.ts | Adds user_security table typing to the generated Supabase Database types. |
| src/pages/settings/account/index.vue | Adds UI/workflow to send & verify email OTP, store verification time, and block MFA enrollment unless verification is still valid. |
| messages/en.json | Adds new English i18n keys for the email OTP verification UI. |
| "email-otp-2fa-title": "Email verification for 2FA", | ||
| "email-otp-2fa-description": "Verify your email with a one-time code before enabling 2FA. Verification expires after 1 hour.", | ||
| "email-otp-code-required": "Enter the verification code", | ||
| "email-otp-expired": "Verification expired, please verify again", | ||
| "email-otp-not-verified": "Not verified", | ||
| "email-otp-required": "Verify your email before enabling 2FA", | ||
| "email-otp-send-code": "Send verification code", | ||
| "email-otp-sent": "Verification code sent", | ||
| "email-otp-verified": "Email verified for 2FA", | ||
| "email-otp-verified-until": "Verified until {time}", |
There was a problem hiding this comment.
New i18n keys were added only in en.json. Since src/modules/i18n.ts doesn’t configure a fallbackLocale, other locales will render the raw key (and emit missing-key warnings) when this UI is used.
Add these keys to the other messages/*.json locale files (or configure a fallback strategy) to avoid regressions for non-English users.
| const { error: unregisterError } = await supabase.auth.mfa.unenroll({ factorId: data.id }) | ||
| if (error) | ||
| console.error('Cannot unregister MFA', unregisterError) | ||
| } |
There was a problem hiding this comment.
Both cancel paths in handleMfa check if (error) (from the earlier enroll call) instead of checking unregisterError, so MFA unenroll failures won’t be surfaced correctly when the user cancels the setup flow.
src/pages/settings/account/index.vue
Outdated
| email_otp_verified_at: now, | ||
| updated_at: now, |
There was a problem hiding this comment.
This writes email_otp_verified_at using the client’s clock (new Date().toISOString()). Besides clock skew, it also makes the “verified for 1 hour” window dependent on a client-supplied timestamp.
Prefer setting the verification timestamp on the database side (e.g., via a SECURITY DEFINER RPC or trigger that uses now()), and have the client call that instead of sending the timestamp value.
| email_otp_verified_at: now, | |
| updated_at: now, |
| CREATE POLICY "Users can insert own security status" | ||
| ON "public"."user_security" | ||
| FOR INSERT | ||
| TO authenticated | ||
| WITH CHECK (user_id = (select auth.uid())); | ||
|
|
||
| CREATE POLICY "Users can update own security status" | ||
| ON "public"."user_security" | ||
| FOR UPDATE | ||
| TO authenticated | ||
| USING (user_id = (select auth.uid())) | ||
| WITH CHECK (user_id = (select auth.uid())); |
There was a problem hiding this comment.
The user_security RLS policies currently allow any authenticated user to insert and update their own email_otp_verified_at value, even though this column is used by auth.enforce_email_otp_for_mfa to gate MFA enrollment. An attacker who has an authenticated session (e.g., via stolen token) can simply create or update their user_security row and set email_otp_verified_at to a recent timestamp, thereby bypassing the intended email OTP verification requirement for registering or verifying MFA factors. To fix this, prevent direct client-controlled writes to email_otp_verified_at (e.g., restrict INSERT/UPDATE on this column to trusted backend logic or a secure RPC that only sets it after a real OTP check).
There was a problem hiding this comment.
Actionable comments posted: 12
🤖 Fix all issues with AI agents
In `@messages/hi.json`:
- Around line 23-32: Translate the new email-otp JSON keys into Hindi: replace
the values for "email-otp-2fa-title", "email-otp-2fa-description",
"email-otp-code-required", "email-otp-expired", "email-otp-not-verified",
"email-otp-required", "email-otp-send-code", "email-otp-sent",
"email-otp-verified", and "email-otp-verified-until" with appropriate Hindi
translations while keeping the placeholder "{time}" exactly as-is in
"email-otp-verified-until"; ensure tone matches existing Hindi entries and do
not change the keys or any placeholder formatting.
In `@messages/id.json`:
- Around line 23-32: Translate the new email-OTP message values in the id locale
for the keys "email-otp-2fa-title", "email-otp-2fa-description",
"email-otp-code-required", "email-otp-expired", "email-otp-not-verified",
"email-otp-required", "email-otp-send-code", "email-otp-sent",
"email-otp-verified", and "email-otp-verified-until" into Indonesian (replace
the current English strings), keeping the placeholder "{time}" exactly as-is in
"email-otp-verified-until" and preserving punctuation/capitalization conventions
consistent with the rest of the id file.
In `@messages/it.json`:
- Around line 23-32: Translate the new email-otp i18n keys into Italian: replace
values for "email-otp-2fa-title", "email-otp-2fa-description",
"email-otp-code-required", "email-otp-expired", "email-otp-not-verified",
"email-otp-required", "email-otp-send-code", "email-otp-sent",
"email-otp-verified", and "email-otp-verified-until" with appropriate Italian
text (keeping the existing keys unchanged) and ensure the placeholder "{time}"
remains intact in "email-otp-verified-until".
In `@messages/ja.json`:
- Around line 23-32: Several new localization keys (email-otp-2fa-title,
email-otp-2fa-description, email-otp-code-required, email-otp-expired,
email-otp-not-verified, email-otp-required, email-otp-send-code, email-otp-sent,
email-otp-verified, email-otp-verified-until) are still in English; replace each
English value with the appropriate Japanese translation, preserving placeholders
like {time} and JSON string formatting, and follow the file's existing
translation style and UTF-8 encoding to ensure consistency for Japanese users.
In `@messages/ko.json`:
- Around line 23-32: The listed "email-otp-*" message values are still in
English and must be replaced with Korean translations; update each key
("email-otp-2fa-title", "email-otp-2fa-description", "email-otp-code-required",
"email-otp-expired", "email-otp-not-verified", "email-otp-required",
"email-otp-send-code", "email-otp-sent", "email-otp-verified",
"email-otp-verified-until") with proper Korean text, preserving punctuation and
any placeholders (e.g., keep "{time}" exactly as-is for
"email-otp-verified-until"), and ensure translations match the tone/length of
other entries in the file.
In `@messages/pl.json`:
- Around line 23-32: Replace the English values for the listed localization keys
with Polish translations: update "email-otp-2fa-title",
"email-otp-2fa-description", "email-otp-code-required", "email-otp-expired",
"email-otp-not-verified", "email-otp-required", "email-otp-send-code",
"email-otp-sent", "email-otp-verified", and "email-otp-verified-until" to Polish
equivalents (e.g., "Weryfikacja e-mail dla 2FA", "Zweryfikuj swój e‑mail przy
użyciu jednorazowego kodu przed włączeniem 2FA. Weryfikacja wygasa po 1
godzinie.", "Wprowadź kod weryfikacyjny", "Weryfikacja wygasła, zweryfikuj
ponownie", "Niezweryfikowano", "Zweryfikuj swój e‑mail przed włączeniem 2FA",
"Wyślij kod weryfikacyjny", "Kod weryfikacyjny wysłany", "E‑mail zweryfikowany
dla 2FA", and "Zweryfikowano do {time}"), and ensure the {time} placeholder in
"email-otp-verified-until" is preserved exactly.
In `@messages/ru.json`:
- Around line 23-32: The locale file has ten email OTP keys still in English;
replace the English values with correct Russian translations for
"email-otp-2fa-title", "email-otp-2fa-description", "email-otp-code-required",
"email-otp-expired", "email-otp-not-verified", "email-otp-required",
"email-otp-send-code", "email-otp-sent", "email-otp-verified", and
"email-otp-verified-until" so the Russian locale is consistent (ensure
placeholders like {time} are preserved exactly).
In `@messages/tr.json`:
- Around line 23-32: Replace the English values for the email OTP localization
keys with Turkish translations: update "email-otp-2fa-title",
"email-otp-2fa-description", "email-otp-code-required", "email-otp-expired",
"email-otp-not-verified", "email-otp-required", "email-otp-send-code",
"email-otp-sent", "email-otp-verified", and "email-otp-verified-until" in
messages/tr.json to their proper Turkish equivalents while preserving
placeholder {time} in "email-otp-verified-until" and keeping the keys unchanged.
In `@messages/vi.json`:
- Around line 23-32: Replace the English values for the email OTP locale keys
with proper Vietnamese translations: "email-otp-2fa-title",
"email-otp-2fa-description", "email-otp-code-required", "email-otp-expired",
"email-otp-not-verified", "email-otp-required", "email-otp-send-code",
"email-otp-sent", "email-otp-verified", and "email-otp-verified-until"; update
their string values to correct Vietnamese phrases (including preserving the
placeholder {time} in "email-otp-verified-until") so the email OTP verification
flow displays Vietnamese text consistently.
In `@messages/zh-cn.json`:
- Around line 23-32: The listed locale entries (email-otp-2fa-title,
email-otp-2fa-description, email-otp-code-required, email-otp-expired,
email-otp-not-verified, email-otp-required, email-otp-send-code, email-otp-sent,
email-otp-verified, email-otp-verified-until) are still in English; replace each
value with the correct Simplified Chinese translation (preserving placeholders
like {time} exactly), e.g. translate the title/description, prompts and status
messages into Chinese, update the JSON values for those keys
(email-otp-verified-until must keep "{time}" unchanged), and run the locale/JSON
validation to ensure there are no syntax or placeholder errors.
In `@src/pages/settings/account/index.vue`:
- Around line 912-918: Replace the bespoke Tailwind class binding on the MFA
button with DaisyUI button classes so it matches other buttons; specifically,
change the element that has `@click`="handleMfa" to use the base classes "d-btn
d-btn-outline d-btn-sm" and map the dynamic states: when mfaEnabled is false use
a success/emerald DaisyUI variant (e.g., apply the success/emerald styling
class), when mfaEnabled is true use an error/rose variant, and when !mfaEnabled
&& !otpVerificationValid add the disabled styling (opacity/cursor) and keep
:disabled="!mfaEnabled && !otpVerificationValid"; preserve the
`@click`="handleMfa" handler and ensure the conditional class logic is expressed
using the same reactive properties (mfaEnabled, otpVerificationValid) but with
DaisyUI variant classes instead of the current bespoke border/ring classes.
In `@supabase/migrations/20260204103000_mfa_email_otp_guard.sql`:
- Around line 111-163: The trigger function auth.enforce_email_otp_for_mfa
currently skips OTP enforcement for users with user_created_at < enforced_at
(via the enforced_at check from
public.security_settings.mfa_email_otp_enforced_at), which allows pre‑existing
users to bypass OTP; update the function to remove or tighten that bypass by
eliminating the user_created_at < enforced_at conditional (or change the logic
to only bypass when enforcement is explicitly disabled in
public.security_settings), so that both INSERT and UPDATE flows always call
public.is_recent_email_otp_verified and raise the exception if otp_ok is false;
keep the trigger trg_enforce_email_otp_for_mfa and auth.mfa_factors target
unchanged.
🧹 Nitpick comments (3)
messages/pt-br.json (1)
23-32: Translate the new OTP strings to pt‑BR (currently English).These values are user‑visible and should match the locale; leaving them in English creates a language mismatch in the UI.
💬 Suggested pt‑BR translations
- "email-otp-2fa-title": "Email verification for 2FA", - "email-otp-2fa-description": "Verify your email with a one-time code before enabling 2FA. Verification expires after 1 hour.", - "email-otp-code-required": "Enter the verification code", - "email-otp-expired": "Verification expired, please verify again", - "email-otp-not-verified": "Not verified", - "email-otp-required": "Verify your email before enabling 2FA", - "email-otp-send-code": "Send verification code", - "email-otp-sent": "Verification code sent", - "email-otp-verified": "Email verified for 2FA", - "email-otp-verified-until": "Verified until {time}", + "email-otp-2fa-title": "Verificação de e-mail para 2FA", + "email-otp-2fa-description": "Verifique seu e-mail com um código de uso único antes de ativar o 2FA. A verificação expira após 1 hora.", + "email-otp-code-required": "Insira o código de verificação", + "email-otp-expired": "Verificação expirada, verifique novamente", + "email-otp-not-verified": "Não verificado", + "email-otp-required": "Verifique seu e-mail antes de ativar o 2FA", + "email-otp-send-code": "Enviar código de verificação", + "email-otp-sent": "Código de verificação enviado", + "email-otp-verified": "E-mail verificado para 2FA", + "email-otp-verified-until": "Verificado até {time}",messages/fr.json (1)
23-32: Translate the new OTP strings to French (currently English).These UI strings should be localized for the French locale.
💬 Suggested fr translations
- "email-otp-2fa-title": "Email verification for 2FA", - "email-otp-2fa-description": "Verify your email with a one-time code before enabling 2FA. Verification expires after 1 hour.", - "email-otp-code-required": "Enter the verification code", - "email-otp-expired": "Verification expired, please verify again", - "email-otp-not-verified": "Not verified", - "email-otp-required": "Verify your email before enabling 2FA", - "email-otp-send-code": "Send verification code", - "email-otp-sent": "Verification code sent", - "email-otp-verified": "Email verified for 2FA", - "email-otp-verified-until": "Verified until {time}", + "email-otp-2fa-title": "Vérification de l’e-mail pour le 2FA", + "email-otp-2fa-description": "Vérifiez votre e-mail avec un code à usage unique avant d’activer le 2FA. La vérification expire après 1 heure.", + "email-otp-code-required": "Saisissez le code de vérification", + "email-otp-expired": "Vérification expirée, veuillez vérifier à nouveau", + "email-otp-not-verified": "Non vérifié", + "email-otp-required": "Vérifiez votre e-mail avant d’activer le 2FA", + "email-otp-send-code": "Envoyer le code de vérification", + "email-otp-sent": "Code de vérification envoyé", + "email-otp-verified": "E-mail vérifié pour le 2FA", + "email-otp-verified-until": "Vérifié jusqu’à {time}",messages/de.json (1)
23-32: Translate the new OTP strings to German (currently English).These values should be localized for the German locale to keep the UI consistent.
💬 Suggested de translations
- "email-otp-2fa-title": "Email verification for 2FA", - "email-otp-2fa-description": "Verify your email with a one-time code before enabling 2FA. Verification expires after 1 hour.", - "email-otp-code-required": "Enter the verification code", - "email-otp-expired": "Verification expired, please verify again", - "email-otp-not-verified": "Not verified", - "email-otp-required": "Verify your email before enabling 2FA", - "email-otp-send-code": "Send verification code", - "email-otp-sent": "Verification code sent", - "email-otp-verified": "Email verified for 2FA", - "email-otp-verified-until": "Verified until {time}", + "email-otp-2fa-title": "E-Mail-Verifizierung für 2FA", + "email-otp-2fa-description": "Verifiziere deine E-Mail mit einem Einmalcode, bevor du 2FA aktivierst. Die Verifizierung läuft nach 1 Stunde ab.", + "email-otp-code-required": "Bestätigungscode eingeben", + "email-otp-expired": "Verifizierung abgelaufen, bitte erneut verifizieren", + "email-otp-not-verified": "Nicht verifiziert", + "email-otp-required": "E-Mail vor dem Aktivieren von 2FA verifizieren", + "email-otp-send-code": "Bestätigungscode senden", + "email-otp-sent": "Bestätigungscode gesendet", + "email-otp-verified": "E-Mail für 2FA verifiziert", + "email-otp-verified-until": "Verifiziert bis {time}",
| CREATE OR REPLACE FUNCTION "auth"."enforce_email_otp_for_mfa"() RETURNS trigger | ||
| LANGUAGE "plpgsql" SECURITY DEFINER | ||
| SET "search_path" TO '' | ||
| AS $$ | ||
| DECLARE | ||
| otp_ok boolean; | ||
| enforced_at timestamptz; | ||
| user_created_at timestamptz; | ||
| BEGIN | ||
| SELECT public.security_settings.mfa_email_otp_enforced_at | ||
| INTO enforced_at | ||
| FROM public.security_settings | ||
| WHERE public.security_settings.id = true; | ||
|
|
||
| IF enforced_at IS NOT NULL THEN | ||
| SELECT auth.users.created_at | ||
| INTO user_created_at | ||
| FROM auth.users | ||
| WHERE auth.users.id = NEW.user_id; | ||
|
|
||
| IF user_created_at IS NOT NULL AND user_created_at < enforced_at THEN | ||
| RETURN NEW; | ||
| END IF; | ||
| END IF; | ||
|
|
||
| IF TG_OP = 'INSERT' THEN | ||
| otp_ok := public.is_recent_email_otp_verified(NEW.user_id); | ||
| IF NOT otp_ok THEN | ||
| RAISE EXCEPTION 'email otp verification required for mfa enrollment'; | ||
| END IF; | ||
| RETURN NEW; | ||
| END IF; | ||
|
|
||
| IF TG_OP = 'UPDATE' | ||
| AND (NEW.status IS DISTINCT FROM OLD.status) | ||
| AND NEW.status = 'verified' THEN | ||
| otp_ok := public.is_recent_email_otp_verified(NEW.user_id); | ||
| IF NOT otp_ok THEN | ||
| RAISE EXCEPTION 'email otp verification required for mfa enrollment'; | ||
| END IF; | ||
| END IF; | ||
|
|
||
| RETURN NEW; | ||
| END; | ||
| $$; | ||
|
|
||
| ALTER FUNCTION "auth"."enforce_email_otp_for_mfa"() OWNER TO "postgres"; | ||
|
|
||
| DROP TRIGGER IF EXISTS "trg_enforce_email_otp_for_mfa" ON auth.mfa_factors; | ||
| CREATE TRIGGER "trg_enforce_email_otp_for_mfa" | ||
| BEFORE INSERT OR UPDATE ON auth.mfa_factors | ||
| FOR EACH ROW | ||
| EXECUTE FUNCTION auth.enforce_email_otp_for_mfa(); |
There was a problem hiding this comment.
Potential OTP bypass for pre‑existing users.
The cutoff on user_created_at < enforced_at lets all pre‑enforcement users enroll/verify MFA via API without recent OTP, even though the UI blocks it. If the goal is to gate MFA enrollment for all users, remove this bypass (or only bypass when enforcement is explicitly disabled).
🔒 Suggested enforcement adjustment
DECLARE
otp_ok boolean;
enforced_at timestamptz;
- user_created_at timestamptz;
BEGIN
SELECT public.security_settings.mfa_email_otp_enforced_at
INTO enforced_at
FROM public.security_settings
WHERE public.security_settings.id = true;
- IF enforced_at IS NOT NULL THEN
- SELECT auth.users.created_at
- INTO user_created_at
- FROM auth.users
- WHERE auth.users.id = NEW.user_id;
-
- IF user_created_at IS NOT NULL AND user_created_at < enforced_at THEN
- RETURN NEW;
- END IF;
- END IF;
+ IF enforced_at IS NULL THEN
+ RETURN NEW;
+ END IF;🤖 Prompt for AI Agents
In `@supabase/migrations/20260204103000_mfa_email_otp_guard.sql` around lines 111
- 163, The trigger function auth.enforce_email_otp_for_mfa currently skips OTP
enforcement for users with user_created_at < enforced_at (via the enforced_at
check from public.security_settings.mfa_email_otp_enforced_at), which allows
pre‑existing users to bypass OTP; update the function to remove or tighten that
bypass by eliminating the user_created_at < enforced_at conditional (or change
the logic to only bypass when enforcement is explicitly disabled in
public.security_settings), so that both INSERT and UPDATE flows always call
public.is_recent_email_otp_verified and raise the exception if otp_ok is false;
keep the trigger trg_enforce_email_otp_for_mfa and auth.mfa_factors target
unchanged.
a86260c to
bf2b469
Compare
ec6ef91 to
b2be395
Compare
|
|
/tip @testingpene1010 $250 please have look in our production if you confirm that fixed for you |
|
🎉🎈 @testingpene1010 has been awarded $250 by Capgo! 🎈🎊 |



Summary (AI generated)
Test plan (AI generated)
Screenshots (AI generated)
Checklist (AI generated)
Generated with AI