public void PrepareRegistrationChallenge(Guid correlationId, string emailOrPhone, ContactChallengePurpose purpose) { if (ContactChallenges.ContainsKey(correlationId)) throw new CommandRejectedException(nameof(correlationId), correlationId, CommandRejectionReason.AlreadyApplied, $"Contact challenge for correlationId '{correlationId}' has already been prepared."); string stamp = Guid.NewGuid().ToString(); string code = ContactChallengers.TotpCodeProvider.Generate(Id, emailOrPhone, purpose, stamp); string token = ContactChallengers.DataProtectionTokenProvider.Generate(Id, purpose, stamp); var assembly = Assembly.GetExecutingAssembly(); string message = assembly.GetManifestResourceText(assembly.GetManifestResourceName($"{purpose}.Message.txt")).Replace("{Code}", code); ContactChallengePrepared eventToRaise; switch (purpose) { case ContactChallengePurpose.CreateUserFromEmail: string subject = assembly.GetManifestResourceText(assembly.GetManifestResourceName($"{purpose}.Subject.txt")); eventToRaise = new ContactEmailChallengePrepared(Id, DateTime.UtcNow, correlationId, emailOrPhone, purpose, stamp, token, subject, message); break; case ContactChallengePurpose.CreateUserFromPhone: var phoneNumber = ContactIdParser.AsPhoneNumber(emailOrPhone); eventToRaise = new ContactSmsChallengePrepared(Id, DateTime.UtcNow, correlationId, phoneNumber.NationalNumber, ContactIdParser.DefaultRegionCode, emailOrPhone, purpose, stamp, token, message); break; default: throw new CommandRejectedException(nameof(emailOrPhone), emailOrPhone, CommandRejectionReason.Unknown); } RaiseEvent(eventToRaise); }
public static string Generate(Guid userId, string contactValue, ContactChallengePurpose purpose, string stamp) { byte[] stampBytes = Encoding.Unicode.GetBytes(stamp); string modifier = $"Totp:{purpose}:{contactValue}:{userId}"; int code = Rfc6238AuthenticationService.GenerateCode(stampBytes, modifier); string totpCode = code.ToString("D6", CultureInfo.InvariantCulture); return totpCode; }
private bool ValidateCore(string token, Guid userId, ContactChallengePurpose purpose, string stamp) { try { byte[] unprotectedData = Protector.Unprotect(Convert.FromBase64String(token)); var ms = new MemoryStream(unprotectedData); using (var reader = new BinaryReader(ms, DefaultEncoding, true)) { DateTimeOffset creationTime = new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero); DateTimeOffset expirationTime = creationTime + TokenLifespan; if (expirationTime < DateTimeOffset.UtcNow) { return false; } string tokenUserId = reader.ReadString(); if (!string.Equals(tokenUserId, userId.ToString(), StringComparison.OrdinalIgnoreCase)) { return false; } var tokenPurpose = reader.ReadString(); if (!string.Equals(tokenPurpose, purpose.ToString())) { return false; } var tokenStamp = reader.ReadString(); if (reader.PeekChar() != -1) { return false; } bool isTokenValid = tokenStamp == stamp; return isTokenValid; } } catch { // do not leak exception return false; } }
public ContactEmailChallengePrepared(Guid aggregateId, DateTime happenedOn, Guid correlationId, string emailAddress, ContactChallengePurpose purpose, string stamp, string token, string messageSubject, string messageBody) : base(aggregateId, happenedOn, correlationId, purpose, stamp, token) { EmailAddress = emailAddress; MessageSubject = messageSubject; MessageBody = messageBody; }
protected ContactChallengePrepared(Guid aggregateId, DateTime happenedOn, Guid correlationId, ContactChallengePurpose purpose, string stamp, string token) : base(aggregateId, happenedOn) { CorrelationId = correlationId; Purpose = purpose; Stamp = stamp; Token = token; }
public static bool Validate(string totpCode, Guid userId, string contactValue, ContactChallengePurpose purpose, string stamp) { byte[] stampBytes = Encoding.Unicode.GetBytes(stamp); int code; if (!int.TryParse(totpCode, out code)) return false; string modifier = $"Totp:{purpose}:{contactValue}:{userId}"; bool isValid = Rfc6238AuthenticationService.ValidateCode(stampBytes, code, modifier); return isValid; }
public ContactSmsChallengePrepared(Guid aggregateId, DateTime happenedOn, Guid correlationId, ulong phoneNumber, string regionCode, string unformattedPhone, ContactChallengePurpose purpose, string stamp, string token, string message) : base(aggregateId, happenedOn, correlationId, purpose, stamp, token) { PhoneNumber = phoneNumber; RegionCode = regionCode; UnformattedPhone = unformattedPhone; Message = message; }
private string GenerateCore(Guid userId, ContactChallengePurpose purpose, string stamp) { MemoryStream memoryStream = new MemoryStream(); using (var writer = new BinaryWriter(memoryStream, DefaultEncoding, true)) { writer.Write(DateTimeOffset.UtcNow.Ticks); writer.Write(userId.ToString()); writer.Write(purpose.ToString()); writer.Write(stamp); } byte[] protectedBytes = Protector.Protect(memoryStream.ToArray()); string token = Convert.ToBase64String(protectedBytes); return token; }
internal static bool Validate(string token, Guid userId, ContactChallengePurpose purpose, string stamp) { return new DataProtectionTokenProvider().ValidateCore(token, userId, purpose, stamp); }
internal static string Generate(Guid userId, ContactChallengePurpose purpose, string stamp) { return new DataProtectionTokenProvider().GenerateCore(userId, purpose, stamp); }