Skip to content

Feature | Add MultiFactor Authentication. Initial migrations and entities#125

Open
matiasperrone-exo wants to merge 2 commits intomainfrom
feat/mfa-phase1---migrations--and--interfaces
Open

Feature | Add MultiFactor Authentication. Initial migrations and entities#125
matiasperrone-exo wants to merge 2 commits intomainfrom
feat/mfa-phase1---migrations--and--interfaces

Conversation

@matiasperrone-exo
Copy link
Copy Markdown
Contributor

@matiasperrone-exo matiasperrone-exo commented Apr 22, 2026

Task:

Ref: https://app.clickup.com/t/86b9e8wm7

Changes

  • Add migration for 2FA schema foundation (Phase I)
  • Add UserTrustedDevice entity
  • Add TwoFactorAuditLog entity
  • Add UserRecoveryCode entity
  • Add repository interfaces for 2FA entities
  • Add Doctrine implementations for 2FA repositories
  • Register 2FA repositories in service container
  • Add repository round-trip tests for 2FA entities

Requested Goal

Current state

The IDP database has no 2FA-related tables or columns. The User entity lacks fields for tracking two-factor enrollment or enforcement status.

Target state

Three new tables (user_trusted_devices, two_factor_audit_log, user_recovery_codes) and three new columns on the users table (two_factor_enabled, two_factor_method, two_factor_enforced_at) exist via a single additive Doctrine migration. Corresponding Doctrine entities and repositories are registered in the ORM mappings.

This migration is the foundation for every other Phase I ticket. It must run with zero downtime since it only adds nullable columns and new tables.

Tasks

  • Create Doctrine migration adding three columns to the users table: two_factor_enabled (boolean, default false), two_factor_method (string/enum, default 'email_otp'), two_factor_enforced_at
    (datetime, nullable)
  • Create UserTrustedDevice Doctrine entity with fields: id, user_id (FK), device_identifier (varchar 255), device_name (varchar 255), ip_address (varchar 45), user_agent (text), trusted_at,
    expires_at, last_seen_at, is_revoked (boolean, default false), created_at, updated_at. Indexes on (user_id, device_identifier), (user_id, is_revoked), (expires_at)
  • Create TwoFactorAuditLog Doctrine entity with fields: id, user_id (FK), event_type (enum: challenge_issued, challenge_succeeded, challenge_failed, enrollment_changed, device_trusted,
    device_revoked, recovery_used, settings_changed), method (enum: email_otp, sms_otp, totp, passkey, recovery), ip_address (varchar 45), user_agent (text), metadata (json, nullable), created_at.
    Indexes on (user_id, event_type, created_at), (created_at)
  • Create UserRecoveryCode Doctrine entity with fields: id, user_id (FK), code_hash (varchar 255, bcrypt hash), used_at (datetime, nullable), created_at. Index on (user_id, used_at)
  • Create IUserTrustedDeviceRepository, IUserRecoveryCodeRepository, ITwoFactorAuditLogRepository interfaces and their Doctrine implementations
  • Register all new entities in the Doctrine ORM entity mappings
  • Verify migration runs cleanly on a fresh database and on an existing database with data

ACCEPTANCE CRITERIA

php artisan doctrine:migrations:migrate runs without errors on both fresh and populated databases
All three new tables exist with correct column types, defaults, and indexes
Users table has the three new columns with correct defaults (two_factor_enabled=false, two_factor_method='email_otp', two_factor_enforced_at=NULL)
Each Doctrine entity can be persisted and retrieved via its repository ( provide unit tests )
Migration is fully additive: no columns dropped, no data modified, no destructive operations
Existing application functionality is unaffected after migration (login, OTP flow, admin panel all work)

DEVELOPMENT NOTES

Key files:

  • New: app/libs/Auth/Models/UserTrustedDevice.php
  • New: app/libs/Auth/Models/TwoFactorAuditLog.php
  • New: app/libs/Auth/Models/UserRecoveryCode.php
  • New: database/migrations/Version*.php (single migration file)
  • Modified: Doctrine entity mapping configuration

