Index: TFP-WebServer/WebServer/ModInfo.xml
===================================================================
--- TFP-WebServer/WebServer/ModInfo.xml	(revision 451)
+++ TFP-WebServer/WebServer/ModInfo.xml	(revision 453)
@@ -5,5 +5,5 @@
 	<Description value="Integrated Webserver for the Web Dashboard and server APIs" />
 	<Author value="The Fun Pimps LLC" />
-	<Version value="21.0.318.0" />
+	<Version value="21.1.9.0" />
 	<Website value="" />
 </xml>
Index: TFP-WebServer/WebServer/WebServer.csproj
===================================================================
--- TFP-WebServer/WebServer/WebServer.csproj	(revision 451)
+++ TFP-WebServer/WebServer/WebServer.csproj	(revision 453)
@@ -10,5 +10,5 @@
     <RootNamespace>Webserver</RootNamespace>
     <AssemblyName>WebServer</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
     <LangVersion>9</LangVersion>
   </PropertyGroup>
Index: TFP-WebServer/WebServer/src/UrlHandlers/ApiHandler.cs
===================================================================
--- TFP-WebServer/WebServer/src/UrlHandlers/ApiHandler.cs	(revision 451)
+++ TFP-WebServer/WebServer/src/UrlHandlers/ApiHandler.cs	(revision 453)
@@ -30,5 +30,6 @@
 
 		private void apiFoundCallback (Type _type) {
-			ConstructorInfo ctor = _type.GetConstructor (apiWithParentCtorTypes);
+			ConstructorInfo ctor = _type.GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, (Binder)null,
+				apiWithParentCtorTypes, (ParameterModifier[])null);
 			if (ctor != null) {
 				AbsWebAPI apiInstance = (AbsWebAPI)ctor.Invoke (apiWithParentCtorArgs);
@@ -37,5 +38,6 @@
 			}
 
