0

I'm working on implementing OAuth in an ASP.NET Core (using .NET 5, Razor Pages) application. I'm encountering an error, but I don't quite understand what it's telling me.

Background Info

I haven't gotten the OAuth implementation to work yet, so even if I am able to get it to work and resolve this error, I'm not convinced that I have everything set up perfectly. If you have any tips on things I could improve it would be greatly appreciated!

For reference, I've only been trying to work with OAuth for a few days, and I don't have any prior experience in authentication/authorization. I have read a few guides though, which I'll mention at the end of my post here.

I've included details on the following:

  • the exception message and the stack trace
  • my code
  • my thoughts
  • references

The Error

Exception: The oauth state was missing or invalid.

Stack Trace:

System.Exception: An error was encountered while handling the remote login.
 ---> System.Exception: The oauth state was missing or invalid.
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

My Code

  • I'm not sure how private the information relating to my company's implementation of OAuth is, so I've omitted details that could identify the company just to be safe.

Startup.cs ConfigureServices()

  • only including portion that relates to OAuth2
public void ConfigureServices(IServiceCollection services)
{
    // OAuth2 Authentication 

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = "MyCompanyAuthenticationScheme";
    }).AddCookie(options =>
    {
        options.Cookie.IsEssential = true;
    }).AddOAuth("MyCompanyAuthenticationScheme", options => 
    { 
        options.ClientId = Configuration["Company:ClientId"];
        options.ClientSecret = Configuration["Company:ClientSecret"];
        options.CallbackPath = new PathString("/Auth/Login");
        
        options.AuthorizationEndpoint = "...";
        options.TokenEndpoint = "...";
        options.UserInformationEndpoint = "...";

        // todo: are all three of these necessary?
        options.Scope.Add("loginAccountInfo");
        options.Scope.Add("contactInfo");
        options.Scope.Add("employeeInfo");

        options.SaveTokens = true;

        // todo: are the jsonKeys defined in the above Scopes? follow up when you have a better understanding of how these work, or if the MapJsonKey lines are even necessary
        options.ClaimActions.MapJsonKey(ClaimTypes.Name, "firstName");
        options.ClaimActions.MapJsonKey(ClaimTypes.Email, "emailAddress");
    });
}

Startup.cs Configure()

  • again only included relevant sections of code
  • the important piece is that I do include app.UseAuthentication()
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseRouting();

    app.UseAuthentication();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
        endpoints.MapControllers();
    });
}

Auth controller:

[AllowAnonymous]
[Route("[controller]/[action]")]
public class AuthController : Controller
{
    [HttpGet]
    public IActionResult Login(string returnUrl = "/")
    {
        if(!User.Identity.IsAuthenticated)
            return Challenge(new AuthenticationProperties() { RedirectUri = returnUrl});

        return Forbid();
    }
}

I think this is all of the relevant code relating to OAuth, but if you think I need to include more details let me know!

My Thoughts

  • I think the issue is something with my options.CallbackPath defined in Startup.cs ConfigureServices(), under services.AddOAuth().
    • I've seen some posts detailing that I don't need to set this variable, but in those posts it seems that they're implementing OAuth for a provider (such as Google, GitHub, Facebook, etc) that has a NuGet package in which options.CallbackPath is defined ahead of time.
    • I've also seen some information saying that options.CallbackPath is used internally by the OAuth middleware, and that it doesn't correspond to a controller action (which means I wouldn't need my Auth controller and its Login action). However, if I remove the line defining options.CallbackPath, I get another error saying that it isn't defined, leading me to believe that it is very necessary.

References

Ethan
  • 51
  • 4
  • As far as I know, the state is a code which is used to check if the server is the right server which we want to authenticated. I suggest you could firstly check the server returned response contains this stats code or not. If your identity provider doesn't return this stats to client. It will cause this error. Besides, normally, this stats check is required to improve your application's security. But if you feel your connection is safe, you could disable this check. – Brando Zhang Nov 16 '21 at 08:45

1 Answers1

1

options.CallbackPath = new PathString("/Auth/Login");

If you have a controller action bound to /Auth/Login, this should be the source of the issue.

The value of options.CallbackPath doesn't have to match any controller action. It is an internal route where the middleware receives the answer of the Identity provider. You can simply assign /callback as its value or any other value that doesn't match an existing route

  • Thanks for the tip! I tried this out and did actually get my program to continue past this error, which is great. Unfortunately I'm getting a 400 status code when I'm redirecting to the authorization endpoint, which I think is an issue with running the app on localhost, so I'll probably have to work with my organization more on this. Thank you! – Ethan Nov 19 '21 at 22:25
  • Awesome... I dont know why isn't this answer everywhere – ajzbrun May 23 '23 at 13:45