Skip to content

feat: Implement external login functionality and user profile management#53

Merged
taiphanvan2k3 merged 1 commit intomainfrom
feat/implement-profile-endpoint
Nov 22, 2025
Merged

feat: Implement external login functionality and user profile management#53
taiphanvan2k3 merged 1 commit intomainfrom
feat/implement-profile-endpoint

Conversation

@taiphanvan2k3
Copy link
Copy Markdown
Member

@taiphanvan2k3 taiphanvan2k3 commented Nov 22, 2025

  • Added ExternalLoginCommand and its handler to support external provider logins (e.g., Google, Facebook).
  • Created ExternalLoginCommandValidator for input validation of external login requests.
  • Developed GetProfileQuery and its handler to retrieve the current user's profile information.
  • Implemented UpdateProfileCommand and its handler for updating user profile details.
  • Introduced necessary response models for external login and user profile operations.
  • Updated AuthController to include endpoints for external login and user logout.
  • Refactored UsersController to manage user profile retrieval and updates.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added endpoint to retrieve current user profile containing identity details, assigned roles, and linked external login accounts
    • Added endpoint to update user profile, allowing modification of display name and avatar URL
    • Implemented logout endpoint for secure session termination and token revocation

✏️ Tip: You can customize this high-level summary in your review settings.

- Added ExternalLoginCommand and its handler to support external provider logins (e.g., Google, Facebook).
- Created ExternalLoginCommandValidator for input validation of external login requests.
- Developed GetProfileQuery and its handler to retrieve the current user's profile information.
- Implemented UpdateProfileCommand and its handler for updating user profile details.
- Introduced necessary response models for external login and user profile operations.
- Updated AuthController to include endpoints for external login and user logout.
- Refactored UsersController to manage user profile retrieval and updates.
@taiphanvan2k3 taiphanvan2k3 self-assigned this Nov 22, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 22, 2025

Walkthrough

Refactors external login terminology from "LoginExternal" to "ExternalLogin" across multiple layers, adds user profile management endpoints (GetProfile, UpdateProfile), implements logout with refresh token revocation, removes centralized authorization filters, and updates API routing accordingly.

Changes

Cohort / File(s) Summary
External Login Refactoring
src/Application/Common/Models/UserInfo.cs, src/Application/Features/Auth/ExternalLogin/ExternalLoginCommand.cs, src/Application/Features/Auth/ExternalLogin/ExternalLoginCommandHandler.cs, src/Application/Features/Auth/ExternalLogin/ExternalLoginCommandValidator.cs, src/Application/Features/Auth/ExternalLogin/ExternalLoginlResponse.cs
Renames command/handler/validator/response classes from LoginExternal* to ExternalLogin* pattern; updates namespace from LoginExternal to ExternalLogin; aligns all type references and XML documentation comments.
External Login Info Model
src/Application/Features/Auth/ExternalLogin/ExternalLoginInfo.cs
Adds new public sealed record defining external login provider metadata with properties for Provider, ProviderKey, DisplayName, AvatarUrl, and CreatedAt.
Logout Implementation
src/Application/Features/Auth/Logout/LogoutCommandHandler.cs
Injects IRefreshTokenRepository, validates token existence and revocation status, implements token revocation on logout, and returns appropriate authorization errors for invalid tokens.
Get Profile Query
src/Application/Features/User/GetProfile/GetProfileQuery.cs, src/Application/Features/User/GetProfile/GetProfileQueryHandler.cs, src/Application/Features/User/GetProfile/GetUserProfileResponse.cs
Adds new query/handler/response for retrieving current user profile including user identity data, roles, timestamps, avatar URL, and mapped external login accounts.
Update Profile Command
src/Application/Features/User/UpdateProfile/UpdateProfileCommand.cs, src/Application/Features/User/UpdateProfile/UpdateProfileCommandHandler.cs, src/Application/Features/User/UpdateProfile/UpdateProfileCommandValidator.cs, src/Application/Features/User/UpdateProfile/UpdateProfileResponse.cs
Adds new command/handler/validator/response for updating user profile fields (FullName, AvatarUrl) with validation constraints and UpdatedAt timestamp management.
API Controller Updates
src/Web.Api/Controllers/V1/AuthController.cs, src/Web.Api/Controllers/V1/UsersController.cs
Updates AuthController with renamed ExternalLogin endpoint and new Logout endpoint; replaces GetUsers with GetProfile and AddUpdateProfile actions in UsersController; updates authorization from role-based to general.
Authorization Filter Removal
src/Web.Api/Filters/AuthorizationLoggingFilter.cs, src/Web.Api/Filters/CustomAuthorizeFilter.cs, src/Web.Api/Program.cs
Deletes AuthorizationLoggingFilter and CustomAuthorizeFilter classes; removes their global registration from dependency injection.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

  • LoginExternal → ExternalLogin renaming: Verify all type references, usings, and response generics are consistently updated across command, handler, validator, and response files.
  • New user profile features: Confirm GetProfile and UpdateProfile handlers correctly validate authentication, fetch user data, and map external logins.
  • Logout token revocation logic: Validate that refresh token lookup, revocation status checks, and expiration validation are correctly implemented.
  • Authorization filter removal: Ensure custom authorization response handling is not required elsewhere or handled by framework defaults.
  • API route/controller changes: Confirm all endpoint routes, response types, and authorization attributes align with new structure.

