GitHub Codespaces Gotchas with ASP.NET Core OAuth
Some gotchas to account for when debugging an ASP.NET Core app using OAuth with GitHub Codespaces
This week GitHub Codespaces was made generally available for Teams and
Enterprise, and coupled with the release of the ability to open any repository
in Visual Studio Code in a web browser just by pressing .
, I thought
I’d give it a try with some existing projects. In the process I hit a few
gotchas that took me a few hours to get to the bottom of. This post goes through
some of those and how to resolve them.
One of the projects I decided I’d try it out on is the sample application I recently published for integration testing with ASP.NET Core 6 Minimal APIs. This sample uses GitHub OAuth for authentication and is relatively simple in its setup, with a mostly out-of-the box configuration.
Once I’d got the basic Codespaces setup added to the repository (which took me a bit of time to bed-in and sort out), I found that whenever I tried to log in to the sample application using the Codespaces preview URL when I ran it in the debugger, I’d be greeted with an HTTP 405 error (Method Not Allowed).

However, the error page wasn’t coming from my app. The Server
HTTP response
header identified it as NGINX, and turning up the default log level to Trace
didn’t show any requests hitting the app in the debugger for the /signin
path
the form POST was sent to.
Weird. Confused, I posted a question on the GitHub Support Community and left it for the rest of the day.
This weekend, I then went through some other repos to add Codespaces support to. The setup was pretty much the same as with the first one I tried, yet any HTTP POST resources those apps had in them worked fine.
Curious. What is it about this particular TodoApp that means that it gets an
HTTP 405 when trying to post to the /signin
path?
After quite a lot of trial and error, I found that /signin
appears to be
some sort of reserved path that the GitHub Codespaces internals use. I haven’t
been able to find any documentation about this anywhere about paths that you
cannot use, so I guess it’s some sort of the internal management functionality
for their devs or something like that.
Once I changed the path in the app to /sign-in
, I could hit the GitHub.com
OAuth page to grant permission to my app and get redirected back to complete
the OAuth dance to get logged in.
The next hurdle was that my OAuth callback path would 404. Seems that the NGINX
setup in Codespaces seems to also trap paths beginning with /signin
. As the
out-of-the-box callback path was /signin-github
, I needed to change that too.
With that sorted out, I had a third and final problem. GitHub would reject the
callback URL as invalid. Inspecting the logs in the debugger, I found that the
redirect URL being sent to GitHub was starting with https://localhost/
. For
some reason the hostname for the Codespaces preview wasn’t being used by the app.
At first I thought I’d just need to explicitly configure the ASP.NET Core proxy
configuration for Linux, but that didn’t seem to fix it either. Finally I
resorted to reading the source code for the ForwardedHeadersMiddleware
class
in the ASP.NET Core repo and then I found the answer.
By default, the X-Forwarded-Host
HTTP request header is not inspected by the
middleware to update the value of the Host
property on the HttpRequest
.
Once I updated the configuration for the forwarded headers middleware, the OAuth
login flow for the application started to work.
I also subsequently found that the documentation on docs.microsoft.com is a bit
out of date and that there has been a config value built-in since ASP.NET
Core 3.0 that will automatically configure the middleware for you if you set the
ASPNETCORE_FORWARDEDHEADERS_ENABLED
setting to true
.
This meant I could simplify the code to just two lines in the Program.cs
file
to add on the additional flag for the middleware:
if (string.Equals(
builder.Configuration["CODESPACES"],
"true",
StringComparison.OrdinalIgnoreCase))
{
builder.Services.Configure<ForwardedHeadersOptions>(
options => options.ForwardedHeaders |= ForwardedHeaders.XForwardedHost);
}
With that in place, I could just set up the forwarded headers from the VS Code launch configuration:
{
"env": {
"ASPNETCORE_FORWARDEDHEADERS_ENABLED": "${env:CODESPACES}"
}
}
Now the app works correctly when running in GitHub Codespaces! 🎉
You can see the full set of changes I made to get things working for the sample app when running in Codespaces in this Pull Request.
This was quite the head-scratcher to work out and fix, but I’m glad that I’ve gotten things working in the end to give a nicer experience to people looking at my sample repo with Codespaces without neeeding to clone it locally.
If you’ve stumbled across the blog post while trying to get this working yourself, I hope you found reading this useful!