Gotchas

  • The User entity uses Doctrine ORM, not Eloquent. All mappings follow the existing annotation/attribute pattern in the codebase.
  • device_identifier stores a SHA-256 hash, not the raw cookie token. Size varchar(255) is sufficient for hex-encoded SHA-256 (64 chars).
  • The OTP table (oauth2_otp) already has phone_number and connection='sms' columns from existing infrastructure. Do NOT modify this table.
  • two_factor_method should be stored as a string column, not a database-level ENUM, to allow adding values in Phase II/III without migration.

Out of scope:

User entity PHP methods (separate ticket), service layer code, UI changes.

Summary by CodeRabbit

  • New Features

    • Added two-factor authentication with trusted device management
    • Added recovery code system
    • Added security event audit logging
  • Tests

    • Added comprehensive tests for two-factor authentication features
  • Chores

    • Updated database schema to support two-factor authentication

feat(2fa): add migration for 2FA schema foundation (Phase I)
feat(2fa): add UserTrustedDevice entity
feat(2fa): add TwoFactorAuditLog entity
feat(2fa): add UserRecoveryCode entity
feat(2fa): add repository interfaces for 2FA entities
feat(2fa): add Doctrine implementations for 2FA repositories
feat(2fa): register 2FA repositories in service container
test(2fa): add repository round-trip tests for 2FA entities
@matiasperrone-exo matiasperrone-exo self-assigned this Apr 22, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 22, 2026

Warning

Rate limit exceeded

@matiasperrone-exo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 9 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 13 minutes and 9 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 42129c93-fa2c-4a0f-8f7e-010c57cc3d84

📥 Commits

Reviewing files that changed from the base of the PR and between 6a2bd34 and 4624ff5.

📒 Files selected for processing (5)
  • app/Repositories/DoctrineUserTrustedDeviceRepository.php
  • app/libs/Auth/Models/UserRecoveryCode.php
  • app/libs/Auth/Models/UserTrustedDevice.php
  • database/migrations/Version20260424120000.php
  • tests/TwoFactorRepositoriesTest.php
📝 Walkthrough

Walkthrough

This pull request introduces a foundational two-factor authentication (2FA) infrastructure by adding three new Doctrine ORM entities for managing trusted devices, audit logging, and recovery codes, along with their corresponding repository interfaces and implementations. A database migration creates the supporting tables, and integration tests validate the persistence layer.

Changes

Cohort / File(s) Summary
Entity Models
app/libs/Auth/Models/TwoFactorAuditLog.php, app/libs/Auth/Models/UserRecoveryCode.php, app/libs/Auth/Models/UserTrustedDevice.php
Added three new Doctrine ORM entities with mapped database tables, field definitions, typed getters/setters, and lifecycle initialization logic. Entities include audit event tracking, recovery code management, and trusted device records with revocation and expiration support.
Repository Interfaces
app/libs/Auth/Repositories/ITwoFactorAuditLogRepository.php, app/libs/Auth/Repositories/IUserRecoveryCodeRepository.php, app/libs/Auth/Repositories/IUserTrustedDeviceRepository.php
Added three repository interfaces extending IBaseRepository with method signatures for querying audit logs by user, retrieving/deleting unused recovery codes, and fetching active trusted devices by user and device identifier.
Repository Implementations
app/Repositories/DoctrineTwoFactorAuditLogRepository.php, app/Repositories/DoctrineUserRecoveryCodeRepository.php, app/Repositories/DoctrineUserTrustedDeviceRepository.php
Implemented concrete Doctrine repositories with query methods for retrieving recent audit logs, unused recovery codes, and active trusted devices; includes a delete operation for recovery codes.
Service Registration
app/Repositories/RepositoriesProvider.php
Registered three new repository interfaces in the service container with their Doctrine implementations and added them to the provides() array for deferred loading.
Database Schema
database/migrations/Version20260416194357.php
Created migration adding two_factor_enabled, two_factor_method, and two_factor_enforced_at columns to the users table, and three new tables: user_trusted_devices, two_factor_audit_log, and user_recovery_codes, each with appropriate indexes and cascade delete foreign keys.
Integration Tests
tests/TwoFactorRepositoriesTest.php
Added PHPUnit test suite validating round-trip persistence and retrieval for trusted devices, audit logs, and recovery codes, including device activation/revocation, code usage marking, and batch deletion operations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • smarcet
  • romanetar

