diff --git a/Disco.Services/Interop/DiscoServices/ActivationService.cs b/Disco.Services/Interop/DiscoServices/ActivationService.cs index f96277aa..cfcda7ab 100644 --- a/Disco.Services/Interop/DiscoServices/ActivationService.cs +++ b/Disco.Services/Interop/DiscoServices/ActivationService.cs @@ -42,6 +42,24 @@ namespace Disco.Services.Interop.DiscoServices public Uri GetCallbackUrl() => new Uri(DiscoServiceHelpers.ActivationServiceUrl, "/api/callback"); + public string CalculateCallbackProof(Guid correlationId, string userId, long timestamp) + { + var deploymentId = Guid.Parse(database.DiscoConfiguration.DeploymentId); + var secret = Guid.Parse(database.DiscoConfiguration.DeploymentSecret); + using (var hmac = new HMACSHA256(secret.ToByteArray())) + { + var data = new MemoryStream(); + data.Write(deploymentId.ToByteArray(), 0, 16); + data.Write(correlationId.ToByteArray(), 0, 16); + var userIdBytes = Encoding.UTF8.GetBytes(userId); + data.Write(BitConverter.GetBytes(userIdBytes.Length), 0, 4); + data.Write(userIdBytes, 0, userIdBytes.Length); + data.Write(BitConverter.GetBytes(timestamp), 0, 8); + var hash = hmac.ComputeHash(data.ToArray()); + return Convert.ToBase64String(hash).TrimEnd('=').Replace('+', '-').Replace('/', '_'); + } + } + /// /// Begin the activation process /// diff --git a/Disco.Services/Interop/DiscoServices/OnlineServicesConnect.cs b/Disco.Services/Interop/DiscoServices/OnlineServicesConnect.cs index 6e0ec56f..94054ac4 100644 --- a/Disco.Services/Interop/DiscoServices/OnlineServicesConnect.cs +++ b/Disco.Services/Interop/DiscoServices/OnlineServicesConnect.cs @@ -43,7 +43,10 @@ namespace Disco.Services.Interop.DiscoServices connection.Closed += ex => { - SystemLog.LogException("Online Services: Connection Closed", ex); + if (ex != null) + SystemLog.LogException("Online Services: Connection Closed", ex); + else + SystemLog.LogInformation("Online Services: Connection Closed"); return Task.CompletedTask; }; connection.Reconnected += connectionId => @@ -53,7 +56,11 @@ namespace Disco.Services.Interop.DiscoServices }; connection.Reconnecting += ex => { - SystemLog.LogInformation("Online Services: Connection Reconnecting"); + if (ex != null) + SystemLog.LogException("Online Services: Connection Reconnecting", ex); + else + SystemLog.LogInformation("Online Services: Connection Reconnecting"); + return Task.CompletedTask; }; } diff --git a/Disco.Web/Areas/API/Controllers/ActivationController.cs b/Disco.Web/Areas/API/Controllers/ActivationController.cs index aa1d3ea4..e94106c9 100644 --- a/Disco.Web/Areas/API/Controllers/ActivationController.cs +++ b/Disco.Web/Areas/API/Controllers/ActivationController.cs @@ -12,10 +12,22 @@ namespace Disco.Web.Areas.API.Controllers [DiscoAuthorize(Claims.DiscoAdminAccount)] public partial class ActivationController : AuthorizedDatabaseController { - [HttpPost] - public virtual ActionResult TestCallback(CallbackModel model) + [HttpGet] + public virtual async Task Begin(CallbackModel model) { - return this.PrecompiledPartialView(model); + // validate timestamp + var thresholdStart = DateTimeOffset.UtcNow.AddSeconds(-20).ToUnixTimeMilliseconds(); + var thresholdEnd = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + if (model.Timestamp < thresholdStart || model.Timestamp > thresholdEnd) + return new HttpStatusCodeResult(400, "Invalid timestamp"); + + // validate proof + var service = new ActivationService(Database); + var expectedProof = service.CalculateCallbackProof(model.CorrelationId, model.UserId, model.Timestamp); + if (model.Proof != expectedProof) + return new HttpStatusCodeResult(400, "Invalid proof"); + + return await Begin(); } [HttpPost, ValidateAntiForgeryToken] @@ -33,7 +45,7 @@ namespace Disco.Web.Areas.API.Controllers RedirectUrl = challengeModel.RedirectUrl }; - return View(model); + return View(MVC.API.Activation.Views.Begin, model); } [HttpGet] diff --git a/Disco.Web/Areas/API/Models/Activation/CallbackModel.cs b/Disco.Web/Areas/API/Models/Activation/CallbackModel.cs index 1d5d5105..9fa7c2f7 100644 --- a/Disco.Web/Areas/API/Models/Activation/CallbackModel.cs +++ b/Disco.Web/Areas/API/Models/Activation/CallbackModel.cs @@ -5,9 +5,12 @@ namespace Disco.Web.Areas.API.Models.Activation { public class CallbackModel { + public string Origin { get; set; } public Guid DeploymentId { get; set; } public Guid CorrelationId { get; set; } [StringLength(50)] public string UserId { get; set; } + public long Timestamp { get; set; } + public string Proof { get; set; } } } diff --git a/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.cshtml b/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.cshtml deleted file mode 100644 index 7512fdb3..00000000 --- a/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.cshtml +++ /dev/null @@ -1,17 +0,0 @@ -@model Disco.Web.Areas.API.Models.Activation.CallbackModel -@{ - Layout = null; -} - - - - - - - - diff --git a/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.generated.cs b/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.generated.cs deleted file mode 100644 index dcf94d58..00000000 --- a/Disco.Web/Areas/API/Views/Activation/_ActivateCallback.generated.cs +++ /dev/null @@ -1,107 +0,0 @@ -#pragma warning disable 1591 -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Disco.Web.Areas.API.Views.Activation -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Net; - using System.Text; - using System.Web; - using System.Web.Helpers; - using System.Web.Mvc; - using System.Web.Mvc.Ajax; - using System.Web.Mvc.Html; - using System.Web.Routing; - using System.Web.Security; - using System.Web.UI; - using System.Web.WebPages; - using Disco; - using Disco.Models.Repository; - using Disco.Services; - using Disco.Services.Authorization; - using Disco.Services.Web; - using Disco.Web; - using Disco.Web.Extensions; - - [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorGenerator", "2.0.0.0")] - [System.Web.WebPages.PageVirtualPathAttribute("~/Areas/API/Views/Activation/_ActivateCallback.cshtml")] - public partial class _ActivateCallback : Disco.Services.Web.WebViewPage - { - public _ActivateCallback() - { - } - public override void Execute() - { - - #line 2 "..\..\Areas\API\Views\Activation\_ActivateCallback.cshtml" - - Layout = null; - - - #line default - #line hidden -WriteLiteral("\r\n\r\n\r\n\r\n\r\n