Skip to content

Feature | Add AuthService validateCredentials method#127

Open
matiasperrone-exo wants to merge 3 commits intofeat/mfa-phase1-user-modelfrom
feat/mfa-phase1-authservice-validatecredentials-method
Open

Feature | Add AuthService validateCredentials method#127
matiasperrone-exo wants to merge 3 commits intofeat/mfa-phase1-user-modelfrom
feat/mfa-phase1-authservice-validatecredentials-method

Conversation

@matiasperrone-exo
Copy link
Copy Markdown
Contributor

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

Task:

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

Summary

Adds validateCredentials() and loginUser() to AuthService as the foundation for the MFA two-step login flow. The new methods split what was previously a single login() call into two discrete phases: verify the password (phase 1, no session) and establish the session (phase 2, after the second factor passes).

Changes

app/libs/Auth/AuthService.php

  • validateCredentials(string $username, string $password): User — validates credentials via Auth::getProvider()->retrieveByCredentials() without calling Auth::attempt(), so no session is established. Pre-checks the user's active state before touching the provider; if the account is locked it throws immediately with a distinct "is locked" message (the provider swallows AuthenticationLockedUserLoginAttempt and returns null, masking the lock state). Security checkpoints such as LockUserCounterMeasure still fire on provider-level failures.
  • loginUser(User $user, bool $remember): void — thin wrapper around Auth::login() for use after the second factor is verified.
  • getCurrentUser() — added instanceof User guard to prevent a non-User return type.
  • login() — removed stale $this->last_login_error = "" line and duplicated @params docblock entry.

app/libs/Utils/Services/IAuthService.php

  • Added validateCredentials() and loginUser() to the interface with full doc-blocks explaining the session-safety contract.

Tests

  • tests/unit/AuthServiceValidateCredentialsTest.php — isolated unit tests (Mockery alias mocks for Auth and Log facades, run in separate processes). Covers: valid credentials
    return user without session, invalid credentials throw AuthenticationException, locked account short-circuits before calling the provider, active user does not take the locked path, loginUser() with remember = true/false.
  • tests/AuthServiceValidateCredentialsIntegrationTest.php — integration test against the real database and security-checkpoint stack. Verifies that a failed validateCredentials() call increments login_failed_attempt via LockUserCounterMeasure, and that a successful call returns the User entity without establishing a session (Auth::check() stays false).

Test plan

  • Run unit suite: docker compose exec app ./vendor/bin/phpunit tests/unit/AuthServiceValidateCredentialsTest.php
  • Run integration test: docker compose exec app ./vendor/bin/phpunit tests/AuthServiceValidateCredentialsIntegrationTest.php
  • Run full suite to check for regressions: docker compose exec app ./vendor/bin/phpunit

Requested Goal

Current state

AuthService::login() validates credentials AND establishes a session in a single call. There is no way to validate a user's password without logging them in, which is required to insert a 2FA gate between credential validation and session establishment.

Target state

AuthService exposes a new validateCredentials(string $username, string $password): User method that validates the password via the existing CustomAuthProvider::retrieveByCredentials() (which handles attempt tracking and account locking via security checkpoints) and returns the authenticated User object WITHOUT establishing a
session. Throws AuthenticationException on failure.

TASKS

  • Add validateCredentials() method to IAuthService interface
  • Implement validateCredentials() in AuthService: delegates to Auth::getProvider()->retrieveByCredentials() with username and password, throws AuthenticationException if user is null
  • Add loginUser(User $user, bool $remember) method to IAuthService interface (needed by verify2FA to establish session after 2FA succeeds) → it should call Auth::login($user, $remember);
  • Write unit test: valid credentials return User object without session being established
  • Write unit test: invalid credentials throw AuthenticationException
  • Write unit test: locked account throws AuthenticationException with locked-account message
  • Write integration test: validateCredentials() triggers security checkpoint tracking (failed attempt counter increments on failure)

ACCEPTANCE CRITERIA

  • validateCredentials() with correct username/password returns the User object
  • validateCredentials() with wrong password throws AuthenticationException
  • validateCredentials() does NOT call Auth::login() or establish any session
  • After validateCredentials() succeeds, Session::get('login_web') is null (no authenticated session)
  • Security checkpoints still fire: failed attempts increment the counter, account locking still triggers after threshold
  • loginUser() establishes a Laravel session for the given User object ( call Auth::login() )
  • All tests pass

