From d3f29be227cc6652db5641e0a4f39a8c2288e8ff Mon Sep 17 00:00:00 2001 From: "DESKTOP-T0O5CDB\\DESK-555BD" Date: Mon, 31 Mar 2025 07:48:29 -0600 Subject: [PATCH] Add debug page for advance OIDC debugging --- Controllers/LoginController.cs | 106 +++++++++++++++++++++++++++-- Models/OIDC/OpenIDConfig.cs | 1 - Views/Login/RemoteAuthDebug.cshtml | 17 +++++ 3 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 Views/Login/RemoteAuthDebug.cshtml diff --git a/Controllers/LoginController.cs b/Controllers/LoginController.cs index 45d4050..964beff 100644 --- a/Controllers/LoginController.cs +++ b/Controllers/LoginController.cs @@ -136,10 +136,6 @@ namespace CarCareTracker.Controllers //validate JWT token var tokenParser = new JwtSecurityTokenHandler(); var parsedToken = tokenParser.ReadJwtToken(userJwt); - if (openIdConfig.TroubleshootingMode) - { - _logger.LogInformation($"OpenID Troubleshooting Mode Enabled - Token: {userJwt}"); - } var userEmailAddress = string.Empty; if (parsedToken.Claims.Any(x => x.Type == "email")) { @@ -192,6 +188,108 @@ namespace CarCareTracker.Controllers } return new RedirectResult("/Login"); } + public async Task RemoteAuthDebug(string code, string state = "") + { + List results = new List(); + try + { + if (!string.IsNullOrWhiteSpace(code)) + { + results.Add(OperationResponse.Succeed($"Received code from OpenID Provider: {code}")); + //received code from OIDC provider + //create http client to retrieve user token from OIDC + var httpClient = new HttpClient(); + var openIdConfig = _config.GetOpenIDConfig(); + //check if validate state is enabled. + if (openIdConfig.ValidateState) + { + var storedStateValue = Request.Cookies["OIDC_STATE"]; + if (!string.IsNullOrWhiteSpace(storedStateValue)) + { + Response.Cookies.Delete("OIDC_STATE"); + } + if (string.IsNullOrWhiteSpace(storedStateValue) || string.IsNullOrWhiteSpace(state) || storedStateValue != state) + { + results.Add(OperationResponse.Failed($"Failed State Validation - Expected: {storedStateValue} Received: {state}")); + } else + { + results.Add(OperationResponse.Succeed($"Passed State Validation - Expected: {storedStateValue} Received: {state}")); + } + } + var httpParams = new List> + { + new KeyValuePair("code", code), + new KeyValuePair("grant_type", "authorization_code"), + new KeyValuePair("client_id", openIdConfig.ClientId), + new KeyValuePair("client_secret", openIdConfig.ClientSecret), + new KeyValuePair("redirect_uri", openIdConfig.RedirectURL) + }; + if (openIdConfig.UsePKCE) + { + //retrieve stored challenge verifier + var storedVerifier = Request.Cookies["OIDC_VERIFIER"]; + if (!string.IsNullOrWhiteSpace(storedVerifier)) + { + httpParams.Add(new KeyValuePair("code_verifier", storedVerifier)); + Response.Cookies.Delete("OIDC_VERIFIER"); + } + } + var httpRequest = new HttpRequestMessage(HttpMethod.Post, openIdConfig.TokenURL) + { + Content = new FormUrlEncodedContent(httpParams) + }; + var tokenResult = await httpClient.SendAsync(httpRequest).Result.Content.ReadAsStringAsync(); + var userJwt = JsonSerializer.Deserialize(tokenResult)?.id_token ?? string.Empty; + if (!string.IsNullOrWhiteSpace(userJwt)) + { + results.Add(OperationResponse.Succeed($"Passed JWT Parsing - id_token: {userJwt}")); + //validate JWT token + var tokenParser = new JwtSecurityTokenHandler(); + var parsedToken = tokenParser.ReadJwtToken(userJwt); + var userEmailAddress = string.Empty; + if (parsedToken.Claims.Any(x => x.Type == "email")) + { + userEmailAddress = parsedToken.Claims.First(x => x.Type == "email").Value; + results.Add(OperationResponse.Succeed($"Passed Claim Validation - email")); + } + else + { + var returnedClaims = parsedToken.Claims.Select(x => x.Type); + results.Add(OperationResponse.Failed($"Failed Claim Validation - Expected: email Received: {string.Join(",", returnedClaims)}")); + } + if (!string.IsNullOrWhiteSpace(userEmailAddress)) + { + var userData = _loginLogic.ValidateOpenIDUser(new LoginModel() { EmailAddress = userEmailAddress }); + if (userData.Id != default) + { + results.Add(OperationResponse.Succeed($"Passed User Validation - Email: {userEmailAddress} Username: {userData.UserName}")); + } + else + { + results.Add(OperationResponse.Succeed($"Passed Email Validation - Email: {userEmailAddress} User not registered")); + } + } + else + { + results.Add(OperationResponse.Failed($"Failed Email Validation - No email received from OpenID Provider")); + } + } + else + { + results.Add(OperationResponse.Failed($"Failed to parse JWT - Expected: id_token Received: {tokenResult}")); + } + } + else + { + results.Add(OperationResponse.Failed("No code received from OpenID Provider")); + } + } + catch (Exception ex) + { + results.Add(OperationResponse.Failed($"Exception: {ex.Message}")); + } + return View(results); + } [HttpPost] public IActionResult Login(LoginModel credentials) { diff --git a/Models/OIDC/OpenIDConfig.cs b/Models/OIDC/OpenIDConfig.cs index 35b0970..e4aa621 100644 --- a/Models/OIDC/OpenIDConfig.cs +++ b/Models/OIDC/OpenIDConfig.cs @@ -15,7 +15,6 @@ public bool DisableRegularLogin { get; set; } = false; public bool UsePKCE { get; set; } = false; public string LogOutURL { get; set; } = ""; - public bool TroubleshootingMode { get; set; } = false; public string RemoteAuthURL { get { var redirectUrl = $"{AuthURL}?client_id={ClientId}&response_type=code&redirect_uri={RedirectURL}&scope={Scope}&state={State}"; if (UsePKCE) diff --git a/Views/Login/RemoteAuthDebug.cshtml b/Views/Login/RemoteAuthDebug.cshtml new file mode 100644 index 0000000..0e1e407 --- /dev/null +++ b/Views/Login/RemoteAuthDebug.cshtml @@ -0,0 +1,17 @@ +@model List +@{ + ViewData["Title"] = "Remote Auth Debug"; +} +
+ @foreach (OperationResponse result in Model) + { +
+
+ +
+
+ } +
+ \ No newline at end of file