Poem

🐰 Hops of joy through databases new,
Four models hopping, each one true,
Devices trusted, logs so bright,
Recovery codes shine in the night,
Two-factor hops, security's delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly describes the main change: introducing Multi-Factor Authentication with initial migrations and entities. It accurately summarizes the Phase I 2FA foundation work across database schema, entity models, repositories, and tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mfa-phase1---migrations--and--interfaces

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/openstackid/openapi/pr-125/

This page is automatically updated on each push to this PR.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (6)
app/libs/Auth/Repositories/IUserRecoveryCodeRepository.php (1)

25-28: Consider documenting transaction/atomicity expectations for deleteAllForUser.

Since this is typically invoked during recovery-code regeneration (delete-then-insert), callers likely need the operation wrapped in a transaction with the subsequent re-insert to avoid leaving a user with zero recovery codes on partial failure. Document whether callers must manage the transaction themselves, or have the implementation wrap it. A one-line docblock addition is sufficient.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/libs/Auth/Repositories/IUserRecoveryCodeRepository.php` around lines 25 -
28, Add a one-line sentence to the deleteAllForUser(User $user): int docblock
clarifying transaction/atomicity expectations: state whether implementations
will wrap this operation in a database transaction or whether callers must
perform a surrounding transaction when doing delete-then-insert (e.g., "This
method does NOT start a transaction; callers should wrap delete-and-reinsert
operations in a transaction to ensure atomicity." or the converse if
implementations guarantee transactional semantics). Reference the method name
deleteAllForUser in the docblock so callers know the requirement.
app/Repositories/DoctrineUserRecoveryCodeRepository.php (1)

34-42: Consider flushing pending UoW changes or documenting caller responsibility.

Query::execute() on a DQL DELETE bypasses the UnitOfWork and runs directly against the DB. If a caller has pending in-memory changes on UserRecoveryCode entities for this user (e.g. newly persisted but not flushed, or a markUsed() pending flush), those will not be considered by the bulk delete and may re-surface on a later flush(), or conflict. Either document this (bulk operation; caller must flush/clear beforehand) or call $em->flush()/$em->clear(UserRecoveryCode::class) around the delete. Given the "used when regenerating" use case, a docblock note is likely sufficient.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Repositories/DoctrineUserRecoveryCodeRepository.php` around lines 34 -
42, The bulk DQL delete in deleteAllForUser bypasses the UnitOfWork and can
conflict with pending in-memory UserRecoveryCode changes; update the docblock
for DoctrineUserRecoveryCodeRepository::deleteAllForUser to state this is a bulk
operation and callers must flush or clear pending UserRecoveryCode changes
before calling, or alternatively call $em->flush() and/or
$em->clear(UserRecoveryCode::class) around the delete inside deleteAllForUser to
ensure UoW consistency (refer to getEntityManager(), UserRecoveryCode and the
Query::execute() bulk delete in your change).
app/libs/Auth/Models/UserRecoveryCode.php (1)

27-29: Minor: attribute order — JoinColumn is placed before ManyToOne.

Doctrine accepts either order, but the conventional pairing is #[ManyToOne] first, then #[JoinColumn], which matches Doctrine docs/examples and the surrounding codebase's style. Purely stylistic; no behavior change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/libs/Auth/Models/UserRecoveryCode.php` around lines 27 - 29, Swap the
attribute order on the $user property so #[ORM\ManyToOne(...)] appears before
#[ORM\JoinColumn(...)]; specifically update the attributes on the private $user
property (currently using #[ORM\JoinColumn(...)] then #[ORM\ManyToOne(...)] ) to
#[ORM\ManyToOne(targetEntity: \Auth\User::class)] followed by
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', onDelete:
'CASCADE')], preserving the same arguments.
app/Repositories/DoctrineUserTrustedDeviceRepository.php (1)