Possibly related PRs

Poem

🐰 Hoppity hop, the names take flight,
LoginExternal becomes ExternalLogin—what a delight!
New profiles bloom and tokens revoke with care,
Old filters fade like morning air!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately reflects the main changes: implementing external login functionality and user profile management features, which are the primary objectives throughout the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/implement-profile-endpoint

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

Copy link
Copy Markdown
Contributor

@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: 2

🧹 Nitpick comments (8)
src/Application/Features/User/UpdateProfile/UpdateProfileCommand.cs (1)

6-20: Clarify null/empty semantics for profile fields

FullName and AvatarUrl are nullable, which is good for partial updates, but it’d be useful to ensure the handler clearly distinguishes:

  • null → “leave as is”
  • non-null (including empty string) → “update/clear value”

Just confirm the handler follows this contract and that it’s documented for clients so they don’t accidentally wipe values.

src/Application/Features/Auth/ExternalLogin/ExternalLoginCommandHandler.cs (2)

22-175: ExternalLogin rename and flow look consistent; validate repository/unit-of-work assumptions

The handler’s rename to ExternalLoginCommandHandler and switch to Result<ExternalLoginResponse> is consistent across all branches (NotFound, Deactivated, IdentityFailed, and success paths), and the three scenarios (existing external login, existing user by email, new user) read logically.

A couple of points to double‑check rather than change blindly here:

  • The code assumes request.Email, request.FirstName, request.LastName, and request.ProviderKey are non‑null/valid. Please confirm ExternalLoginCommandValidator enforces that so this handler never gets bad inputs.
  • User creation + external login + refresh token creation appear to rely on a shared unit of work / context. You call unitOfWork.SaveChangesAsync for external login changes but not after adding refresh tokens; if you also have a pipeline behavior that saves at the end of a request, just ensure you’re not inadvertently doing multiple commits or leaving refresh tokens unpersisted when no outer save runs.

If those assumptions hold, the logic itself looks sound.


180-227: Name splitting in GenerateTokenResponse is simplistic; consider future‑proofing

GenerateTokenResponse derives first/last names by splitting user.FullName on the first space. This works for many Western‑style names but degrades for multi‑word or non‑Western names and loses the original external provider’s first/last breakdown.

Not urgent, but for better i18n/name handling you might later:

  • Store first/last names explicitly on the user (or reuse TblUserExternalLogin’s first/last when available), and
  • Treat FullName as a display-only field not used to reconstruct structured name parts.

For now, the existing behavior is consistent with the rest of the model.

src/Web.Api/Program.cs (1)

25-30: Confirm removal of global authorization filter is intentional

Switching to plain AddControllers() (without the former global CustomAuthorizeFilter) changes how authorization failures are shaped/handled. If the goal is to rely purely on [Authorize] attributes + JWT middleware and default 401/403 responses, this is fine—just make sure:

  • All sensitive endpoints have proper [Authorize] attributes, and
  • Any custom error/response formatting previously in the filter is now covered elsewhere (e.g., middleware) if still required.
src/Application/Features/User/UpdateProfile/UpdateProfileCommandHandler.cs (1)

42-42: Consider conditional UpdatedAt.

UpdatedAt is set unconditionally even when no fields change. Consider setting it only when FullName or AvatarUrl is actually modified.

src/Application/Features/User/GetProfile/GetUserProfileResponse.cs (1)

30-30: Consider using IReadOnlyList for immutable collection properties.

The List<string> and List<ExternalLoginInfo> properties expose mutable collections, allowing callers to modify the lists after construction. For DTOs with init-only setters, using IReadOnlyList<T> better conveys immutability and prevents unintended modifications.

Apply this diff to use immutable collection types:

-    public required List<string> Roles { get; init; }
+    public required IReadOnlyList<string> Roles { get; init; }
-    public required List<ExternalLoginInfo> ExternalLogins { get; init; }
+    public required IReadOnlyList<ExternalLoginInfo> ExternalLogins { get; init; }

