Implementation of Custom Token Authentication in ASP.NET Core 2.0 and Analysis of Common Errors

Nov 23, 2025 · Programming · 15 views · 7.8

Keywords: ASP.NET Core 2.0 | Custom Authentication | Token Authentication | AuthenticationHandler | Authentication Scheme

Abstract: This article provides an in-depth exploration of the "No authenticationScheme was specified" error encountered when implementing custom token authentication in ASP.NET Core 2.0 applications. By analyzing the core differences between authentication and authorization, the article demonstrates step-by-step how to properly configure AuthenticationHandler, define authentication scheme options, and apply authentication schemes in controllers. The article also compares different authentication configuration methods and provides complete code examples and best practice recommendations to help developers deeply understand ASP.NET Core security mechanisms.

Problem Background and Error Analysis

During ASP.NET Core 2.0 application development, many developers encounter a common error when attempting to implement custom authorization policies: "No authenticationScheme was specified, and there was no DefaultChallengeScheme found". This error typically occurs when developers try to use custom authorization policies without properly configuring authentication schemes.

From a technical perspective, the root cause of this error lies in ASP.NET Core 2.0's separation of authentication and authorization design. In earlier versions, the boundaries between authentication and authorization were relatively blurred, but in version 2.0, Microsoft clearly separated the two. Authentication is responsible for verifying user identity, while authorization determines whether users have permission to access specific resources.

Core Differences Between Authentication and Authorization

To understand this error, it's essential to first clarify the different roles of authentication and authorization in ASP.NET Core. Authentication is the process of verifying user identity, typically involving checking credentials such as tokens, cookies, or username/password combinations. Authorization, on the other hand, determines whether to allow access to specific resources based on the authenticated user's identity and permissions.

In the problem description, the developer attempted to implement token validation through a custom AuthorizationHandler, which essentially placed authentication logic incorrectly in the authorization layer. The correct approach should be to implement a dedicated AuthenticationHandler to handle authentication, then use standard authorization mechanisms.

Implementation of Custom Authentication Handler

Based on best practices, we need to create a custom authentication handler that inherits from AuthenticationHandler<TokenAuthenticationOptions>. This handler is responsible for extracting tokens from request headers and performing validation.

The core implementation of TokenAuthenticationHandler is as follows:

public class TokenAuthenticationHandler : AuthenticationHandler<TokenAuthenticationOptions>
{
    public IServiceProvider ServiceProvider { get; set; }

    public TokenAuthenticationHandler(IOptionsMonitor<TokenAuthenticationOptions> options, 
        ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, IServiceProvider serviceProvider) 
        : base(options, logger, encoder, clock)
    {
        ServiceProvider = serviceProvider;
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var headers = Request.Headers;
        var token = "X-Auth-Token".GetHeaderOrCookieValue(Request);

        if (string.IsNullOrEmpty(token))
        {
            return Task.FromResult(AuthenticateResult.Fail("Token is null"));
        }

        bool isValidToken = false; // Actual token validation logic should be implemented here

        if (!isValidToken)
        {
            return Task.FromResult(AuthenticateResult.Fail($"Balancer not authorize token : for token={token}"));
        }

        var claims = new[] { new Claim("token", token) };
        var identity = new ClaimsIdentity(claims, nameof(TokenAuthenticationHandler));
        var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), this.Scheme.Name);
        return Task.FromResult(AuthenticateResult.Success(ticket));
    }
}

Authentication Scheme Configuration

In the ConfigureServices method of Startup.cs, authentication services need to be properly configured:

#region Authentication
services.AddAuthentication(o =>
{
    o.DefaultScheme = SchemesNamesConst.TokenAuthenticationDefaultScheme;
})
.AddScheme<TokenAuthenticationOptions, TokenAuthenticationHandler>(SchemesNamesConst.TokenAuthenticationDefaultScheme, o => { });
#endregion

Additionally, authentication scheme constants need to be defined:

public static class SchemesNamesConst
{
    public const string TokenAuthenticationDefaultScheme = "TokenAuthenticationScheme";
}

Authentication Application at Controller Level

Apply authentication schemes on controllers or action methods:

[Authorize(AuthenticationSchemes = SchemesNamesConst.TokenAuthenticationDefaultScheme)]
public class MainController : BaseController
{
    // Controller method implementations
}

Alternative Solution Comparison

In addition to the custom authentication handler approach, developers can also consider using the built-in Cookie authentication scheme:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
        options =>
        {
            options.LoginPath = new PathString("/auth/login");
            options.AccessDeniedPath = new PathString("/auth/denied");
        });

This approach is more suitable for traditional web applications, but for API interfaces, custom token authentication is generally more appropriate.

Best Practice Recommendations

When implementing custom authentication, it's recommended to follow these best practices: ensure calling app.UseAuthentication() in the Startup.Configure method, properly handle asynchronous operations to avoid deadlocks, implement comprehensive error handling mechanisms, and consider token refresh and expiration strategies.

By correctly understanding the differences between authentication and authorization and adopting appropriate implementation approaches, developers can avoid common configuration errors and build secure and reliable ASP.NET Core applications.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.