using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Threading; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Newtonsoft.Json; using C4IT.Security; using C4IT.Logging; using C4IT.XML; using C4IT.FASD.Base; using C4IT.DataHistoryProvider; using static C4IT.Logging.cLogManager; using System.Web.Http.Controllers; using System.Reflection; using Microsoft.Ajax.Utilities; namespace F4SDwebService { public class cAuthentication : DelegatingHandler { private const string WwwAuthenticateHeader = "WWW-Authenticate"; private const string Basic = "Basic"; public cDataHistoryCollector Collector = null; public Dictionary OTPWs = null; protected override async Task SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { if (Collector == null || OTPWs == null) return await base.SendAsync(request, cancellationToken); MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var Path = request.RequestUri.LocalPath.ToLowerInvariant(); var UserInfo = ValidateBearer(request); if (UserInfo != null) { request.Properties.Add("F4SD_UserInfo", UserInfo); return await base.SendAsync(request, cancellationToken); } if (!cLogManager.DefaultLogger.IsDebug && !Path.EndsWith("checkconnection") && !Path.EndsWith("logon/logon") ) { var response = ValidateOTP(request); if (response != null) { var tsc = new TaskCompletionSource(); tsc.SetResult(response); return await tsc.Task; } } var _res = await base.SendAsync(request, cancellationToken); return _res; } catch (System.OperationCanceledException EC) { LogEntry($"The operation of the request '{request.RequestUri} was canceled.'", LogLevels.Debug); LogException(EC, LogLevels.Debug); } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } var responseErr = new HttpResponseMessage(HttpStatusCode.InternalServerError); var tsc2 = new TaskCompletionSource(); tsc2.SetResult(responseErr); return await tsc2.Task; } private cF4sdUserInfo ValidateBearer(HttpRequestMessage request) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { if (Collector == null) return null; var authenticationHeaderValue = request.Headers.Authorization; if (authenticationHeaderValue == null || string.IsNullOrEmpty(authenticationHeaderValue.Scheme) || !authenticationHeaderValue.Scheme.Equals("Bearer") || string.IsNullOrEmpty(authenticationHeaderValue.Parameter)) { LogEntry($"no valid bearer authentication header", LogLevels.Debug); return null; } var _res = Collector.JwtTokenHandler.ValidateToken(authenticationHeaderValue.Parameter, Collector.JwtTokenValidationParameters, out var _res2); if (!(_res?.Identity?.IsAuthenticated == true)) { LogEntry($"bearer token could not validated", LogLevels.Debug); return null; } if (!(_res2 is JwtSecurityToken _sec)) { LogEntry($"bearer token is not a JwtSecurityToken", LogLevels.Debug); return null; } var _now = DateTime.UtcNow; if (_sec.ValidFrom > _now || _sec.ValidTo < _now) { LogEntry($"bearer token could is not a JwtSecurityToken", LogLevels.Debug); return null; } var strNameId = _sec.Claims.First(c => c.Type == JwtRegisteredClaimNames.NameId)?.Value; if (!Guid.TryParse(strNameId, out var _nameId)) { LogEntry($"bearer token has no valid NameId property", LogLevels.Debug); return null; } var _retVal = new cF4sdUserInfo() { Id = _nameId, ValidUntil = _sec.ValidTo, LogonTime = _sec.ValidFrom }; _retVal.Name = _sec.Claims.First(c => c.Type == JwtRegisteredClaimNames.UniqueName).Value; var strAuthTime = _sec.Claims.First(c => c.Type == JwtRegisteredClaimNames.AuthTime).Value; if (DateTime.TryParseExact(strAuthTime, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'z'", null, System.Globalization.DateTimeStyles.AssumeUniversal, out var _AuthTime)) _retVal.LogonTime = _AuthTime; var _strLoginMethod = _sec.Claims.First(c => c.Type == "login_method").Value; _retVal.AccountType = cXmlParser.GetEnumFromString(_strLoginMethod, cF4sdUserInfo.enumAccountType.unknown); _retVal.AdAccount = _sec.Claims.First(c => c.Type == "login_account").Value; _retVal.AdSid = _sec.Claims.First(c => c.Type == "account_sid").Value; _retVal.Language = _sec.Claims.First(c => c.Type == "language").Value; var _roles = _sec.Claims.Where(c => c.Type == "roles")?.Select(c => c.Value)?.ToList(); if (_roles?.Count() > 0) _retVal.Roles = _roles; if (cLogManager.DefaultLogger.IsDebug) { var _str = JsonConvert.SerializeObject(_retVal, Formatting.Indented); var _lst = new List(2) { "Bearer user info:", _str }; cLogManager.DefaultLogger.LogList(LogLevels.Debug, _lst); } return _retVal; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return null; } private HttpResponseMessage ValidateOTP(HttpRequestMessage request) { bool auth = false; try { var authenticationHeaderValue = request.Headers.Authorization; if (authenticationHeaderValue == null || string.IsNullOrEmpty(authenticationHeaderValue.Scheme) || !authenticationHeaderValue.Scheme.Equals(Basic) || string.IsNullOrEmpty(authenticationHeaderValue.Parameter)) { var response = new HttpResponseMessage(HttpStatusCode.Unauthorized); response.Headers.Add(WwwAuthenticateHeader, Basic); return response; } string authValue = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(authenticationHeaderValue.Parameter)); string[] authParts = authValue.Split(':'); if (authParts.Length == 2) { if (OTPWs == null) cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, "no OTP providers configured."); cOneTimePW OTP; if ((OTPWs != null) && OTPWs.TryGetValue(authParts[0], out OTP)) { UInt32 Id; if (UInt32.TryParse(authParts[1], System.Globalization.NumberStyles.Integer, null, out Id)) { auth = OTP.Check(Id); } } } else cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("Key contains {0} items!", authParts.Length)); } catch { } if (!auth) { var response = new HttpResponseMessage(HttpStatusCode.Unauthorized); response.Headers.Add(WwwAuthenticateHeader, Basic); return response; } return null; } static public cF4sdUserInfo GetUserInfo(HttpActionContext context) { try { var _props = context?.Request?.Properties; if (_props == null) return null; if (!_props.TryGetValue("F4SD_UserInfo", out var _objUI)) return null; if (_objUI is cF4sdUserInfo userInfo) return userInfo; } catch (Exception E) { LogException(E); } return null; } } }