我有一个使用 C# .NET 后端的前端 (JavaScript) 应用程序。身份验证是通过 OAuth (Oracle IDCS) 实现的。它运行良好,但现在后端应用程序位于 API 网关/WAF(可通过互联网访问)后面。假设公共主机是
public-host.com
。网关将公共地址转换为我的后端应用程序正在接收请求的本地主机(因此我的后端“认为”它托管在与实际不同的 URL 下)。假设私人主机是 private-host.io
。现在,后端在调用 Oracle 时使用了错误的 redirect_uri
OAuth 参数(https://idcs-xxx.identity.oraclecloud.com/oauth2/v1/authorize?client_id=...&scope=...&...&redirect_uri=http://private-host.io/authorization_code/callback&...
- 没有 url 编码的示例)。在公开场合是 https
,但在本地/内部是 http
。
我尝试通过以下方式解决此问题:
services
.AddAuthentication(options =>
{
/*...*/
})
.AddOAuth("Oracle", options =>
{
options.Events = new OAuthEvents
{
OnRedirectToAuthorizationEndpoint = context =>
{
// Replace the `redirect_uri` query string parameter found in `context.RedirectUri` with the public one
// so replace "http://internal-host.io/" with "https://public-host.com/" when redirecting to Oracles OAuth endpoint
// context.RedirectUri holds the full redirect URL like: https://idcs-xxx.identity.oraclecloud.com/oauth2/v1/authorize?client_id=...&scope=...&...&redirect_uri=http://internal-host.io/authorization_code/callback
// this is more of a pseudo-code because we have to deal with encoding the URL/query string
context.RedirectUri = context.RedirectUri.Replace("http://internal-host.io/", "https://public-host.com/");
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
}
};
});
现在发送 OAuth 请求时使用了正确的
redirect_uri
参数,Oracle 中的身份验证成功(我认为)并且 Oracle 重定向回正确的地址(https://public-host.com/authorization-code/callback?code=XXX
但现在 C# 代码抛出两个错误:
An error was encountered while handling the remote login. Correlation failed.
Microsoft.AspNetCore.Authentication.AuthenticationFailureException:
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1+<HandleRequestAsync>d__12.MoveNext (Microsoft.AspNetCore.Authentication, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60)
和
Microsoft.AspNetCore.Authentication.AuthenticationFailureException: An error was encountered while handling the remote login.
---> Microsoft.AspNetCore.Authentication.AuthenticationFailureException: OAuth token endpoint failure: invalid_redirect_uri;Description=Client XXXX requested an invalid redirect URL: http://internal-host.io:443/authorization-code/callback.
(请注意
http://internal-host.io
)
所以我怀疑,尽管我更改了
redirect_uri
中的 OnRedirectToAuthorizationEndpoint
,但 OAuth 处理代码以某种方式期望 local-host.io 而不是 public-host.com 并且失败,因为它是不同的......
更多配置:
services.AddAuthentication(options => {...})
.AddOAuth("Oracle", options =>
{
Uri apiAddress = configuration.OracleApiAddress;
options.AuthorizationEndpoint = $"{apiAddress}/oauth2/v1/authorize";
options.Scope.Add("openid");
options.Scope.Add("urn:opc:idm:__myscopes__");
options.CallbackPath = new PathString("/authorization-code/callback");
options.ClientId = configuration.OracleApplicationId;
options.ClientSecret = configuration.OracleApplicationSecret;
options.TokenEndpoint = $"{apiAddress}/oauth2/v1/token";
options.UserInformationEndpoint = $"{apiAddress}/oauth2/v1/userinfo";
// ...
}
解决方案是在自定义中间件中修改请求主机/方案/端口。就我而言,由于 API 网关包含的标头,外部/原始主机是已知的。
public class CustomMiddleware(RequestDelegate next)
{
public async Task InvokeAsync(HttpContext context)
{
// Header from the API gateway
string originalHost = context.Request.Headers["x-original-host"].ToString();
context.Request.Scheme = "https";
context.Request.Host = new HostString(originalHost, 443);
await next(context);
}
}
用途
Program.cs
public static void Main(string[] args)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
/* ... */
WebApplication app = builder.Build();
app.UseMiddleware<CustomMiddleware>();
/* ... */
app.UseAuthentication();
app.UseAuthorization();
/* ... */
app.Run();
}