DEVELOPMENT NOTES

Key files:

  • Modified: app/Services/Auth/IAuthService.php (interface)
  • Modified: app/Services/Auth/AuthService.php (implementation)
  • Modified: app/libs/Auth/CustomAuthProvider.php (reference only, do not modify)
  • New: tests/Unit/AuthServiceValidateCredentialsTest.php

Gotchas:

  • CustomAuthProvider::retrieveByCredentials() is the ONLY path for credential validation. Do NOT duplicate password checking logic. The provider handles password hashing, security checkpoints,
    and lock countermeasures.
  • The existing login() method calls Auth::attempt() which both validates AND establishes a session. validateCredentials() must NOT use Auth::attempt().
  • LockUserCounterMeasure tracks failed attempts. Ensure validateCredentials() triggers this on failure by going through the same code path as the existing login flow.

Out of scope:

Controller changes

@matiasperrone-exo matiasperrone-exo self-assigned this Apr 27, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5981b9d6-abc9-4742-8457-9bb3f04e0923

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mfa-phase1-authservice-validatecredentials-method

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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-127/

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

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-user-model branch 2 times, most recently from 133b2a8 to 1d8c6e4 Compare April 28, 2026 21:52
@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from d560647 to f4ee340 Compare April 28, 2026 21:53
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

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

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

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from f4ee340 to a293dde Compare April 28, 2026 22:07
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

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

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

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from a293dde to 496a595 Compare April 28, 2026 22:10
@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-user-model branch from 5f9b3a2 to 894cbe4 Compare April 28, 2026 22:10
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

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

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

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-user-model branch from 894cbe4 to fc6818d Compare April 28, 2026 22:12
@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from 496a595 to 1bbe5ff Compare April 28, 2026 22:13
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

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

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

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from 1bbe5ff to e1bbb75 Compare April 28, 2026 22:17
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

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

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

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from e1bbb75 to ca405e6 Compare April 29, 2026 14:30
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

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

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

1 similar comment
@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

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

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

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

Comment thread app/libs/Auth/AuthService.php
Comment thread app/libs/Auth/AuthService.php Outdated
Comment thread app/libs/Auth/AuthService.php
Comment thread app/libs/Auth/AuthService.php Outdated
Comment thread app/libs/Auth/AuthService.php Outdated
Comment thread app/libs/Auth/AuthService.php Outdated
Copy link
Copy Markdown
Collaborator

@smarcet smarcet 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 review comments
@matiasperrone-exo @martinquiroga-exo changes on existent code are out of scope
anything like that should be discussed before any proposed changes

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-user-model branch 3 times, most recently from 4b760a8 to 9146757 Compare April 30, 2026 19:06
- test: cover canLogin()=false branch in validateCredentials() unit tests
- docs: document known double-query cost in validateCredentials()
- fix: use consistent error message in validateCredentials()
@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from a810012 to cfd3bb7 Compare May 4, 2026 21:01
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

📘 OpenAPI / Swagger preview

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

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

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from cfd3bb7 to f1afe11 Compare May 4, 2026 21:07
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

📘 OpenAPI / Swagger preview

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

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

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from f1afe11 to 80972f0 Compare May 4, 2026 21:12
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

📘 OpenAPI / Swagger preview

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

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

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.

LGTM

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

📘 OpenAPI / Swagger preview

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

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

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from ecd7cd3 to 6b5f96e Compare May 5, 2026 14:01
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

📘 OpenAPI / Swagger preview

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

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

@smarcet
Copy link
Copy Markdown
Collaborator

smarcet commented May 5, 2026

@martinquiroga-exo the ticket ask for

Implement validateCredentials() in AuthService: delegates to Auth::getProvider()->retrieveByCredentials() with username and password, throws AuthenticationException if user is null

but the code is not following that please re review it

https://github.com/OpenStackweb/openstackid/pull/127/changes#diff-31b8c36cbff745e9a0410953fc435b4b933632a787b6815b2e861af3a5f6adb3R198

Copy link
Copy Markdown
Collaborator

@smarcet smarcet 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 matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from 6b5f96e to 5f3c28e Compare May 5, 2026 14:54
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 5, 2026

📘 OpenAPI / Swagger preview

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

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

@matiasperrone-exo matiasperrone-exo force-pushed the feat/mfa-phase1-authservice-validatecredentials-method branch from 5f3c28e to 70ba198 Compare May 5, 2026 14:58
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.

3 participants