我的應用程式堆疊中有一個 OpenID Connect / OAuth2 服務器 (IdP)。IdP 允許本地和外部身份驗證。
我有涵蓋大多數場景的集成測驗,但很難為外部身份驗證場景創建端到端測驗。有多個外部提供者,但從我的應用程式的角度來看,它們都在 OpenID Connect 上使用相同的作業流程,只有細微的差別(引數,即重定向 uri、方案名稱等)。因此,測驗其中之一就足夠了。其中之一是 Microsoft 帳戶(又名 Azure AD)
集成測驗基于 WebApplicationFactory (記憶體服務器與相應的HttpClient)。本地身份驗證非常簡單,因為整個部分都在我的應用程式域中運行,可以訪問完整的源代碼等。我只需向授權端點創建一個請求并在出現提示時回發用戶憑據(我仍然需要決議登錄頁面檢索防偽令牌,但這是可行的)
但是當涉及到外部時,例如 Microsoft 帳戶,登錄涉及通過 AJAX 的多個步驟,最后的帖子有超過 10 個引數,我無法逆向工程師。其他供應商也有同樣的難度。
由于外部提供者只是黑匣子,從我的 IdP 的角度來看,它只是發出挑戰(重定向到外部授權)并在重定向后拾取。有沒有一種模擬“中間”部分的好方法?
uj5u.com熱心網友回復:
我的解決方案是創建一個中間件,它將模擬外部身份驗證。然后重新配置外部身份驗證方案的選項以指向中間件正在處理的路徑。您可能還想覆寫簽名密鑰(或簽名驗證)。因此,此代碼轉到 WebApplicationFactory 的 ConfigureServices/ConfigureTestServices(等,取決于您的設定),以覆寫原始設定:
services.AddTransient<IStartupFilter, FakeExternalAuthenticationStartupFilter>();
services.Configure(AuthenticationSchemes.ExternalMicrosoft, (OpenIdConnectOptions options) =>
{
options.Configuration = new OpenIdConnectConfiguration
{
AuthorizationEndpoint = FakeExternalAuthenticationStartupFilter.AuthorizeEndpoint,
};
options.TokenValidationParameters.IssuerSigningKey = FakeExternalAuthenticationStartupFilter.SecurityKey;
});
備注:WebApplicationFactory沒有提供覆寫IApplicationBuilder(中間件)堆疊的方法,所以需要添加IStartupFilter
然后,中間件需要使用安全密鑰發出令牌,并將表單發送回重定向 uri。實作此目的的常用方法是回傳帶有表單的簡單 HTML 頁面,該表單將在加載后自行提交。這在瀏覽器中運行良好,但 HttpClient 不會做任何事情,因此測驗必須決議回應并手動創建發布請求。
雖然這是可行的,但我想省去這個額外的步驟,必須決議回應并重新發送它,并使其成為一個步驟。困難是:
- 重定向是不可能的(以 GET 請求開始,應以 POST 結束,還需要表單資料)
- 由 OpenIdConnectHandler 在重定向(相關性和亂數)之前發出的 cookie 是恢復狀態所必需的,僅在重定向 uri 路徑中可用(帶有 path= 的 Set-Cookie)
我的解決方案是在與設定重定向 uri 的路徑相同的路徑上創建一個中間件處理授權 (GET) 請求,發出令牌并重寫請求,以便 OpenIdConnectHandler 能夠接受。這是中間件的 Invoke 方法:
public async Task Invoke(HttpContext httpContext)
{
if (!HttpMethods.IsGet(httpContext.Request.Method) || !httpContext.Request.Path.StartsWithSegments(AuthorizeEndpoint))
{
await _next(httpContext);
return;
}
// get and validate query parameters
// Note: these are absolute minimal, might need to add more depending on your flow logic
var clientId = httpContext.Request.Query["client_id"].FirstOrDefault();
var state = httpContext.Request.Query["state"].FirstOrDefault();
var nonce = httpContext.Request.Query["nonce"].FirstOrDefault();
if (clientId is null || state is null || nonce is null)
{
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
var token = CreateToken(clientId, state, nonce); // CreateToken implementation omitted, use same signing key as used above
httpContext.Request.Method = HttpMethods.Post;
httpContext.Request.QueryString = QueryString.Empty;
httpContext.Request.ContentType = "application/x-www-form-urlencoded";
var content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
["id_token"] = token,
["token_type"] = "Bearer",
["expires_in"] = "3600",
["state"] = state,
});
using var buffer = new MemoryStream();
await content.CopyToAsync(buffer, httpContext.RequestAborted);
buffer.Seek(offset: 0, loc: SeekOrigin.Begin);
var oldBody = httpContext.Request.Body;
httpContext.Request.Body = buffer;
await _next(httpContext);
httpContext.Request.Body = oldBody;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/436105.html
標籤:asp.net 核心 验证 oauth-2.0 开放式连接 国内流离失所者
