Merge pull request #569 from hargata/Hargata/568

Add PKCE to OIDC Auth Flow
This commit is contained in:
Hargata Softworks
2024-07-31 12:28:20 -06:00
committed by GitHub
5 changed files with 51 additions and 3 deletions

View File

@@ -34,10 +34,16 @@ namespace CarCareTracker.Controllers
{
var generatedState = Guid.NewGuid().ToString().Substring(0, 8);
remoteAuthConfig.State = generatedState;
var pkceKeyPair = _loginLogic.GetPKCEChallengeCode();
remoteAuthConfig.CodeChallenge = pkceKeyPair.Value;
if (remoteAuthConfig.ValidateState)
{
Response.Cookies.Append("OIDC_STATE", remoteAuthConfig.State, new CookieOptions { Expires = new DateTimeOffset(DateTime.Now.AddMinutes(5)) });
}
if (remoteAuthConfig.UsePKCE)
{
Response.Cookies.Append("OIDC_VERIFIER", pkceKeyPair.Key, new CookieOptions { Expires = new DateTimeOffset(DateTime.Now.AddMinutes(5)) });
}
var remoteAuthURL = remoteAuthConfig.RemoteAuthURL;
return Redirect(remoteAuthURL);
}
@@ -60,10 +66,16 @@ namespace CarCareTracker.Controllers
var remoteAuthConfig = _config.GetOpenIDConfig();
var generatedState = Guid.NewGuid().ToString().Substring(0, 8);
remoteAuthConfig.State = generatedState;
var pkceKeyPair = _loginLogic.GetPKCEChallengeCode();
remoteAuthConfig.CodeChallenge = pkceKeyPair.Value;
if (remoteAuthConfig.ValidateState)
{
Response.Cookies.Append("OIDC_STATE", remoteAuthConfig.State, new CookieOptions { Expires = new DateTimeOffset(DateTime.Now.AddMinutes(5)) });
}
if (remoteAuthConfig.UsePKCE)
{
Response.Cookies.Append("OIDC_VERIFIER", pkceKeyPair.Key, new CookieOptions { Expires = new DateTimeOffset(DateTime.Now.AddMinutes(5)) });
}
var remoteAuthURL = remoteAuthConfig.RemoteAuthURL;
return Json(remoteAuthURL);
}
@@ -99,6 +111,16 @@ namespace CarCareTracker.Controllers
new KeyValuePair<string, string>("client_secret", openIdConfig.ClientSecret),
new KeyValuePair<string, string>("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<string, string>("code_verifier", storedVerifier));
Response.Cookies.Delete("OIDC_VERIFIER");
}
}
var httpRequest = new HttpRequestMessage(HttpMethod.Post, openIdConfig.TokenURL)
{
Content = new FormUrlEncodedContent(httpParams)
@@ -137,6 +159,11 @@ namespace CarCareTracker.Controllers
} else
{
_logger.LogInformation("OpenID Provider did not provide a valid id_token");
if (!string.IsNullOrWhiteSpace(tokenResult))
{
//if something was returned from the IdP but it's invalid, we want to log it as an error.
_logger.LogError($"Expected id_token, received {tokenResult}");
}
}
} else
{

View File

@@ -8,7 +8,7 @@ namespace CarCareTracker.Helper
/// </summary>
public static class StaticHelper
{
public static string VersionNumber = "1.3.5";
public static string VersionNumber = "1.3.6";
public static string DbName = "data/cartracker.db";
public static string UserConfigPath = "config/userConfig.json";
public static string GenericErrorMessage = "An error occurred, please try again later";

View File

@@ -2,6 +2,7 @@
using CarCareTracker.Helper;
using CarCareTracker.Models;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
@@ -28,7 +29,7 @@ namespace CarCareTracker.Logic
bool GenerateTokenForEmailAddress(string emailAddress, bool isPasswordReset);
List<UserData> GetAllUsers();
List<Token> GetAllTokens();
KeyValuePair<string, string> GetPKCEChallengeCode();
}
public class LoginLogic : ILoginLogic
{
@@ -439,6 +440,14 @@ namespace CarCareTracker.Logic
{
return Guid.NewGuid().ToString().Substring(0, 8);
}
public KeyValuePair<string, string> GetPKCEChallengeCode()
{
var verifierCode = Base64UrlEncoder.Encode(Guid.NewGuid().ToString().Replace("-", ""));
var verifierBytes = Encoding.UTF8.GetBytes(verifierCode);
var hashedCode = SHA256.Create().ComputeHash(verifierBytes);
var encodedChallengeCode = Base64UrlEncoder.Encode(hashedCode);
return new KeyValuePair<string, string>(verifierCode, encodedChallengeCode);
}
public bool GenerateTokenForEmailAddress(string emailAddress, bool isPasswordReset)
{
bool result = false;

View File

@@ -10,9 +10,18 @@
public string RedirectURL { get; set; }
public string Scope { get; set; }
public string State { get; set; }
public string CodeChallenge { get; set; }
public bool ValidateState { get; set; } = false;
public bool DisableRegularLogin { get; set; } = false;
public bool UsePKCE { get; set; } = false;
public string LogOutURL { get; set; } = "";
public string RemoteAuthURL { get { return $"{AuthURL}?client_id={ClientId}&response_type=code&redirect_uri={RedirectURL}&scope={Scope}&state={State}"; } }
public string RemoteAuthURL { get {
var redirectUrl = $"{AuthURL}?client_id={ClientId}&response_type=code&redirect_uri={RedirectURL}&scope={Scope}&state={State}";
if (UsePKCE)
{
redirectUrl += $"&code_challenge={CodeChallenge}&code_challenge_method=S256";
}
return redirectUrl;
} }
}
}

View File

@@ -22,6 +22,9 @@ LubeLogger is available as both a Docker Image and a Windows Standalone Executab
Read this [Getting Started Guide](https://docs.lubelogger.com/Getting%20Started) on how to download either of them
### Kubernetes Deployment
[Helm Chart](https://artifacthub.io/packages/helm/anza-labs/lubelogger) provided by [Anza-Labs](https://github.com/anza-labs)
### Need Help?
[Documentation](https://docs.lubelogger.com/)