mirror of
https://github.com/hargata/lubelog.git
synced 2025-12-10 00:46:08 -06:00
Added OpenID login.
This commit is contained in:
parent
ac4ea07319
commit
d5f0e57c3b
@ -13,7 +13,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CsvHelper" Version="30.0.1" />
|
<PackageReference Include="CsvHelper" Version="30.0.1" />
|
||||||
<PackageReference Include="LiteDB" Version="5.0.17" />
|
<PackageReference Include="LiteDB" Version="5.0.17" />
|
||||||
<PackageReference Include="Npgsql" Version="8.0.1" />
|
<PackageReference Include="Npgsql" Version="8.0.2" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.3.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -4,6 +4,8 @@ using CarCareTracker.Models;
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -15,16 +17,19 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
private IDataProtector _dataProtector;
|
private IDataProtector _dataProtector;
|
||||||
private ILoginLogic _loginLogic;
|
private ILoginLogic _loginLogic;
|
||||||
|
private IConfigHelper _config;
|
||||||
private readonly ILogger<LoginController> _logger;
|
private readonly ILogger<LoginController> _logger;
|
||||||
public LoginController(
|
public LoginController(
|
||||||
ILogger<LoginController> logger,
|
ILogger<LoginController> logger,
|
||||||
IDataProtectionProvider securityProvider,
|
IDataProtectionProvider securityProvider,
|
||||||
ILoginLogic loginLogic
|
ILoginLogic loginLogic,
|
||||||
)
|
IConfigHelper config
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_dataProtector = securityProvider.CreateProtector("login");
|
_dataProtector = securityProvider.CreateProtector("login");
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_loginLogic = loginLogic;
|
_loginLogic = loginLogic;
|
||||||
|
_config = config;
|
||||||
}
|
}
|
||||||
public IActionResult Index(string redirectURL = "")
|
public IActionResult Index(string redirectURL = "")
|
||||||
{
|
{
|
||||||
@ -42,6 +47,66 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
return View();
|
return View();
|
||||||
}
|
}
|
||||||
|
public IActionResult GetRemoteLoginLink()
|
||||||
|
{
|
||||||
|
var remoteAuthURL = _config.GetOpenIDConfig().RemoteAuthURL;
|
||||||
|
return Json(remoteAuthURL);
|
||||||
|
}
|
||||||
|
public async Task<IActionResult> RemoteAuth(string code)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(code))
|
||||||
|
{
|
||||||
|
//received code from OIDC provider
|
||||||
|
//create http client to retrieve user token from OIDC
|
||||||
|
var httpClient = new HttpClient();
|
||||||
|
var openIdConfig = _config.GetOpenIDConfig();
|
||||||
|
var httpParams = new List<KeyValuePair<string, string>>
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("code", code),
|
||||||
|
new KeyValuePair<string, string>("grant_type", "authorization_code"),
|
||||||
|
new KeyValuePair<string, string>("client_id", openIdConfig.ClientId),
|
||||||
|
new KeyValuePair<string, string>("client_secret", openIdConfig.ClientSecret),
|
||||||
|
new KeyValuePair<string, string>("redirect_uri", openIdConfig.RedirectURL)
|
||||||
|
};
|
||||||
|
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<OpenIDResult>(tokenResult)?.id_token ?? string.Empty;
|
||||||
|
if (!string.IsNullOrWhiteSpace(userJwt))
|
||||||
|
{
|
||||||
|
//validate JWT token
|
||||||
|
var tokenParser = new JwtSecurityTokenHandler();
|
||||||
|
var parsedToken = tokenParser.ReadJwtToken(userJwt);
|
||||||
|
var userEmailAddress = parsedToken.Claims.First(x => x.Type == "email").Value;
|
||||||
|
if (!string.IsNullOrWhiteSpace(userEmailAddress))
|
||||||
|
{
|
||||||
|
var userData = _loginLogic.ValidateOpenIDUser(new LoginModel() { EmailAddress = userEmailAddress });
|
||||||
|
if (userData.Id != default)
|
||||||
|
{
|
||||||
|
AuthCookie authCookie = new AuthCookie
|
||||||
|
{
|
||||||
|
UserData = userData,
|
||||||
|
ExpiresOn = DateTime.Now.AddDays(1)
|
||||||
|
};
|
||||||
|
var serializedCookie = JsonSerializer.Serialize(authCookie);
|
||||||
|
var encryptedCookie = _dataProtector.Protect(serializedCookie);
|
||||||
|
Response.Cookies.Append("ACCESS_TOKEN", encryptedCookie, new CookieOptions { Expires = new DateTimeOffset(authCookie.ExpiresOn) });
|
||||||
|
return new RedirectResult("/Home");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex.Message);
|
||||||
|
return new RedirectResult("/Login");
|
||||||
|
}
|
||||||
|
return new RedirectResult("/Login");
|
||||||
|
}
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult Login(LoginModel credentials)
|
public IActionResult Login(LoginModel credentials)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -7,6 +7,7 @@ namespace CarCareTracker.Helper
|
|||||||
{
|
{
|
||||||
public interface IConfigHelper
|
public interface IConfigHelper
|
||||||
{
|
{
|
||||||
|
OpenIDConfig GetOpenIDConfig();
|
||||||
UserConfig GetUserConfig(ClaimsPrincipal user);
|
UserConfig GetUserConfig(ClaimsPrincipal user);
|
||||||
bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData);
|
bool SaveUserConfig(ClaimsPrincipal user, UserConfig configData);
|
||||||
string GetLogoUrl();
|
string GetLogoUrl();
|
||||||
@ -28,6 +29,11 @@ namespace CarCareTracker.Helper
|
|||||||
_userConfig = userConfig;
|
_userConfig = userConfig;
|
||||||
_cache = memoryCache;
|
_cache = memoryCache;
|
||||||
}
|
}
|
||||||
|
public OpenIDConfig GetOpenIDConfig()
|
||||||
|
{
|
||||||
|
OpenIDConfig openIdConfig = _config.GetSection("OpenID").Get<OpenIDConfig>() ?? new OpenIDConfig();
|
||||||
|
return openIdConfig;
|
||||||
|
}
|
||||||
public string GetLogoUrl()
|
public string GetLogoUrl()
|
||||||
{
|
{
|
||||||
var logoUrl = _config["LUBELOGGER_LOGO_URL"];
|
var logoUrl = _config["LUBELOGGER_LOGO_URL"];
|
||||||
|
|||||||
@ -21,6 +21,7 @@ namespace CarCareTracker.Logic
|
|||||||
OperationResponse ResetPasswordByUser(LoginModel credentials);
|
OperationResponse ResetPasswordByUser(LoginModel credentials);
|
||||||
OperationResponse ResetUserPassword(LoginModel credentials);
|
OperationResponse ResetUserPassword(LoginModel credentials);
|
||||||
UserData ValidateUserCredentials(LoginModel credentials);
|
UserData ValidateUserCredentials(LoginModel credentials);
|
||||||
|
UserData ValidateOpenIDUser(LoginModel credentials);
|
||||||
bool CheckIfUserIsValid(int userId);
|
bool CheckIfUserIsValid(int userId);
|
||||||
bool CreateRootUserCredentials(LoginModel credentials);
|
bool CreateRootUserCredentials(LoginModel credentials);
|
||||||
bool DeleteRootUserCredentials();
|
bool DeleteRootUserCredentials();
|
||||||
@ -193,6 +194,19 @@ namespace CarCareTracker.Logic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public UserData ValidateOpenIDUser(LoginModel credentials)
|
||||||
|
{
|
||||||
|
var result = _userData.GetUserRecordByEmailAddress(credentials.EmailAddress);
|
||||||
|
if (result.Id != default)
|
||||||
|
{
|
||||||
|
result.Password = string.Empty;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new UserData();
|
||||||
|
}
|
||||||
|
}
|
||||||
#region "Admin Functions"
|
#region "Admin Functions"
|
||||||
public bool MakeUserAdmin(int userId, bool isAdmin)
|
public bool MakeUserAdmin(int userId, bool isAdmin)
|
||||||
{
|
{
|
||||||
|
|||||||
14
Models/OIDC/OpenIDConfig.cs
Normal file
14
Models/OIDC/OpenIDConfig.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public class OpenIDConfig
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string ClientId { get; set; }
|
||||||
|
public string ClientSecret { get; set; }
|
||||||
|
public string AuthURL { get; set; }
|
||||||
|
public string TokenURL { get; set; }
|
||||||
|
public string RedirectURL { get; set; }
|
||||||
|
public string Scope { get; set; }
|
||||||
|
public string RemoteAuthURL { get { return $"{AuthURL}?client_id={ClientId}&response_type=code&redirect_uri={RedirectURL}&scope={Scope}"; } }
|
||||||
|
}
|
||||||
|
}
|
||||||
7
Models/OIDC/OpenIDResult.cs
Normal file
7
Models/OIDC/OpenIDResult.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace CarCareTracker.Models
|
||||||
|
{
|
||||||
|
public class OpenIDResult
|
||||||
|
{
|
||||||
|
public string id_token { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@
|
|||||||
@{
|
@{
|
||||||
var logoUrl = config.GetLogoUrl();
|
var logoUrl = config.GetLogoUrl();
|
||||||
var userLanguage = config.GetServerLanguage();
|
var userLanguage = config.GetServerLanguage();
|
||||||
|
var openIdConfigName = config.GetOpenIDConfig().Name;
|
||||||
}
|
}
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "LubeLogger - Login";
|
ViewData["Title"] = "LubeLogger - Login";
|
||||||
@ -31,6 +32,12 @@
|
|||||||
<div class="d-grid">
|
<div class="d-grid">
|
||||||
<button type="button" class="btn btn-warning mt-2" onclick="performLogin()"><i class="bi bi-box-arrow-in-right me-2"></i>@translator.Translate(userLanguage, "Login")</button>
|
<button type="button" class="btn btn-warning mt-2" onclick="performLogin()"><i class="bi bi-box-arrow-in-right me-2"></i>@translator.Translate(userLanguage, "Login")</button>
|
||||||
</div>
|
</div>
|
||||||
|
@if (!string.IsNullOrWhiteSpace(openIdConfigName))
|
||||||
|
{
|
||||||
|
<div class="d-grid">
|
||||||
|
<button type="button" class="btn btn-secondary mt-2" onclick="remoteLogin()"><i class="bi bi-box-arrow-in-right me-2"></i>@($"{translator.Translate(userLanguage, "Login via")} {openIdConfigName}")</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="d-grid">
|
<div class="d-grid">
|
||||||
<a href="/Login/ForgotPassword" class="btn btn-link mt-2">@translator.Translate(userLanguage, "Forgot Password")</a>
|
<a href="/Login/ForgotPassword" class="btn btn-link mt-2">@translator.Translate(userLanguage, "Forgot Password")</a>
|
||||||
</div>
|
</div>
|
||||||
@ -41,7 +48,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
function getRedirectURL(){
|
function getRedirectURL() {
|
||||||
return { url: decodeHTMLEntities('@Model') };
|
return { url: decodeHTMLEntities('@Model') };
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
File diff suppressed because one or more lines are too long
@ -59,4 +59,12 @@ function handlePasswordKeyPress(event) {
|
|||||||
if (event.keyCode == 13) {
|
if (event.keyCode == 13) {
|
||||||
performLogin();
|
performLogin();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remoteLogin() {
|
||||||
|
$.get('/Login/GetRemoteLoginLink', function (data) {
|
||||||
|
if (data) {
|
||||||
|
window.location.href = data;
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user