26

Im making a website with an API, the API needs validation so the user only gets his own data. I have written the following middleware to validate the login.

public class ApiAuthenticationMiddleware
{
    private readonly RequestDelegate _next;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;

    public ApiAuthenticationMiddleware(RequestDelegate next,
        SignInManager<ApplicationUser> signInManager,
        UserManager<ApplicationUser> usermanager)
    {
        _next = next;
        _signInManager = signInManager;
        _userManager = usermanager;
    }

    public async  Task Invoke(HttpContext context)
    {
        if (!context.Request.Query.ContainsKey("password") || !context.Request.Query.ContainsKey("email"))
        {
            context.Response.StatusCode = 401; //UnAuthorized
            await context.Response.WriteAsync("Invalid User Key");
            return;
        }
        var email = context.Request.Query["email"];
        var password = context.Request.Query["password"];

        var result = await _signInManager.PasswordSignInAsync(email, password, false, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            await _next.Invoke(context);
        }
        else if (//some more checks)
            context.Response.StatusCode = 401; //UnAuthorized
            await context.Response.WriteAsync("Invalid User Key");
            return;
        }
    }
}

What I want is that a blank page or an error message like "Invalid User Key" is shown if the user has no valid login. However what happens at the moment is the home page is returned (because my middleware is called before usemvc and the return statement skips the controller request done by usemvc).

My relevant code in the configure method in startup.cs

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {

        app.UseIdentity();
        app.UseWhen(x => (x.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase)),
            builder =>
            {
                builder.UseMiddleware<ApiAuthenticationMiddleware>();
            });
        app.UseStaticFiles();
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

How can I make my middleware return a blank page with status code?

Update

After trying alot of things I found that when I remove:

 context.Response.StatusCode = 401; //UnAuthorized

It works as expected, the user gets the message given in:

await context.Response.WriteAsync("Invalid User Key"); 

Howerver I dont want the user to get a 200 statuscode on login fail but a 401 statuscode. But when using the statuscode line the user is redirected. So how can I send the statuscode without redirection?

13
  • Why not using Authorize attribute Commented Aug 15, 2017 at 19:57
  • Authorize assumes a user is logged in (via cookie if im correct) Im making an API for a mobile app so the app sends requests which needs to be from a valid user. Also authorize will redirect to the register page if not loggedin which is not the behaviour I want for my API. Commented Aug 15, 2017 at 19:58
  • I would take a look at the OpenId Middleware. This Middleware works with the Authorize Attribute Commented Aug 15, 2017 at 20:01
  • 2
    Passing credentials in every request sounds like a bad idea. I'm no expert, but should use cookie authentication or something like OAuth2 where a short lived key is passed instead. Have you tried setting the ContentType header in the response object? Commented Sep 6, 2017 at 18:53
  • 1
    What @BrandonK mentions is important too. Sending credentials repeatedly isn't a good idea. Also, sending sensitive information (e.g. a password) via a query string is a bad practice too. IIS by default logs the url, including query strings, so all of your users' passwords would be stored in plain text in a log file on your server. Commented Sep 7, 2017 at 21:38

3 Answers 3

25

Try to use .Clear() before setting the StatusCode .

            context.Response.Clear();
            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            await context.Response.WriteAsync("Unauthorized");
Sign up to request clarification or add additional context in comments.

Comments

6
+25

I have a hunch that app.UseIdentity() has default behavoir that when a 403/401 occurs, it will intercept the response and initiate a redirect. Try commenting it out and see if it works.

Alternate solution see: https://stackoverflow.com/a/43456151/3948882

Last comment: I strongly encourage not passing credentials for each request. I recommend using prebuilt auth providers and implement as your middleware. The Authorize attribute should be sufficient for requests made and can do what you are looking for. If you do want to return something special, you can override the response with middleware like the app.UseIdentity() middleware might be with redirecting.

Comments

2

You should also set ContentType. Something like this:

        context.Response.ContentType = "text/plain";
        context.Response.StatusCode = 401; //UnAuthorized
        await context.Response.WriteAsync("Invalid User Key");

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.