Also applies to: 45-45

src/Application/Features/User/GetProfile/GetProfileQueryHandler.cs (1)

46-55: Consider providing a fallback for null DisplayName.

The DisplayName construction at lines 50-52 returns null when both FirstName and LastName are null. This could result in a null DisplayName in the response, which may lead to poor UX when displaying external login information.

Consider adding a fallback to improve the user experience:

                 DisplayName = el.FirstName != null && el.LastName != null
                     ? $"{el.FirstName} {el.LastName}"
-                    : el.FirstName ?? el.LastName,
+                    : el.FirstName ?? el.LastName ?? el.Provider.ToString(),

This ensures that at minimum, the provider name is shown when personal names are unavailable.

src/Web.Api/Controllers/V1/AuthController.cs (1)

110-117: Consider adding [Authorize] attribute to the Logout endpoint.

The logout endpoint is not protected by the [Authorize] attribute. While the LogoutCommand likely contains a refresh token for validation, it's a best practice to require authentication for logout operations to prevent potential abuse (e.g., token enumeration attacks).

Apply this diff to add authorization:

     /// <summary>
     /// Logout user by invalidating refresh token
     /// </summary>
     /// <param name="command">Logout command with refresh token</param>
     /// <returns>Success response</returns>
     [HttpPost("logout")]
+    [Authorize]
     [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
     [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status400BadRequest)]
     public async Task<IActionResult> Logout([FromBody] LogoutCommand command)

If there's a specific reason to keep it unauthenticated (e.g., allowing logout with only a refresh token without an access token), please document this design decision.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b20dd7f and 09a6870.

📒 Files selected for processing (19)
  • src/Application/Common/Models/UserInfo.cs (1 hunks)
  • src/Application/Features/Auth/ExternalLogin/ExternalLoginCommand.cs (1 hunks)
  • src/Application/Features/Auth/ExternalLogin/ExternalLoginCommandHandler.cs (7 hunks)
  • src/Application/Features/Auth/ExternalLogin/ExternalLoginCommandValidator.cs (1 hunks)
  • src/Application/Features/Auth/ExternalLogin/ExternalLoginInfo.cs (1 hunks)
  • src/Application/Features/Auth/ExternalLogin/ExternalLoginlResponse.cs (1 hunks)
  • src/Application/Features/Auth/Logout/LogoutCommandHandler.cs (1 hunks)
  • src/Application/Features/User/GetProfile/GetProfileQuery.cs (1 hunks)
  • src/Application/Features/User/GetProfile/GetProfileQueryHandler.cs (1 hunks)
  • src/Application/Features/User/GetProfile/GetUserProfileResponse.cs (1 hunks)
  • src/Application/Features/User/UpdateProfile/UpdateProfileCommand.cs (1 hunks)
  • src/Application/Features/User/UpdateProfile/UpdateProfileCommandHandler.cs (1 hunks)
  • src/Application/Features/User/UpdateProfile/UpdateProfileCommandValidator.cs (1 hunks)
  • src/Application/Features/User/UpdateProfile/UpdateProfileResponse.cs (1 hunks)
  • src/Web.Api/Controllers/V1/AuthController.cs (3 hunks)
  • src/Web.Api/Controllers/V1/UsersController.cs (2 hunks)
  • src/Web.Api/Filters/AuthorizationLoggingFilter.cs (0 hunks)
  • src/Web.Api/Filters/CustomAuthorizeFilter.cs (0 hunks)
  • src/Web.Api/Program.cs (1 hunks)
💤 Files with no reviewable changes (2)
  • src/Web.Api/Filters/CustomAuthorizeFilter.cs
  • src/Web.Api/Filters/AuthorizationLoggingFilter.cs
🧰 Additional context used
🧬 Code graph analysis (4)
src/Application/Features/User/UpdateProfile/UpdateProfileCommandHandler.cs (1)
src/Application/Features/User/UpdateProfile/UpdateProfileResponse.cs (1)
  • UpdateProfileResponse (3-24)
src/Application/Features/User/GetProfile/GetProfileQuery.cs (1)
src/Application/Features/User/GetProfile/GetUserProfileResponse.cs (1)
  • GetUserProfileResponse (5-46)
src/Application/Features/User/GetProfile/GetProfileQueryHandler.cs (1)
src/Application/Features/User/GetProfile/GetUserProfileResponse.cs (1)
  • GetUserProfileResponse (5-46)
