< 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: 104
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 a valid anti-forgery token and only accepts local URLs for redirection
 69    /// to help prevent 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    [HttpPost("/Account/Logout")]
 75    [ValidateAntiForgeryToken]
 76    public async Task<IActionResult> Logout(string? returnUrl = null)
 77    {
 278        string safeReturnUrl = string.IsNullOrWhiteSpace(returnUrl) ? "/" : returnUrl;
 79
 280        if (!Url.IsLocalUrl(safeReturnUrl))
 81        {
 082            return BadRequest();
 83        }
 84
 285        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
 86
 287        return LocalRedirect(safeReturnUrl);
 288    }
 89
 90    /// <summary>
 91    /// Handles requests to the access denied page and returns a view indicating that the user does not have permission
 92    /// to access the requested resource.
 93    /// </summary>
 94    /// <remarks>This action is accessible to all users, including unauthenticated users. The response status
 95    /// code is set to 403 to indicate forbidden access.</remarks>
 96    /// <returns>A view result that displays the access denied page with a 403 Forbidden status code.</returns>
 97    [HttpGet("/Account/AccessDenied")]
 98    [AllowAnonymous]
 99    public IActionResult AccessDenied()
 100    {
 2101        Response.StatusCode = StatusCodes.Status403Forbidden;
 2102        return View();
 103    }
 104}