-			ctor = _type.GetConstructor (apiEmptyCtorTypes);
+			ctor = _type.GetConstructor (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, (Binder)null,
+				apiEmptyCtorTypes, (ParameterModifier[])null);
 			if (ctor != null) {
 				AbsWebAPI apiInstance = (AbsWebAPI)ctor.Invoke (apiEmptyCtorArgs);
Index: TFP-WebServer/WebServer/src/UrlHandlers/SessionHandler.cs
===================================================================
--- TFP-WebServer/WebServer/src/UrlHandlers/SessionHandler.cs	(revision 451)
+++ TFP-WebServer/WebServer/src/UrlHandlers/SessionHandler.cs	(revision 453)
@@ -9,4 +9,5 @@
 namespace Webserver.UrlHandlers {
 	public class SessionHandler : AbsHandler {
+		
 		private const string pageBasePath = "/app";
 		private const string pageErrorPath = "/app/error/";
@@ -14,19 +15,16 @@
 		private const string steamOpenIdVerifyUrl = "verifysteamopenid";
 		private const string steamLoginUrl = "loginsteam";
+		private const string steamLoginName = "Steam OpenID";
 		private const string steamLoginFailedPage = "SteamLoginFailed";
 
 		private const string userPassLoginUrl = "login";
 		public const string userPassLoginName = "User/pass";
-		public const string userPassErrorPage = "UserPassLoginFailed";
+		private const string userPassErrorPage = "UserPassLoginFailed";
 
-		private readonly ConnectionHandler connectionHandler;
-
-		public SessionHandler (ConnectionHandler _connectionHandler) : base (null) {
-			connectionHandler = _connectionHandler;
+		public SessionHandler () : base (null) {
 		}
-
 		public override void HandleRequest (RequestContext _context) {
 			if (_context.Request.RemoteEndPoint == null) {
-				_context.Response.Redirect (pageErrorPath + "NoRemoteEndpoint");
+				WebUtils.WriteText (_context.Response, "NoRemoteEndpoint", HttpStatusCode.BadRequest);
 				return;
 			}
@@ -37,30 +35,34 @@
 
 			if (subpath.StartsWith (steamOpenIdVerifyUrl)) {
-				HandleSteamVerification (_context, remoteEndpointString);
+				if (HandleSteamVerification (parent.ConnectionHandler, _context, remoteEndpointString)) {
+					_context.Response.Redirect (pageBasePath);
+				} else {
+					_context.Response.Redirect (pageErrorPath + steamLoginFailedPage);
+				}
 				return;
 			}
 
 			if (subpath.StartsWith ("logout")) {
-				HandleLogout (_context);
+				HandleLogout (parent.ConnectionHandler, _context, pageBasePath);
 				return;
 			}
 
 			if (subpath.StartsWith (steamLoginUrl)) {
-				HandleSteamLogin (_context);
+				HandleSteamLogin (_context, $"{urlBasePath}{steamOpenIdVerifyUrl}");
 				return;
 			}
 
 			if (subpath.StartsWith (userPassLoginUrl)) {
-				HandleUserPassLogin (_context, remoteEndpointString);
+				HandleUserPassLogin (parent.ConnectionHandler, _context, remoteEndpointString);
 				return;
 			}
 
-			_context.Response.Redirect (pageErrorPath + "InvalidSessionsCommand");
+			WebUtils.WriteText (_context.Response, "InvalidSessionsCommand", HttpStatusCode.BadRequest);
 		}
 
-		private void HandleUserPassLogin (RequestContext _context, string _remoteEndpointString) {
+		public static bool HandleUserPassLogin (ConnectionHandler _connectionHandler, RequestContext _context, string _remoteEndpointString) {
 			if (!_context.Request.HasEntityBody) {
 				WebUtils.WriteText (_context.Response, "NoLoginData", HttpStatusCode.BadRequest);
-				return;
+				return false;
 			}
 
@@ -77,33 +79,40 @@
 				Log.Exception (e);
 				WebUtils.WriteText (_context.Response, "InvalidLoginJson", HttpStatusCode.BadRequest);
-				return;
+				return false;
 			}
 
 			if (!inputJson.TryGetValue ("username", out object fieldNode) || fieldNode is not string username) {
 				WebUtils.WriteText (_context.Response, "InvalidLoginJson", HttpStatusCode.BadRequest);
-				return;
+				return false;
 			}
 
 			if (!inputJson.TryGetValue ("password", out fieldNode) || fieldNode is not string password) {
 				WebUtils.WriteText (_context.Response, "InvalidLoginJson", HttpStatusCode.BadRequest);
-				return;
+				return false;
 			}
 
 			if (!AdminWebUsers.Instance.TryGetUser (username, password, out AdminWebUsers.WebUser webUser)) {
+				WebUtils.WriteText (_context.Response, "UserPassInvalid", HttpStatusCode.Unauthorized);
 				Log.Out ($"[Web] User/pass login failed from {_remoteEndpointString}");
-				WebUtils.WriteText (_context.Response, "UserPassInvalid", HttpStatusCode.Unauthorized);
-				return;
+				return false;
 			}
 
-			HandleUserIdLogin (connectionHandler, _context, _remoteEndpointString, userPassLoginName, userPassErrorPage, webUser.Name, webUser.PlatformUser, webUser.CrossPlatformUser);
+			var loginResult = HandleUserIdLogin (_connectionHandler, _context, _remoteEndpointString, userPassLoginName, webUser.Name, webUser.PlatformUser, webUser.CrossPlatformUser);
+			if (loginResult) {
+				WebUtils.WriteText (_context.Response, "");
+			} else {
+				WebUtils.WriteText (_context.Response, "LoginError", HttpStatusCode.InternalServerError);
+			}
+
+			return loginResult;
 		}
 
-		private void HandleSteamLogin (RequestContext _context) {
+		public static void HandleSteamLogin (RequestContext _context, string _verificationCallbackUrl) {
 			string host = $"{(WebUtils.IsSslRedirected (_context.Request) ? "https://" : "http://")}{_context.Request.UserHostName}";
-			string url = OpenID.GetOpenIdLoginUrl (host, $"{host}{urlBasePath}{steamOpenIdVerifyUrl}");
+			string url = OpenID.GetOpenIdLoginUrl (host, $"{host}{_verificationCallbackUrl}");
 			_context.Response.Redirect (url);
 		}
 
-		private void HandleLogout (RequestContext _context) {
+		public static bool HandleLogout (ConnectionHandler _connectionHandler, RequestContext _context, string _pageBase) {
 			Cookie cookie = new Cookie ("sid", "", "/") {
 				Expired = true
@@ -112,13 +121,14 @@
 
 			if (_context.Connection == null) {
-				_context.Response.Redirect (pageErrorPath + "NotLoggedIn");
-				return;
+				_context.Response.Redirect (_pageBase);
+				return false;
 			}
 
-			connectionHandler.LogOut (_context.Connection.SessionID);
-			_context.Response.Redirect (pageBasePath);
+			_connectionHandler.LogOut (_context.Connection.SessionID);
+			_context.Response.Redirect (_pageBase);
+			return true;
 		}
 
-		private void HandleSteamVerification (RequestContext _context, string _remoteEndpointString) {
+		public static bool HandleSteamVerification (ConnectionHandler _connectionHandler, RequestContext _context, string _remoteEndpointString) {
 			ulong id;
 			try {
@@ -127,20 +137,18 @@
 				Log.Error ($"[Web] Error validating Steam login from {_remoteEndpointString}:");
 				Log.Exception (e);
-				_context.Response.Redirect (pageErrorPath + steamLoginFailedPage);
-				return;
+				return false;
 			}
 
 			if (id <= 0) {
 				Log.Out ($"[Web] Steam OpenID login failed (invalid ID) from {_remoteEndpointString}");
-				_context.Response.Redirect (pageErrorPath + steamLoginFailedPage);
-				return;
+				return false;
 			}
 
 			UserIdentifierSteam userId = new UserIdentifierSteam (id);
-			HandleUserIdLogin (connectionHandler, _context, _remoteEndpointString, "Steam OpenID", steamLoginFailedPage, userId.ToString (), userId);
+			return HandleUserIdLogin (_connectionHandler, _context, _remoteEndpointString, steamLoginName, userId.ToString (), userId);
 		}
 
-		public static void HandleUserIdLogin (ConnectionHandler _connectionHandler, RequestContext _context, string _remoteEndpointString,
-			string _loginName, string _errorPage, string _username, PlatformUserIdentifierAbs _userId, PlatformUserIdentifierAbs _crossUserId = null, bool _sendResponse = true) {
+		public static bool HandleUserIdLogin (ConnectionHandler _connectionHandler, RequestContext _context, string _remoteEndpointString,
+			string _loginName, string _username, PlatformUserIdentifierAbs _userId, PlatformUserIdentifierAbs _crossUserId = null) {
 			try {
 				WebConnection con = _connectionHandler.LogIn (_context.Request.RemoteEndPoint!.Address, _username, _userId, _crossUserId);
@@ -154,5 +162,5 @@
 				int higherLevel = Math.Min (level1, level2);
 
-				Log.Out ($"[Web] {_loginName} login from {_remoteEndpointString}, name {_username} with ID {_userId}, CID {(_crossUserId != null ? _crossUserId : "none")}, permission level {higherLevel}");
+				Log.Out ($"[Web] {_loginName} login from {_remoteEndpointString}, name {_username} with ID {_userId}, CID {(_crossUserId != null ? _crossUserId.ToString () : "none")}, permission level {higherLevel}");
 				Cookie cookie = new Cookie ("sid", con.SessionID, "/") {
 					Expired = false,
@@ -163,15 +171,13 @@
 				_context.Response.AppendCookie (cookie);
 
-				if (_sendResponse) {
-					WebUtils.WriteText (_context.Response, "");
-				}
+				return true;
 			} catch (Exception e) {
 				Log.Error ($"[Web] Error during {_loginName} login:");
 				Log.Exception (e);
-				if (_sendResponse) {
-					WebUtils.WriteText (_context.Response, "LoginError", HttpStatusCode.InternalServerError);
-				}
 			}
+
+			return false;
 		}
+
 	}
 }
Index: TFP-WebServer/WebServer/src/Web.cs
===================================================================
--- TFP-WebServer/WebServer/src/Web.cs	(revision 451)
+++ TFP-WebServer/WebServer/src/Web.cs	(revision 453)
@@ -85,5 +85,5 @@
 			RegisterWebMods (useCacheForStatic);
 
-			RegisterPathHandler ("/session/", new SessionHandler (ConnectionHandler));
+			RegisterPathHandler ("/session/", new SessionHandler ());
 			RegisterPathHandler ("/userstatus", new UserStatusHandler ());
 			RegisterPathHandler ("/sse/", new SseHandler ());
Index: TFP-WebServer/WebServer/src/WebAPI/APIs/Permissions/RegisterUser.cs
===================================================================
--- TFP-WebServer/WebServer/src/WebAPI/APIs/Permissions/RegisterUser.cs	(revision 451)
+++ TFP-WebServer/WebServer/src/WebAPI/APIs/Permissions/RegisterUser.cs	(revision 453)
@@ -110,5 +110,5 @@
 			string remoteEndpointString = _context.Request.RemoteEndPoint!.ToString ();
 			SessionHandler.HandleUserIdLogin (ParentWeb.ConnectionHandler, _context, remoteEndpointString, SessionHandler.userPassLoginName,
-				SessionHandler.userPassErrorPage, username, regData.PlatformUserId, regData.CrossPlatformUserId, false);
+				username, regData.PlatformUserId, regData.CrossPlatformUserId);
 
 			_context.Response.StatusCode = (int)HttpStatusCode.Created;
