Receiving 404 not found rather 401 UnAuthorized

Dear Team,

I am using Identity Core with SS Ver 8.2.2 and trying to secure an endpoint below is the code of Apphost(s), program.cs and other relevant code FYR

public class AppHost : AppHostBase, IHostingStartup
{
    public AppHost() : base("Ecomm.Consumer", typeof(Ecomm.Consumer.ServiceInterface.MyServices).Assembly)
    {

    }
    public void Configure(IWebHostBuilder builder) => builder
        .ConfigureServices(services => {
            // Configure ASP.NET Core IOC Dependencies
        })
        .ConfigureAppHost(appHost =>
        {
        });

    public override void Configure()
    {
        // Configure ServiceStack, Run custom logic after ASP.NET Core Startup
        SetConfig(new HostConfig
        {
            DebugMode  = false,
        });
    
}

ConfigureAuth.cs

public class ConfigureAuth : IHostingStartup
  {
      public void Configure(IWebHostBuilder builder) => builder
     .ConfigureServices((context, services) =>
     {
         services.AddPlugin(new AuthFeature(IdentityAuth.For<ApplicationUser>(options => {
             options.CredentialsAuth();
             options.SessionFactory = () => new CustomUserSession();
             options.IncludeRegisterService = true;
             options.AdminUsersFeature();
         })));
     });
  }

  public class CustomUserSession : AuthUserSession
  {
      public override void PopulateFromClaims(IRequest httpReq, ClaimsPrincipal principal)
      {
          DisplayName = principal.FindFirstValue(JwtClaimTypes.NickName);
      }
  }

  public class AdditionalUserClaimsPrincipalFactory(
      UserManager<ApplicationUser> userManager,
      RoleManager<IdentityRole> roleManager,
      IOptions<IdentityOptions> optionsAccessor)
      : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>(userManager, roleManager, optionsAccessor)
  {
      public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
      {
          var principal = await base.CreateAsync(user);
          var identity = (ClaimsIdentity)principal.Identity!;

          var claims = new List<Claim>();
          if (user.DisplayName != null)
              claims.Add(new Claim(JwtClaimTypes.NickName, user.DisplayName));

          identity.AddClaims(claims);
          return principal;
      }
  }

Request/ResponseDTO

[Route("/hello/{Name}")]
[Authorize(Roles ="Customer")]
public class Hello : IGet, IReturn<HelloResponse>
{
    public string? Name { get; set; }
}

public class HelloResponse
{
    public string? Result { get; set; }
}

Service

  public class MyServices : Service
  {

      public object Get(Hello request)
      {
          return new HelloResponse { Result = $"Hello, {request.Name}!" };
      }
  }

When I try to call the API at this location https://localhost:5001/hello/name using the freshly generated token then I am able to successfully land into the GET method mentioned above. However, when the token is expired I am receiving 404 Not Found error, rather I am expecting 401 UnAuthorized.

If possible please advise the right way to deal this issue

Update

I was trying to investigate further and noticed that when I am providing a valid token then the response headers are different and when it is saying 404 Not Found then the response headers are different. Below screenshots are FYR. Can I say that may be when the token is expired the request is not reaching to servicestack at all? Just my 2 cents

Successful Response

Failed Response

Regards,

By default Identity Auth typically redirects to the login page for 401 Unauthorized requests, unfortunately it also does this for APIs so we’ve added an extension method to override this default behavior and return a 401 response for API requests:

services.AddAuthentication(...)
    .AddIdentityCookies(options =>
    {
        options.DisableRedirectsForApis();
    });

Which of our Identity Auth templates did you start with?

Hi @mythz ,

Appreciate your response, but the suggested trick didn’t work actually. I am still being re-directed to 404 Not found.

services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
     options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.SaveToken = true;
    options.RequireHttpsMetadata = false;
    options.IncludeErrorDetails = true;
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuerSigningKey = true,
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ClockSkew = TimeSpan.Zero,
        ValidIssuer = builder.Configuration["JWT:ValidIssuer"],
        ValidAudience = builder.Configuration["JWT:ValidAudience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"])),
        
    };
})
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler<ApplicationUser>>(BasicAuthenticationHandler.Scheme, null)
.AddIdentityCookies(options => options.DisableRedirectsForApis()) ;

Actually what I am trying to achieve is that I want to return 401 UnAuthorized and in the response I want to return my custom response JSON message.

Due to Identity redirection, not able to catch the exception in any of the below hooks.

public override Task HandleResponseException(IRequest httpReq, IResponse httpRes, string operationName, Exception ex)
{
    return base.HandleResponseException(httpReq, httpRes, operationName, ex);
}
public override Task HandleShortCircuitedErrors(IRequest req, IResponse res, object requestDto)
{
    return base.HandleShortCircuitedErrors(req, res, requestDto);
}
public override Task HandleShortCircuitedErrors(IRequest req, IResponse res, object requestDto, HttpStatusCode statusCode, string statusDescription)
{
    return base.HandleShortCircuitedErrors(req, res, requestDto, statusCode, statusDescription);
}   
public override object OnAfterExecute(IRequest req, object requestDto, object response)
{
    return base.OnAfterExecute(req, requestDto, response);
}
public override void OnEndRequest(IRequest request = null)
{
    base.OnEndRequest(request);
}
public override Task<object> OnServiceException(IRequest httpReq, object request, Exception ex)
{
    return base.OnServiceException(httpReq, request, ex);  
}
public override void OnExceptionTypeFilter(Exception ex, ResponseStatus responseStatus)
{
    base.OnExceptionTypeFilter(ex, responseStatus);
}
public override object OnPostExecuteServiceFilter(IService service, object response, IRequest httpReq, IResponse httpRes)
{
    return base.OnPostExecuteServiceFilter(service, response, httpReq, httpRes);
}
public override Task OnUncaughtException(IRequest httpReq, IResponse httpRes, string operationName, Exception ex)
{
    return base.OnUncaughtException(httpReq, httpRes, operationName, ex);
}

I am not using any service stack template, just going through the docs and used asp.net 8 web api project default plain template which comes with VS 2022.

Can you suggest something …

Regards,

If this is an Identity Auth redirection, the request never reaches ServiceStack so you wont be able to catch or handle it within ServiceStack.

Have a look at the HTTP Response Headers for the location you’re being redirected to. As you’re not using a ServiceStack template the behavior would likely be due to your custom Identity Auth configuration, i.e. unrelated to ServiceStack. You can confirm by removing app.UseServiceStack(...) from your Program.cs.

@mythz appreciate your response.

To use nicely ASP.NET Core 8 project with latest ASP.NET Core Identity along with service stack and web API only project which template you would kindly recommend.

I will download it and try that template. Let me download and try this template

https://mvc.web-templates.io/

Really I am bit confused. I managed to work with SS Auth mechanism but this identity seems a new beast for me.

We don’t have a Web API + ServiceStack template since those technologies are substitutes for creating APIs and rarely used together.

But we have MVC, Razor Pages and Blazor (Vue,Server,WASM) with ServiceStack templates configured with Identity Auth on our Start Page:

https://servicestack.net/start

Sorry, I meant to ask was that my project is only API based and doesn’t require any html, razor etc. I have to develop only and only API using SS and looks like you do not have project related to API only ?

Otherwise kindly advise.

Checking this also https://servicestack.net/start

The issue of not having a UI is that the pages to manage the Identity Auth UI requires a UI, which are integrated in our MVC, Razor Pages and Blazor templates.

If you want ServiceStack + Identity Auth and don’t care about which UI to use I’d recommend either Blazor Vue (the lightest Blazor that only uses Static Rendering) or Razor Pages.

Also if you want to use ServiceStack with Identity Auth JWT, also checkout our Identity Auth JWT docs.

@mythz,

Appreciate your guidance in the right direction. I am trying templates mentioned here, will keep coming back to you

Yes the code snippet I have pasted in my original post is the effort of following this Identity Auth JWT Doc

As mentioned earlier. I need to create API project having only Swagger/Open API related UIs just for the sake of API documentation.

  1. I want to utilize Asp.NET Core 8
  2. I want to utilize NET Core Identity latest version following your recommendation here
  3. I want to utilize ServiceStack latest version available

I am following this link and building my POC. Things were working fine nicely. However the issue is that incase of expired token Identity is redirecting me to this URL https://localhost:5001/Account/Login?ReturnUrl=%2Fhello%2Fsometextstring

Since I am being re-directed to this URL and there is no razor/html or any other page in the project/solution to entertain this request that is why it is throwing me 404 Not Found error in postman.

Anyways I am trying to catch this error using Middleware feature will see.

Thanks and regards