/// <summary>
        /// Used by Online
        /// </summary>
        /// <param name="token">Saml or JWT token</param>
        public static bool TryLogin(string token, string tokenType, OidcModel oidcModel, out string errorReason)
        {
            errorReason = String.Empty;
            var tokenHandler = new SuperIdTokenHandler();

            tokenHandler.ValidIssuer = oidcModel == null ? "SuperOffice AS" : "https://sod.superoffice.com"; // required for OIDC vs. Old Federated Auth...

            var useAppData = Convert.ToBoolean(ConfigurationManager.AppSettings["CertificatesInAppDataFolder"]);

            var typedTokenType = (SuperOffice.SuperID.Contracts.SystemUser.V1.TokenType)
                                 Enum.Parse(typeof(SuperOffice.SuperID.Contracts.SystemUser.V1.TokenType), tokenType);

            if (useAppData && typedTokenType == SuperOffice.SuperID.Contracts.SystemUser.V1.TokenType.Saml)
            {
                tokenHandler.IssuerTokenResolver =
                    new CertificateFileCertificateStoreTokenResolver(
                        HttpContext.Current.Server.MapPath("~/App_Data"));
                tokenHandler.CertificateValidator = X509CertificateValidator.None;
            }
            else if (useAppData && typedTokenType == SuperOffice.SuperID.Contracts.SystemUser.V1.TokenType.Jwt)
            {
                tokenHandler.JwtIssuerSigningCertificate =
                    new System.Security.Cryptography.X509Certificates.X509Certificate2(
                        HttpContext.Current.Server.MapPath("~/App_Data/") + "SuperOfficeFederatedLogin.crt");
            }
            else
            {
                tokenHandler.CertificateValidator = X509CertificateValidator.PeerTrust;
            }

            tokenHandler.ValidateAudience = false;
            var superIdClaims = tokenHandler.ValidateToken(token, typedTokenType);

            var context = new SuperOfficeContext
            {
                Ticket            = superIdClaims.Ticket,
                Email             = superIdClaims.Email,
                ContextIdentifier = superIdClaims.ContextIdentifier,
                NetServerUrl      = superIdClaims.NetserverUrl,
                SystemToken       = superIdClaims.SystemToken,
                CustomerKey       = String.Empty,
                IsOnSiteCustomer  = false,
                AccessToken       = oidcModel?.AccessToken,
                IdToken           = oidcModel?.IdToken,
                RefreshToken      = oidcModel?.RefreshToken
            };

            return(TryLogin(context, out errorReason));
        }
        /// <summary>
        /// Used by OnSite customers only
        /// </summary>
        /// <param name="customerKey"></param>
        /// <param name="ticket"></param>
        /// <param name="userId"></param>
        /// <param name="errorReason"></param>
        /// <returns></returns>
        public static bool TryLogin(string customerKey, string ticket, string userId, bool local, out string errorReason)
        {
            if (String.IsNullOrEmpty(customerKey))
            {
                errorReason = "CustomerKey is missing.";
                return(false);
            }

            var customer = CustomerDataSource.Instance.Customers.Find(c => c.CustomerKey == customerKey);

            if (customer == null)
            {
                errorReason = "Could not find customer: " + customerKey;
                return(false);
            }

            var netServerUrl = customer.NetServerUrl;

            if (String.IsNullOrEmpty(netServerUrl))
            {
                errorReason = "No NetServerUrl";
                return(false);
            }

            var context = new SuperOfficeContext
            {
                Ticket            = ticket,
                Email             = userId,
                ContextIdentifier = customer.ContextIdentifier,
                NetServerUrl      = netServerUrl + "Services75/",
                CustomerKey       = customer.CustomerKey,
                IsOnSiteCustomer  = true,
                WebClientUrl      = customer.WebClientUrl
            };

            return(TryLogin(context, out errorReason));
        }
        /// <summary>
        /// Does the actual authentication
        /// </summary>
        /// <param name="context"></param>
        /// <param name="errorReason"></param>
        /// <returns></returns>
        public static bool TryLogin(SuperOfficeContext context, out string errorReason)
        {
            Context = context;

            //If we are allready authorized, then logout first, before creating a cookie....
            if (SuperOffice.SoContext.IsAuthenticated)
            {
                SuperOffice.SoContext.CloseCurrentSession();
                SuperOfficeAuthHelper.Logout();
            }

            // Use forms authentication - this is optional
            var soFormsTicket          = new FormsAuthenticationTicket(context.Email, false, 3600);
            var soFormsTicketEncrypted = FormsAuthentication.Encrypt(soFormsTicket);

            var httpContext = HttpContext.Current;

            httpContext.Session[ConfigManager.SoAuthCookie] = soFormsTicketEncrypted;
            httpContext.Response.Cookies.Add(new HttpCookie(ConfigManager.SoAuthCookie, soFormsTicketEncrypted));



            try
            {
                // If request is not authenticated, and a controller with the
                // SuperOfficeAuthorize attribute is accessed, the called controller
                // will continue to send the user to SuperID. If already authenticated there
                // this user will always return here and be stuck in an endless loop.
                // Therefore, it is important to authenticate with NetServer, and allow the
                // context provider to store the current session. Thus, the SuperOfficeAuthorize
                // attibute will be able to locate the session and proceed unimpeded

                //Authenticate with NetServer using web services if necessary.

                /*
                 * //    From Jens on DevNet:
                 * //    The SuperOffice.Configuration.ConfigFile.WebServices.RemoteBaseUrl is the value actually being used by the proxy to communicate with the server.
                 * //    This value is read from SuperOffice.Configuration.ConfigFile.Services.RemoteBaseUrl if it is not defined.
                 * //    The values of SuperOffice.Configuration.ConfigFile.Services are stored in a value that is static throughout the NetServer process,
                 * //    and shared between tenants in a multi-tenant configuration.
                 * //    The values SuperOffice.Configuration.ConfigFile.WebServices are tenant specific configuration values.
                 * //    */
                //}

                SoSession session = null;

                if (string.IsNullOrEmpty(context.AccessToken))
                {
                    session = SoSession.Authenticate(new SoCredentials()
                    {
                        Ticket = context.Ticket
                    });
                }
                else
                {
                    session = SoSession.Authenticate(new SoAccessTokenSecurityToken(context.AccessToken));
                }

                var principal = SoContext.CurrentPrincipal;
                OverrideContextIdentifier(principal, context.ContextIdentifier);
                var contact = new ContactAgent().GetContact(principal.ContactId);

                context.Company     = contact.FullName;
                context.Name        = principal.FullName;
                context.Username    = principal.Associate;
                context.AssociateId = principal.AssociateId;

                errorReason = String.Empty;

                return(true);
            }
            catch (Exception ex)
            {
                while (ex.InnerException != null)
                {
                    ex = ex.InnerException;
                }

                errorReason = ex.Message;
                SuperOfficeAuthHelper.Logout();
                return(false);
            }
        }