AAD in relate
Published: 2023-11-15 04:31:30

```csharp
using System;
using System.Linq;
using System.Net;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Configuration;
using System.Data;
using System.Web;

using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Serilog;

namespace RelateSystem
{
    public partial class UnifiedIntegration : System.Web.UI.Page
    {
        private commonHelper commonH;
        private identityHelper identityH;
        private alertEmail alertH;
        private DataTable dtRetrieve;
        private string ipAddress;
        private IAppLogger _logger = AppLogger.LogInstance;

        private string RedirectUrl = ConfigurationManager.AppSettings["ULAUTH-Redirect"];
        private string clientId = ConfigurationManager.AppSettings["ULAUTH-clientId"];
        private string grantType = "authorization_code";
        private string Serurl = ConfigurationManager.AppSettings["ULAUTH-DEV"];

        protected void Page_Load(object sender, EventArgs e)
        {
            Session["userIdentity"] = null;
            bool hasKeys = Request.QueryString.HasKeys();
            bool redirectLoginflag = true;

            if (hasKeys)
            {
                if (Request.QueryString["ai"] != null)
                {
                    LogMessage("Collecting the ai value Query string.");
                    Session["aiState"] = Request.QueryString["ai"].ToString();
                    LogMessage("aiState stored in session." + Request.QueryString["ai"].ToString());
                }

                if (Request.QueryString["code"] != null && Request.QueryString["state"] != null)
                {
                    LogMessage("Request Query string code and state values are not null.");
                    redirectLoginflag = false;
                }
            }

            if (redirectLoginflag)
            {
                LogMessage("Request Query string code and state values are  null.");
                Guid obj = Guid.NewGuid();
                Session["state"] = obj.ToString();
                LogMessage("created the state and kept in session, State:" + obj.ToString());
                string scope = HttpUtility.UrlEncode(ConfigurationManager.AppSettings["Scope"]);
                string ResponseType = ConfigurationManager.AppSettings["ResponseType"];
                string url = "/connect/authorize?state=" + obj.ToString() + "&scope=" + scope + "&response_type=" + ResponseType + "&approval_prompt=auto&redirect_uri=" + HttpUtility.UrlEncode(RedirectUrl) + "&client_id=" + clientId + "";
                string ULurl = Serurl + url;
                LogMessage("Redirecting to unified login page.");
                Response.Redirect(ULurl);
            }

            string Rcode = Request.QueryString["code"].ToString();
            string Rscope = Request.QueryString["scope"].ToString();
            string Rstate = Request.QueryString["state"].ToString();
            string RSession_state = Request.QueryString["session_state"].ToString();
            LogMessage("Collecting the code,scope,state and session_state values from unified redirected URL.");
            string Raistate;
            if (Session["aiState"] != null)
            {
                Raistate = Session["aiState"].ToString();
                Session["aiState"] = null;
            }
            else
            {
                LogError("aiState value in session is assigned to null.");
            }

            //?ai = asdsasadas

            if (Rstate != Session["state"].ToString())
            {
                LogError("aiState value and state value are not matching. aistate : " + Rstate + "State: " + Session["state"].ToString());
                Session["state"] = null;
                string errorId = "0";
                commonHelper commonHE = new commonHelper();
                string page = HttpContext.Current.Request.Url.AbsolutePath;

                string errorInfo = "<br>Error Page URL: " + page +
                            "<br>Error Source: Unified Authentication issue from." + Request.Url +
                            "<br>Error Message: Unified Authentication issue" +
                            "<br>Error Stack trace: " + Request.ContentType.ToString();
                errorId = commonHE.userErrorLog_Process("0", page, errorInfo);
                LogError(errorInfo);
                Response.Redirect("UnifiedErrorPage.aspx?err=" + errorInfo);
            }

            // openid-configuration

            try
            {
                OpenIDJson Oidc;
                using (HttpClient client = new HttpClient())
                {
                    System.Net.ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
                    string oidcsetting = Serurl + "/.well-known/openid-configuration";
                    LogMessage("Http Client call to openid configuration initiated. Url is :" + oidcsetting);
                    client.BaseAddress = new Uri(oidcsetting);
                    LogMessage("Http Client call to openid request raised.");

                    HttpResponseMessage response = client.GetAsync(client.BaseAddress).Result;
                    LogMessage("Http Client call to openid response." + response);
                    LogMessage("penid configuration response deserialization initiated.");
                    Oidc = JsonConvert.DeserializeObject<OpenIDJson>(response.Content.ReadAsStringAsync().Result);
                    LogMessage("penid configuration response deserialization Completed.");
                    LogMessage("Http Client call to openid configuration is completed.");
                }

                // need to valid Oidc
                //need to cache implementation.
                // genrating Token Url
                string Token = GenerateoAuth2Token(Oidc.token_endpoint, Rcode, Rstate);
                LogMessage("Http Client call to token_endpoint completed.");
                //need to cache implementation.
                LogMessage("Http Client call to UnifiedLoginPublicKey initiated.");
                var ULroolObjectKey = getUnifiedLoginPublicKey(Oidc.jwks_uri);
                LogMessage("Http Client call to UnifiedLoginPublicKey Completed.");

                //Validate and decode the token  to claims
                var claims = ValidateToken(ULroolObjectKey, Token);
                LogMessage("validation on token completed. Collected the claims.");
                if (claims != null && claims.FirstOrDefault(x => x.Type.Equals("relate-username")) != null)
                {
                    string loginName = claims.FirstOrDefault(x => x.Type.Equals("relate-username")).Value; //claims.FirstOrDefault(x => x.Type.Equals("loginName")).Value;//"testunifieduser";
                    LogMessage("relate-username is retrived from claims.");
                    if (loginName != null && loginName.Length > 0)
                    {
                        identityH = new identityHelper();
                        LogMessage("Calling relate DB to pull relate-username details.");
                        identityH.setIdentity_UL(loginName, 0);
                        LogMessage("Calling relate DB to pull relate-username details is completed.");
                        LogMessage("Validating the user details.");
                        if (identityH.ValidUser)
                        {
                            Session["userIdentity"] = identityH;
                            LogMessage("Validating the user details successfull. redirecting to Relate Dashboard.");
                            Response.Redirect("Dashboard.aspx");
                        }
                        else
                        {
                            string message = "Validating the user details from Relate DB is Failed. User Name:" + loginName;
                            LogError(message);
                            Response.Redirect("UnifiedErrorPage.aspx?err=" + message);
                        }
                    }
                }
                else
                {
                    string message = "Token claims does not have Relate 247User Name";
                    LogError(message);
                    Response.Redirect("UnifiedErrorPage.aspx?err=" + message);
                }
            }
            catch (AggregateException err)
            {
                int i = 1;

                foreach (var errInner in err.InnerExceptions)
                {
                    LogMessage("Error " + i + " :" + errInner.Message);
                    LogMessage("Error " + i + " : StackTrace: " + errInner.InnerException.StackTrace);
                    LogMessage("Error " + i + " : Source: " + errInner.StackTrace);
                }
            }
        }

        #region object members

        /// <summary>
        /// Onesite Environment
        /// </summary>
        public string Env
        {
            get
            {
                return ConfigurationManager.AppSettings["env"].ToUpper();
            }
        }

        #endregion object members

        public UnifiedLoginRootobject getUnifiedLoginPublicKey(string jwsul)
        {
            try
            {
                UnifiedLoginRootobject Oidc = null;
                using (var client = new HttpClient())
                {
                    var httpResponseMessage = client.GetAsync(jwsul).Result;
                    Oidc = JsonConvert.DeserializeObject<UnifiedLoginRootobject>(httpResponseMessage.Content.ReadAsStringAsync().Result);
                }
                return Oidc;
            }
            catch (Exception ex)
            {
                string message = "Error Occured in getting Unified login public Key information";
                LogError(message);
                Response.Redirect("UnifiedErrorPage.aspx?err=" + ex.Message);
                return null;
            }
        }

        public string GenerateoAuth2Token(string tokenendpoint, string Code, string state)
        {
            try
            {
                string json = null;
                using (var client = new HttpClient())
                {
                    //client.BaseAddress = new Uri(Serurl);
                    var content = new FormUrlEncodedContent(new[]
                    {
                     new KeyValuePair<string, string>("grant_type", grantType)
                    ,new KeyValuePair<string, string>("code", Code)
                    ,new KeyValuePair<string, string>("redirect_uri", RedirectUrl)
                    ,new KeyValuePair<string, string>("client_id", clientId)              //consider sending via basic authentication header
                    ,new KeyValuePair<string, string>("client_secret", "devsecrettesting")
                    });
                    LogMessage("Http Client call to token_endpoint initiated." + tokenendpoint);
                    var httpResponseMessage = client.PostAsync(tokenendpoint, content).Result;
                    json = httpResponseMessage.Content.ReadAsStringAsync().Result;
                }

                //Extract the Access Token
                dynamic results = JsonConvert.DeserializeObject<dynamic>(json);
                LogMessage("Token info:" + results.access_token);
                return results.access_token;
            }
            catch (Exception ex)
            {
                string message = "Error Occured in getting Token information from Unified";
                LogError(message);
                Response.Redirect("UnifiedErrorPage.aspx?err=" + ex.Message);

                return "";
            }
        }

        private List<Claim> decodeJWTToken(string token)
        {
            try
            {
                var stream = token;
                var handler = new JwtSecurityTokenHandler();
                var jsonToken = handler.ReadToken(stream);
                var tokenS = jsonToken as JwtSecurityToken;
                return tokenS.Claims.ToList<Claim>();
            }
            catch (Exception ex)
            {
                string message = "Error Occured in decoding JWT Token to Claims";
                LogError(message);
                Response.Redirect("UnifiedErrorPage.aspx?err=" + ex.Message);

                return null;
            }
        }

        private IEnumerable<Claim> ValidateToken(UnifiedLoginRootobject UlRootKey, string token)
        {
            try
            {
                //Grab certificate for verifying JWT signature
                //IdentityServer4 also has a default certificate you can might reference.
                //In prod, we'd get this from the certificate store or similar
                //var certPath = Path.Combine(Server.MapPath("~/bin"), "SscSign.pfx");

                byte[] bytes = Encoding.ASCII.GetBytes(UlRootKey.keys[0].x5c[0]);
                var cert = new X509Certificate2(bytes);
                var x509SecurityKey = new X509SecurityKey(cert);
                string Aud_url = Serurl + "/resources";
                var parameters = new TokenValidationParameters
                {
                    RequireSignedTokens = true,
                    ValidAudience = Aud_url,
                    ValidIssuer = Serurl,
                    IssuerSigningKey = x509SecurityKey,
                    RequireExpirationTime = true,
                    ClockSkew = TimeSpan.FromMinutes(5)
                };

                //Validate the token and retrieve ClaimsPrinciple
                var handler = new JwtSecurityTokenHandler();
                SecurityToken jwt;
                var id = handler.ValidateToken(token, parameters, out jwt);

                //Discard temp cookie and cookie-based middleware authentication objects (we just needed it for storing State)
                //this.Request.GetOwinContext().Authentication.SignOut("TempCookie");

                return id.Claims;
            }
            catch (Exception ex)
            {
                string message = "Error Occured in Validating JWT token ";
                LogError(message);
                Response.Redirect("UnifiedErrorPage.aspx?err=" + ex.Message);

                return null;
            }
        }

        private void LogMessage(string message)
        {
            bool flag = Convert.ToBoolean(System.Configuration.ConfigurationManager.AppSettings["UnifiedLogFlag"]);
            if (flag)
            {
                _logger.Log(LogLevel.Info, message);
            }
        }

        private void LogError(string message)
        {
            bool flag = Convert.ToBoolean(System.Configuration.ConfigurationManager.AppSettings["UnifiedLogFlag"]);
            if (flag)
            {
                _logger.Log(LogLevel.Error, message);
            }
        }
    }

    [Serializable]
    public class UnifiedLoginRootobject
    {
        public UnifiedLoginKey[] keys { get; set; }
    }

    [Serializable]
    public class UnifiedLoginKey
    {
        public string kty { get; set; }
        public string use { get; set; }
        public string kid { get; set; }
        public string x5t { get; set; }
        public string e { get; set; }
        public string n { get; set; }
        public string[] x5c { get; set; }
        public string alg { get; set; }
    }
}
```