src/Application/Features/User/UpdateProfile/UpdateProfileCommand.cs (1)
src/Application/Features/User/UpdateProfile/UpdateProfileResponse.cs (1)
  • UpdateProfileResponse (3-24)
⏰ 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: build-and-deploy
🔇 Additional comments (15)
src/Application/Common/Models/UserInfo.cs (1)

3-6: Doc comment rename aligns with new ExternalLogin terminology

The summary now correctly mentions ExternalLogin alongside other auth responses; no behavioral impact and keeps docs consistent with the new feature naming.

src/Application/Features/Auth/ExternalLogin/ExternalLoginlResponse.cs (1)

3-39: Response type rename is consistent and non‑breaking at payload level

Renaming to ExternalLoginResponse in the Application.Features.Auth.ExternalLogin namespace keeps the JSON shape (tokens, user info, flags, provider) intact while aligning with the new command/handler naming. No issues from an API contract perspective as long as clients are updated to the new type name in strongly‑typed consumers.

src/Application/Features/Auth/ExternalLogin/ExternalLoginInfo.cs (1)

1-32: LGTM!

The record structure is well-designed with appropriate immutability, clear documentation, and correct use of required/optional properties.

src/Application/Features/User/GetProfile/GetProfileQuery.cs (1)

1-12: LGTM!

The query structure correctly follows CQRS pattern for retrieving the current user's profile, delegating user identification to ICurrentUserService.

src/Application/Features/User/UpdateProfile/UpdateProfileCommandValidator.cs (1)

1-30: LGTM!

The validation logic is correct and appropriate. The URL validation helper properly handles null/empty values and validates absolute HTTP/HTTPS URLs.

src/Application/Features/User/UpdateProfile/UpdateProfileResponse.cs (1)

1-24: LGTM!

The response DTO structure is well-defined with appropriate required/optional properties and clear documentation.

src/Application/Features/Auth/ExternalLogin/ExternalLoginCommandValidator.cs (1)

1-47: LGTM!

The rename from LoginExternalCommandValidator to ExternalLoginCommandValidator improves naming consistency. Validation rules remain correct and comprehensive.

src/Application/Features/Auth/ExternalLogin/ExternalLoginCommand.cs (1)

1-47: LGTM!

The rename from LoginExternalCommand to ExternalLoginCommand improves naming consistency across the authentication feature.

src/Application/Features/Auth/Logout/LogoutCommandHandler.cs (1)

10-28: LGTM!

The logout implementation correctly validates the refresh token (existence, revocation status, expiration) and revokes it with an appropriate reason. The error handling is secure and prevents replay attacks.

src/Web.Api/Controllers/V1/UsersController.cs (3)

23-31: LGTM! Clean implementation of GetProfile endpoint.

The endpoint correctly retrieves the current user's profile by delegating to the mediator with GetProfileQuery. The use of a parameterless query is appropriate since the handler will retrieve the current user's ID from ICurrentUserService.


38-47: LGTM! UpdateProfile endpoint follows standard patterns.

The endpoint correctly accepts the update command from the request body and delegates to the mediator for processing.


16-16: No security issues identified—review comment based on incorrect premise.

The review assumes a "removed GetUsers endpoint" exists, but the current UsersController contains only two endpoints: GetProfile and UpdateProfile. Both are self-service profile operations appropriately protected by class-level [Authorize]. No role-based authorization was needed for these endpoints. The import of GetUsersQuery on line 3 is unused and orphaned, not evidence of a removed endpoint. The authorization model aligns correctly with the controller's current security requirements.

Likely an incorrect or invalid review comment.

src/Application/Features/User/GetProfile/GetProfileQueryHandler.cs (2)

21-26: LGTM! Proper authentication check.

The handler correctly validates that the user is authenticated before proceeding, returning an appropriate unauthorized error if not.


28-33: LGTM! Proper user existence validation.

The handler correctly checks if the user exists and returns a not found error if the user doesn't exist in the repository.

src/Web.Api/Controllers/V1/AuthController.cs (1)

57-65: Verify that the route change is acceptable as a breaking change.

The external login endpoint route changed from login-external to external-login. This is a breaking API change that will affect existing clients. Ensure that:

  • This change is intentional and documented
  • Clients are notified of the update
  • Consider if versioning or a deprecation period is needed

@taiphanvan2k3 taiphanvan2k3 merged commit f2897b5 into main Nov 22, 2025
3 checks passed
@taiphanvan2k3 taiphanvan2k3 deleted the feat/implement-profile-endpoint branch November 22, 2025 15:57
taiphanvan2k3 added a commit that referenced this pull request Dec 22, 2025
…e-endpoint

feat: Implement external login functionality and user profile management
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.

1 participant