Add userinfo to retrieve email claim if not provided in id_token

This commit is contained in:
DESKTOP-T0O5CDB\DESK-555BD
2025-04-05 08:17:40 -06:00
parent bf954a2946
commit d68e1a3939
7 changed files with 58 additions and 5 deletions

View File

@@ -130,7 +130,9 @@ namespace CarCareTracker.Controllers
Content = new FormUrlEncodedContent(httpParams) Content = new FormUrlEncodedContent(httpParams)
}; };
var tokenResult = await httpClient.SendAsync(httpRequest).Result.Content.ReadAsStringAsync(); var tokenResult = await httpClient.SendAsync(httpRequest).Result.Content.ReadAsStringAsync();
var userJwt = JsonSerializer.Deserialize<OpenIDResult>(tokenResult)?.id_token ?? string.Empty; var decodedToken = JsonSerializer.Deserialize<OpenIDResult>(tokenResult);
var userJwt = decodedToken?.id_token ?? string.Empty;
var userAccessToken = decodedToken?.access_token ?? string.Empty;
if (!string.IsNullOrWhiteSpace(userJwt)) if (!string.IsNullOrWhiteSpace(userJwt))
{ {
//validate JWT token //validate JWT token
@@ -140,7 +142,23 @@ namespace CarCareTracker.Controllers
if (parsedToken.Claims.Any(x => x.Type == "email")) if (parsedToken.Claims.Any(x => x.Type == "email"))
{ {
userEmailAddress = parsedToken.Claims.First(x => x.Type == "email").Value; userEmailAddress = parsedToken.Claims.First(x => x.Type == "email").Value;
} else }
else if (!string.IsNullOrWhiteSpace(openIdConfig.UserInfoURL) && !string.IsNullOrWhiteSpace(userAccessToken))
{
//retrieve claims from userinfo endpoint if no email claims are returned within id_token
var userInfoHttpRequest = new HttpRequestMessage(HttpMethod.Get, openIdConfig.UserInfoURL);
userInfoHttpRequest.Headers.Add("Authorization", $"Bearer {userAccessToken}");
var userInfoResult = await httpClient.SendAsync(userInfoHttpRequest).Result.Content.ReadAsStringAsync();
var userInfo = JsonSerializer.Deserialize<OpenIDUserInfo>(userInfoResult);
if (!string.IsNullOrWhiteSpace(userInfo?.email ?? string.Empty))
{
userEmailAddress = userInfo?.email ?? string.Empty;
} else
{
_logger.LogError($"OpenID Provider did not provide an email claim via UserInfo endpoint");
}
}
else
{ {
var returnedClaims = parsedToken.Claims.Select(x => x.Type); var returnedClaims = parsedToken.Claims.Select(x => x.Type);
_logger.LogError($"OpenID Provider did not provide an email claim, claims returned: {string.Join(",", returnedClaims)}"); _logger.LogError($"OpenID Provider did not provide an email claim, claims returned: {string.Join(",", returnedClaims)}");
@@ -239,7 +257,9 @@ namespace CarCareTracker.Controllers
Content = new FormUrlEncodedContent(httpParams) Content = new FormUrlEncodedContent(httpParams)
}; };
var tokenResult = await httpClient.SendAsync(httpRequest).Result.Content.ReadAsStringAsync(); var tokenResult = await httpClient.SendAsync(httpRequest).Result.Content.ReadAsStringAsync();
var userJwt = JsonSerializer.Deserialize<OpenIDResult>(tokenResult)?.id_token ?? string.Empty; var decodedToken = JsonSerializer.Deserialize<OpenIDResult>(tokenResult);
var userJwt = decodedToken?.id_token ?? string.Empty;
var userAccessToken = decodedToken?.access_token ?? string.Empty;
if (!string.IsNullOrWhiteSpace(userJwt)) if (!string.IsNullOrWhiteSpace(userJwt))
{ {
results.Add(OperationResponse.Succeed($"Passed JWT Parsing - id_token: {userJwt}")); results.Add(OperationResponse.Succeed($"Passed JWT Parsing - id_token: {userJwt}"));
@@ -252,6 +272,22 @@ namespace CarCareTracker.Controllers
userEmailAddress = parsedToken.Claims.First(x => x.Type == "email").Value; userEmailAddress = parsedToken.Claims.First(x => x.Type == "email").Value;
results.Add(OperationResponse.Succeed($"Passed Claim Validation - email")); results.Add(OperationResponse.Succeed($"Passed Claim Validation - email"));
} }
else if (!string.IsNullOrWhiteSpace(openIdConfig.UserInfoURL) && !string.IsNullOrWhiteSpace(userAccessToken))
{
//retrieve claims from userinfo endpoint if no email claims are returned within id_token
var userInfoHttpRequest = new HttpRequestMessage(HttpMethod.Get, openIdConfig.UserInfoURL);
userInfoHttpRequest.Headers.Add("Authorization", $"Bearer {userAccessToken}");
var userInfoResult = await httpClient.SendAsync(userInfoHttpRequest).Result.Content.ReadAsStringAsync();
var userInfo = JsonSerializer.Deserialize<OpenIDUserInfo>(userInfoResult);
if (!string.IsNullOrWhiteSpace(userInfo?.email ?? string.Empty))
{
userEmailAddress = userInfo?.email ?? string.Empty;
results.Add(OperationResponse.Succeed($"Passed Claim Validation - Retrieved email via UserInfo endpoint"));
} else
{
results.Add(OperationResponse.Failed($"Failed Claim Validation - Unable to retrieve email via UserInfo endpoint: {openIdConfig.UserInfoURL} using access_token: {userAccessToken} - Received {userInfoResult}"));
}
}
else else
{ {
var returnedClaims = parsedToken.Claims.Select(x => x.Type); var returnedClaims = parsedToken.Claims.Select(x => x.Type);

View File

@@ -12,7 +12,7 @@ namespace CarCareTracker.Helper
/// </summary> /// </summary>
public static class StaticHelper public static class StaticHelper
{ {
public const string VersionNumber = "1.4.6"; public const string VersionNumber = "1.4.7";
public const string DbName = "data/cartracker.db"; public const string DbName = "data/cartracker.db";
public const string UserConfigPath = "data/config/userConfig.json"; public const string UserConfigPath = "data/config/userConfig.json";
public const string LegacyUserConfigPath = "config/userConfig.json"; public const string LegacyUserConfigPath = "config/userConfig.json";

View File

@@ -15,6 +15,7 @@
public bool DisableRegularLogin { get; set; } = false; public bool DisableRegularLogin { get; set; } = false;
public bool UsePKCE { get; set; } = false; public bool UsePKCE { get; set; } = false;
public string LogOutURL { get; set; } = ""; public string LogOutURL { get; set; } = "";
public string UserInfoURL { get; set; } = "";
public string RemoteAuthURL { get { public string RemoteAuthURL { get {
var redirectUrl = $"{AuthURL}?client_id={ClientId}&response_type=code&redirect_uri={RedirectURL}&scope={Scope}&state={State}"; var redirectUrl = $"{AuthURL}?client_id={ClientId}&response_type=code&redirect_uri={RedirectURL}&scope={Scope}&state={State}";
if (UsePKCE) if (UsePKCE)

View File

@@ -3,5 +3,6 @@
public class OpenIDResult public class OpenIDResult
{ {
public string id_token { get; set; } public string id_token { get; set; }
public string access_token { get; set; }
} }
} }

View File

@@ -0,0 +1,7 @@
namespace CarCareTracker.Models
{
public class OpenIDUserInfo
{
public string email { get; set; } = "";
}
}

View File

@@ -165,6 +165,14 @@
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">
<input type="text" readonly id="inputOIDCToken" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.TokenURL"> <input type="text" readonly id="inputOIDCToken" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.TokenURL">
</div> </div>
</div>
<div class="row mb-2">
<div class="col-md-6 col-12">
<label for="inputOIDCUserInfo">@translator.Translate(userLanguage, "OIDC UserInfo URL")</label>
</div>
<div class="col-md-6 col-12">
<input type="text" readonly id="inputOIDCUserInfo" class="form-control" placeholder="@translator.Translate(userLanguage, "Not Configured")" value="@Model.OIDCConfig.UserInfoURL">
</div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<div class="col-md-6 col-12"> <div class="col-md-6 col-12">

File diff suppressed because one or more lines are too long