35-41: Consider deterministic ordering for getActiveByUser.

No orderBy is specified, so results come back in storage order. For a user-facing "trusted devices" listing, ordering by last_seen_at DESC (or trusted_at DESC) produces a more predictable/useful listing and avoids flaky tests if ever asserting order.

♻️ Suggested change
     public function getActiveByUser(User $user): array
     {
         return $this->findBy([
             'user'       => $user,
             'is_revoked' => false,
-        ]);
+        ], ['last_seen_at' => 'DESC']);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Repositories/DoctrineUserTrustedDeviceRepository.php` around lines 35 -
41, getActiveByUser currently returns results with no deterministic ordering;
update the repository method (getActiveByUser) to return active devices ordered
by most-recent activity (e.g. order by last_seen_at DESC or trusted_at DESC
depending on your entity field name) so the user-facing list and tests are
stable—use the Doctrine findBy third parameter for orderBy or switch to the
query builder in DoctrineUserTrustedDeviceRepository::getActiveByUser to apply
the DESC ordering on the appropriate timestamp field.
database/migrations/Version20260416194357.php (2)

33-39: Partial-state guard only checks one column.

The up() guard checks only two_factor_enabled. If a prior run failed after adding two_factor_enabled but before adding the other two columns (or vice versa in a manual intervention), subsequent runs will silently skip the missing columns. Consider guarding each column addition independently, e.g.:

-        if ($schema->hasTable("users") && !$builder->hasColumn("users", "two_factor_enabled")) {
-            $builder->table('users', function (Table $table) {
-                $table->boolean('two_factor_enabled')->setNotnull(true)->setDefault(false);
-                $table->string('two_factor_method', 32)->setNotnull(true)->setDefault('email_otp');
-                $table->dateTime('two_factor_enforced_at')->setNotnull(false)->setDefault(null);
-            });
-        }
+        if ($schema->hasTable("users")) {
+            $builder->table('users', function (Table $table) use ($builder) {
+                if (!$builder->hasColumn("users", "two_factor_enabled")) {
+                    $table->boolean('two_factor_enabled')->setNotnull(true)->setDefault(false);
+                }
+                if (!$builder->hasColumn("users", "two_factor_method")) {
+                    $table->string('two_factor_method', 32)->setNotnull(true)->setDefault('email_otp');
+                }
+                if (!$builder->hasColumn("users", "two_factor_enforced_at")) {
+                    $table->dateTime('two_factor_enforced_at')->setNotnull(false)->setDefault(null);
+                }
+            });
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@database/migrations/Version20260416194357.php` around lines 33 - 39, The
migration's up() currently gates adding all three columns behind a single check
for two_factor_enabled, so if a prior run added one column and failed the rest
will be skipped; change the logic to first verify $schema->hasTable("users") and
then individually check $builder->hasColumn("users", "<column_name>") for each
of the three columns (two_factor_enabled, two_factor_method,
two_factor_enforced_at) and add each missing column (using the existing
builder->table('users', function (Table $table) { ... }) block or separate
blocks) so each column is created idempotently even if previous runs partially
applied the migration.

55-58: Consider a UNIQUE index on (user_id, device_identifier).

