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.
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))
}
});