Authenticated Endpoint is Redirecting to Login Page

I am working in a legacy .net web application and have added Service Stack to /api. Whenever I call a Service Stack authenticated endpoint without authenticating (via a service client or postman) I am getting back the html login page from the app. How do I get Service Stack to return the appropriate 401 not authorized instead of redirecting to the login?

Hi @lucuma,

The HtmlRedirect is used by default for “text/html” MIME type responses. If you are communicating with the service programmatically using a serialization format for the message like JSON/XML etc, the redirect won’t be applied. The redirect location can also be controlled on the AuthFeature itself.

I am getting a redirect in postman to any route that is authenticated if I don’t send the token cookie. It returns the login page for the app even if I send the content-type header set to application/json. I actually don’t want to redirect I want to return 401 unauthorized or something. This is api only and that’s causing me some issues and don’t want a redirect here. I didn’t enable anything in the auth feature only applied the Authenticate attribute to the service.

You’ll want to use the Accept header for the request to specify the Content-Type response header the client accepts. The Content-Type on the request is specifying the content of the request itself. Alternatively you can use the .json suffix on a route or ?format=json query string. If none of these are specified, the server will assume text/html which I believe is still what is happening here.

Please feel free to provide full request + response payloads so make it easier to give suggestions/help.

This isn’t working the way I think it should. If I call this authenticate and it fails it redirects to the login page and the client throws an error b/c it isn’t receiving valid json. If it authenticates correctly it returns the correct AuthenticateResponse in json form. Can you provide some guidance on where I need to look in order that none of these service stack services redirect when the request is for json / authenticated. I’m not doing anything special. I am just using the standard c# client in this example and the same happens with postman.

// redirects to login page and throws an error b/c the client is expecting valid json and is receiving the login page's html
   isAuth = client.Send(new Authenticate()
		{
			UserName = "redacted",
			Password = "bad password",
			RememberMe = true,
			provider = "credentials"

		});

// does NOT redirect  to login page and returns a valid json object as expected 
   isAuth = client.Send(new Authenticate()
		{
			UserName = "redacted",
			Password = "good password",
			RememberMe = true,
			provider = "credentials"

		});

JSON API Responses should return JSON. Please provide the full HTTP Request/Response headers with any sensitive scrubbed out.

Are you doing anything that could be causing this, e.g. non standard Auth Provider impl?

I am using the c# client and this is the full code. The apphost is really basic, didn’t do much to it but have a custom credentials provider that essentially just returns a false in the authenticate for this test.

void  Main()
{
	var client = new JsonServiceClient("https://localhost:44317/api/");
	AuthenticateResponse isAuth;

	try
	{
		isAuth = client.Send(new Authenticate()
		{
			UserName = "redacted",
			Password = "badpassword",
			RememberMe = true,
			provider = "credentials"

		});

	} catch (Exception ex) {
		
	}

	
}

This is the error returned when it fails auth or if I try to call an authenticated endpoint without a token cookie/etc.

Type definitions should start with a ‘{’, expecting serialized type ‘AuthenticateResponse’, got string starting with:

<html ng-app=...

How did you configure to use /api in your AppHost?

Not the client code, I need to see the server response headers. Try using the CaptureHttp() API:

var client = new JsonServiceClient("https://localhost:44317/api/");
client.CaptureHttp(print:true);

It is basic:

public class ApiAppHost : ServiceStack.AppHostBase
{
    public IContainerProvider Provider { get; set; }
    public ApiAppHost(IContainerProvider container) : base("StuffApi", typeof(Infrastructure.Services.MobileService).Assembly)
    {
        Provider = container;
    }

    public override void Configure(Container container)
    {
        SetConfig(new HostConfig {
            AdminAuthSecret = "redacted",
            HandlerFactoryPath = "api",
            UseCamelCase = true,
            DebugMode = true
        });

        Plugins.Add(new OpenApiFeature
        {
            UseCamelCaseSchemaPropertyNames = true,
        });

        Container.Adapter = new Infrastructure.Config.AutofacIocAdapter(Provider);
        Container.RegisterAutoWired<Infrastructure.Services.BaseService>();

        Plugins.Add(new ValidationFeature());
        Plugins.Add(new SessionFeature());
        Plugins.Add(new AuthFeature(() => new JobBoardSession(),
            new IAuthProvider[] {
                new JwtAuthProvider(AppSettings) {
                    AuthKeyBase64= Parameters.ApiAuthKeyBase64,
                    UseTokenCookie = true,
                },
                new JobBoardCredentialsAuthProvider(AppSettings, Container), 
            }
        ));
    }
}

The HTTP Request/Response headers please.

I don’t have them via the c# client. Isn’t that something known on your side?

Here are the postman ones:

The issue you’re reporting is with the C# Client, this is now the 4th time i’m asking for the HTTP Request/Response headers.

If you can’t provide the output of:

var client = new JsonServiceClient("https://localhost:44317/api/");
client.CaptureHttp(print:true);

Provide a stand-alone repro on GitHub that repo’s the issue.

You can also access the headers using Fiddler.

Identical issue in postman and c# client but here is the log output. Server is returning text/html content type when it doesn’t authenticate as in this request or if there is an error like calling an authenticated endpoint without a token.

POST /api/json/reply/Authenticate HTTP/1.1
Host: localhost:44317
Accept: application/json
User-Agent: ServiceStackClient/6.02
Accept-Encoding: gzip,deflate
Content-Type: application/json

{"provider":"credentials","UserName":"redacted","Password":"badpass","RememberMe":true}

HTTP/1.1 200 OK
Cache-Control: private
Vary: Accept-Encoding
Server: Microsoft-IIS/10.0
X-AspNetMvc-Version: 5.2
X-Frame-Options: SAMEORIGIN
X-AspNet-Version: 4.0.30319
Set-Cookie: __RequestVerificationToken=aPfITq7SfNOx1wTiFLWzlkcpedDWO3OAM8QKiraR8plPqUnAGrC9MGRHiY7Am0aFuXDX7OAssLdL2UE4DWYbogRKQ5P2pMrkQuGvmuZC13c1; path=/; HttpOnly
X-MiniProfiler-Ids: ["a9779638-f1ca-431b-9895-587a97ccce37","65b816b4-caff-45bc-966f-1e9e308d8931","66062c57-67e7-41b0-a31e-a8966d21af00","4c1681e6-9ada-481c-8aff-5a6d0614cd73","8d11c27f-7024-465d-8e2e-c92a21ed453d","7b496059-457a-4574-918e-2e35567b334c","9b5d92cc-24ac-4147-b4a0-d6ca114b7eae","355cf14d-1373-4e78-988b-8add83e45e29","bf5a969a-890f-42a5-9d62-92800f4720ae","b74f5dc8-df85-4457-962c-54d5559c05ce","8fe142ed-049d-4344-b042-c980026ad8e4","59d69279-55cd-49a3-9dcb-23b2848eba05","9d2fd536-28e9-4926-a7d5-7a618d8fd728","c75bfad7-b342-488b-acdc-8aefae54ce05","35490dee-8d97-4240-80ba-6176f1ecf4af","0c00876b-71ce-422d-9672-0135ac6c8062","47071e7b-0897-4014-b5f9-1168223a04e7","97c2ad8e-1a9c-4ac7-9192-c68e584a6d8d","dbffd9b7-7e69-4017-bfa7-80d3c419a617","ec6d4a01-6890-42db-b766-87f4f40db7ae","84d5a924-6e99-4e01-85cc-37f0ff21b9f1"]
X-SourceFiles: =?UTF-8?B?RDpccHJvamVjdHNcZ2l0XEpvYkJvYXJkXEpvYkJvYXJkLldlYlxBY2NvdW50XExvZ2lu?=
X-Powered-By: ASP.NET
Date: Thu, 17 Mar 2022 04:00:51 GMT
Content-Type: text/html; charset=utf-8


<!DOCTYPE html>
<html ng-app="app">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Job Board</title>
    <link href="/Content/themes/base/css?v=" rel="stylesheet"/>

 html from login page removed

This doesn’t have the Powered-By ServiceStack response, so it looks like it’s being hijacked by ASP .NET. Can’t tell what’s doing that from here. Is this part of a larger ASP .NET MVC App or something?

Definitely part of a larger old legacy aspnet app. Web.config has the recommended updates from the docs with the handlers but coded to a path:
ie:
<add path="api" type="ServiceStack.HttpHandlerFactory, ServiceStack" verb="*" />

I know that you can’t troubleshoot the aspnet app but is there something I can do on the service stack side to troubleshoot this further?

All I can recommend is to disable any existing Auth, e.g. enable anonymous authentication in IIS

and disable in Web.config:

<system.web>
    <authentication mode="None" />
</system.web>

It was already set to none. :frowning:

Also ensure Anonymous auth is enabled in IIS.

This post provides some examples of disabling FormsAuthenticationModule

Thanks! I tracked it down. 8+ year old legacy app. The line about LoginPath was hijacking it.

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    // hijacking the auth commented out           
    //LoginPath = new PathString("/Account/Login"),
    Provider = new CookieAuthenticationProvider
    {
        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
            validateInterval: TimeSpan.FromMinutes(30),
            regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
    }
});        
1 Like