DoctrineUserTrustedDeviceRepository::getActiveByUserAndIdentifier() is expected to return a single row for a given (user, device_identifier) pair, and device_identifier is a SHA-256 hex string (globally unique per device/token). A plain index allows duplicate rows to accumulate (e.g., re-trusting the same device on retries) and makes the "single active device" contract unenforceable at the DB layer. A unique constraint would prevent silent duplication and align the schema with the repository's semantics.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@database/migrations/Version20260416194357.php` around lines 55 - 58, Replace
the non-unique index on (user_id, device_identifier) with a UNIQUE constraint so
the DB enforces one row per (user_id, device_identifier) pair; update the
migration where $table->index(["user_id", "device_identifier"],
"utd_user_device_idx") is declared in Version20260416194357.php to create a
unique index/constraint (e.g. using $table->unique(...) or the DBAL equivalent)
with a clear name like "utd_user_device_unique", and remove or replace the
existing plain index to avoid duplicate-key conflicts; this aligns the schema
with DoctrineUserTrustedDeviceRepository::getActiveByUserAndIdentifier()
expectations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/libs/Auth/Models/UserRecoveryCode.php`:
- Line 66: The magic __get method on class UserRecoveryCode exposes
private/protected properties (notably code_hash) and bypasses the explicit
getters; replace it with a safe approach by removing the passthrough or
implementing an allow-list: in UserRecoveryCode::__get only return values for an
explicit whitelist of non-sensitive keys (e.g., id, user_id, created_at) or
throw/return null for anything else, so sensitive fields like code_hash are not
accessible via property access while preserving legacy compatibility for
approved fields.

In `@app/libs/Auth/Models/UserTrustedDevice.php`:
- Line 85: Remove the magic __get() passthrough from the UserTrustedDevice
entity: delete the public function __get($name) { return $this->{$name}; } so
callers no longer bypass explicit accessors (e.g. getIsRevoked(), isRevoked(),
getUser()) and Doctrine proxies fire their lazy-load hooks; update any call
sites that relied on property-style access to use the declared getters instead
to preserve type casting and avoid silent nulls for missing properties.

In `@tests/TwoFactorRepositoriesTest.php`:
- Around line 37-41: The test currently grabs an arbitrary real user via
IUserRepository->findOneBy([]) in setUp and testRecoveryCodeRoundTrip calls
RecoveryCodeRepository->deleteAllForUser($this->user), risking deletion of real
users' data; change setUp to create (and persist) a dedicated test user or
select a user explicitly marked for testing, then use that user object for the
test, and in tearDown remove that test user and assert deleteAllForUser removed
exactly the number of recovery codes inserted by the test (or call delete only
for the specific created entities) so that testRecoveryCodeRoundTrip no longer
wipes production/seeding data. Ensure you update any references to findOneBy,
testRecoveryCodeRoundTrip, deleteAllForUser, setUp and tearDown accordingly.

---

Nitpick comments:
In `@app/libs/Auth/Models/UserRecoveryCode.php`:
- Around line 27-29: Swap the attribute order on the $user property so
#[ORM\ManyToOne(...)] appears before #[ORM\JoinColumn(...)]; specifically update
the attributes on the private $user property (currently using
#[ORM\JoinColumn(...)] then #[ORM\ManyToOne(...)] ) to
#[ORM\ManyToOne(targetEntity: \Auth\User::class)] followed by
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', onDelete:
'CASCADE')], preserving the same arguments.

In `@app/libs/Auth/Repositories/IUserRecoveryCodeRepository.php`:
- Around line 25-28: Add a one-line sentence to the deleteAllForUser(User
$user): int docblock clarifying transaction/atomicity expectations: state
whether implementations will wrap this operation in a database transaction or
whether callers must perform a surrounding transaction when doing
delete-then-insert (e.g., "This method does NOT start a transaction; callers
should wrap delete-and-reinsert operations in a transaction to ensure
atomicity." or the converse if implementations guarantee transactional
semantics). Reference the method name deleteAllForUser in the docblock so
callers know the requirement.

In `@app/Repositories/DoctrineUserRecoveryCodeRepository.php`:
- Around line 34-42: The bulk DQL delete in deleteAllForUser bypasses the
UnitOfWork and can conflict with pending in-memory UserRecoveryCode changes;
update the docblock for DoctrineUserRecoveryCodeRepository::deleteAllForUser to
state this is a bulk operation and callers must flush or clear pending
UserRecoveryCode changes before calling, or alternatively call $em->flush()
and/or $em->clear(UserRecoveryCode::class) around the delete inside
deleteAllForUser to ensure UoW consistency (refer to getEntityManager(),
UserRecoveryCode and the Query::execute() bulk delete in your change).

In `@app/Repositories/DoctrineUserTrustedDeviceRepository.php`:
- Around line 35-41: getActiveByUser currently returns results with no
deterministic ordering; update the repository method (getActiveByUser) to return
active devices ordered by most-recent activity (e.g. order by last_seen_at DESC
or trusted_at DESC depending on your entity field name) so the user-facing list
and tests are stable—use the Doctrine findBy third parameter for orderBy or
switch to the query builder in
DoctrineUserTrustedDeviceRepository::getActiveByUser to apply the DESC ordering
on the appropriate timestamp field.

In `@database/migrations/Version20260416194357.php`:
- Around line 33-39: The migration's up() currently gates adding all three
columns behind a single check for two_factor_enabled, so if a prior run added
one column and failed the rest will be skipped; change the logic to first verify
$schema->hasTable("users") and then individually check
$builder->hasColumn("users", "<column_name>") for each of the three columns
(two_factor_enabled, two_factor_method, two_factor_enforced_at) and add each
missing column (using the existing builder->table('users', function (Table
$table) { ... }) block or separate blocks) so each column is created
idempotently even if previous runs partially applied the migration.
- Around line 55-58: Replace the non-unique index on (user_id,
device_identifier) with a UNIQUE constraint so the DB enforces one row per
(user_id, device_identifier) pair; update the migration where
$table->index(["user_id", "device_identifier"], "utd_user_device_idx") is
declared in Version20260416194357.php to create a unique index/constraint (e.g.
using $table->unique(...) or the DBAL equivalent) with a clear name like
"utd_user_device_unique", and remove or replace the existing plain index to
avoid duplicate-key conflicts; this aligns the schema with
DoctrineUserTrustedDeviceRepository::getActiveByUserAndIdentifier()
expectations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 672e540a-70f5-47fd-8667-ab3e07d97782

📥 Commits

Reviewing files that changed from the base of the PR and between db9f777 and 6a2bd34.

📒 Files selected for processing (12)
  • app/Repositories/DoctrineTwoFactorAuditLogRepository.php
  • app/Repositories/DoctrineUserRecoveryCodeRepository.php
  • app/Repositories/DoctrineUserTrustedDeviceRepository.php
  • app/Repositories/RepositoriesProvider.php
  • app/libs/Auth/Models/TwoFactorAuditLog.php
  • app/libs/Auth/Models/UserRecoveryCode.php
  • app/libs/Auth/Models/UserTrustedDevice.php
  • app/libs/Auth/Repositories/ITwoFactorAuditLogRepository.php
  • app/libs/Auth/Repositories/IUserRecoveryCodeRepository.php
  • app/libs/Auth/Repositories/IUserTrustedDeviceRepository.php
  • database/migrations/Version20260416194357.php
  • tests/TwoFactorRepositoriesTest.php

Comment thread app/libs/Auth/Models/UserRecoveryCode.php Outdated
Comment thread app/libs/Auth/Models/UserTrustedDevice.php Outdated
Comment thread tests/TwoFactorRepositoriesTest.php Outdated
Copy link
Copy Markdown

@martinquiroga-exo martinquiroga-exo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matiasperrone-exo please see comments. Thanks

Comment thread app/Repositories/DoctrineUserTrustedDeviceRepository.php
Comment thread database/migrations/Version20260416194357.php
Comment thread tests/TwoFactorRepositoriesTest.php
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/openstackid/openapi/pr-125/

This page is automatically updated on each push to this PR.

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1---migrations--and--interfaces branch from bd6e93f to f75c63d Compare April 24, 2026 20:59
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/openstackid/openapi/pr-125/

This page is automatically updated on each push to this PR.

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1---migrations--and--interfaces branch from f75c63d to 4624ff5 Compare April 24, 2026 21:04
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/openstackid/openapi/pr-125/

This page is automatically updated on each push to this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants