< Summary

Information
Class: ProjectTemplate.Web.Controllers.AccountController
Assembly: ProjectTemplate.Web
File(s): /home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Controllers/AccountController.cs
Line coverage
93%
Covered lines: 31
Uncovered lines: 2
Coverable lines: 33
Total lines: 105
Line coverage: 93.9%
Branch coverage
60%
Covered branches: 6
Total branches: 10
Branch coverage: 60%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Login()75%44100%
Logout()50%4483.33%
AccessDenied()100%11100%

File(s)

/home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/Controllers/AccountController.cs

#LineLine coverage
 1using Microsoft.AspNetCore.Authentication;
 2using Microsoft.AspNetCore.Authentication.Cookies;
 3using Microsoft.AspNetCore.Authorization;
 4using Microsoft.AspNetCore.Mvc;
 5using ProjectTemplate.Web.Models;
 6
 7namespace ProjectTemplate.Web.Controllers;
 8
 9/// <summary>
 10/// Provides actions for user authentication, including login, logout, and access denied handling.
 11/// </summary>
 12/// <remarks>This controller exposes endpoints for user authentication workflows. It supports external
 13/// authentication providers and enforces security best practices such as requiring local return URLs to prevent open
 14/// redirect vulnerabilities. Actions are decorated with appropriate authorization and anti-forgery attributes as
 15/// needed.</remarks>
 16/// <param name="schemeProvider">The authentication scheme provider used to retrieve available external authentication s
 17/// Cannot be null.</param>
 818public class AccountController(IAuthenticationSchemeProvider schemeProvider) : Controller
 19{
 820    private readonly IAuthenticationSchemeProvider _schemeProvider = schemeProvider;
 21
 22    /// <summary>
 23    /// Displays the login page and provides available external authentication providers.
 24    /// </summary>
 25    /// <remarks>This action is accessible without authentication. Only local return URLs are permitted to
 26    /// prevent open redirect vulnerabilities.</remarks>
 27    /// <param name="returnUrl">The URL to redirect to after a successful login. If null or empty, the user is redirecte
 28    /// root. Must be a local URL.</param>
 29    /// <returns>A view result that renders the login page with available external authentication providers, or a bad re
 30    /// result if the return URL is not local.</returns>
 31    [HttpGet("/Account/Login")]
 32    [AllowAnonymous]
 33    public async Task<IActionResult> Login(string? returnUrl = null)
 34    {
 435        string safeReturnUrl = string.IsNullOrWhiteSpace(returnUrl) ? "/" : returnUrl;
 36
 437        if (!Url.IsLocalUrl(safeReturnUrl))
 38        {
 239            return BadRequest();
 40        }
 41
 242        IEnumerable<AuthenticationScheme> schemes = await _schemeProvider.GetAllSchemesAsync();
 43
 244        AccountLoginViewModel model = new()
 245        {
 246            ReturnUrl = safeReturnUrl,
 247            ExternalProviders = schemes
 448                .Where(scheme => !string.Equals(
 449                    scheme.Name,
 450                    CookieAuthenticationDefaults.AuthenticationScheme,
 451                    StringComparison.Ordinal))
 252                .Where(scheme => !string.IsNullOrWhiteSpace(scheme.DisplayName))
 253                .Select(scheme => new ExternalAuthenticationProviderViewModel
 254                {
 255                    Scheme = scheme.Name,
 256                    DisplayName = scheme.DisplayName ?? scheme.Name
 257                })
 058                .OrderBy(provider => provider.DisplayName)
 259                .ToList()
 260        };
 61
 262        return View(model);
 463    }
 64
 65    /// <summary>
 66    /// Signs out the current user and redirects to the specified return URL.
 67    /// </summary>
 68    /// <remarks>This action requires an authenticated user, a valid anti-forgery token, and only accepts local URLs for
 69    /// to help prevent cross-site request forgery and open redirect vulnerabilities.</remarks>
 70    /// <param name="returnUrl">The URL to redirect to after sign-out. If null or empty, defaults to the application's r
 71    /// local URL.</param>
 72    /// <returns>A redirect result to the specified local return URL if sign-out is successful; otherwise, a bad request
 73    /// if the return URL is not local.</returns>
 74    [Authorize]
 75    [HttpPost("/Account/Logout")]
 76    [ValidateAntiForgeryToken]
 77    public async Task<IActionResult> Logout(string? returnUrl = null)
 78    {
 279        string safeReturnUrl = string.IsNullOrWhiteSpace(returnUrl) ? "/" : returnUrl;
 80
 281        if (!Url.IsLocalUrl(safeReturnUrl))
 82        {
 083            return BadRequest();
 84        }
 85
 286        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
 87
 288        return LocalRedirect(safeReturnUrl);
 289    }
 90
 91    /// <summary>
 92    /// Handles requests to the access denied page and returns a view indicating that the user does not have permission
 93    /// to access the requested resource.
 94    /// </summary>
 95    /// <remarks>This action is accessible to all users, including unauthenticated users. The response status
 96    /// code is set to 403 to indicate forbidden access.</remarks>
 97    /// <returns>A view result that displays the access denied page with a 403 Forbidden status code.</returns>
 98    [HttpGet("/Account/AccessDenied")]
 99    [AllowAnonymous]
 100    public IActionResult AccessDenied()
 101    {
 2102        Response.StatusCode = StatusCodes.Status403Forbidden;
 2103        return View();
 104    }
 105}