Solved.
Facebook returned 400 bad request because it expects the redirect_uri
parameter to be identical in calls to both
https://www.facebook.com/dialog/oauth
and
https://graph.facebook.com/oauth/access_token
I am using a variation of the FacebookScopedClient class that is floating on the net.
It sets the value of redirect_uri from: context.Request.Url.OriginalString;
that string contains the port number, while the original url does not.
the solution is to either include the port number in the first call, remove it from the second, or not get the redirect_uri value from the Request.Url in the first place.
I went with the second option, using this:
if (context.Request.Url.IsDefaultPort)
{
rawUrl = rawUrl.Replace(":80", ""); //patch to remove port number.
}
Its probably not bullet proof since there may be an odd case with ":80" showing up elsewhere in the url, but its suficcient for my needs.