Skip to content
Snippets Groups Projects
Commit b4660004 authored by Klaus Fischer's avatar Klaus Fischer
Browse files

Callback verification done

parent 7b4e2c7b
No related branches found
No related tags found
1 merge request!4Feature/563 check callback
......@@ -20,7 +20,6 @@ private_notes/
**.nupkg
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
......
......@@ -206,27 +206,34 @@ public class Subscriber : FitConnectClient,
Logger?.LogInformation("Submission completed {status}", result);
}
public static string VerifyCallback(string callbackSecret,
long timestamp, string body) {
if (timestamp < DateTime.Now.AddMinutes(-5).ToEpochTime())
throw new ArgumentException("Request is too old");
var hmac = new HMACSHA512(Encoding.UTF8.GetBytes(callbackSecret))
.ComputeHash(Encoding.UTF8.GetBytes($"{timestamp}.{body}"));
return Convert.ToHexString(hmac).ToLower();
}
public static bool VerifyCallback(string callbackSecret, HttpRequest request) {
if (!request.Headers.ContainsKey("callback-timestamp"))
throw new ArgumentException("Missing callback-timestamp header");
var timeStampString = request.Headers["callback-timestamp"].ToString();
var timestamp = long.Parse(timeStampString);
if (!long.TryParse(timeStampString, out var timestamp)) {
throw new ArgumentException("Invalid callback-timestamp header");
}
if (timestamp < DateTime.Now.AddMinutes(-5).ToEpochTime())
throw new ArgumentException("Request is too old");
var authentication = request.Headers["callback-authentication"];
using var requestStream = request.Body;
var content = new StreamReader(requestStream).ReadToEnd().Trim();
var hmac = new HMACSHA512(Encoding.UTF8.GetBytes(callbackSecret))
.ComputeHash(Encoding.UTF8.GetBytes($"{timeStampString}.{content}"));
var hmacString = Convert.ToHexString(hmac);
if (hmacString != authentication)
var result = VerifyCallback(callbackSecret, timestamp, content);
if (result != authentication)
throw new ArgumentException("Request is not authentic");
return true;
}
......
......@@ -49,7 +49,7 @@ public class CallbackTest {
// };
var headers = new HeaderDictionary(new Dictionary<string, StringValues>() {
{ "callback-timestamp", DateTime.Now.ToEpochTime().ToString() }, {
{ "callback-timestamp", "1672527599" }, {
"callback-authentication",
"798cd0edb70c08e5b32aa8a18cbbc8ff6b3078c51af6d011ff4e32e470c746234fc4314821fe5185264b029e962bd37de33f3b9fc5f1a93c40ce6672845e90df"
}
......@@ -68,10 +68,25 @@ public class CallbackTest {
_callbackSecret = MockContainer.Container.Create().Resolve<MockSettings>().CallbackSecret;
}
[Test]
public void ValidRequest_WithSingeValues() {
// Arrange
//Act
var authentication = FitConnect.Subscriber.VerifyCallback(_callbackSecret, 1672527599,
"{\"type\":\"https://schema.fitko.de/fit-connect/submission-api/callbacks/new-submissions\",\"submissionIds\":[\"f39ab143-d91a-474a-b69f-b00f1a1873c2\"]}"
);
// Assert
authentication.Should()
.Be(
"798cd0edb70c08e5b32aa8a18cbbc8ff6b3078c51af6d011ff4e32e470c746234fc4314821fe5185264b029e962bd37de33f3b9fc5f1a93c40ce6672845e90df");
}
[Test]
public void ValidRequest() {
// Assert
FitConnect.Subscriber.VerifyCallback(_callbackSecret,Request).Should().Be(true);
FitConnect.Subscriber.VerifyCallback(_callbackSecret, Request).Should().Be(true);
}
[Test]
......@@ -81,7 +96,9 @@ public class CallbackTest {
// Atc
// Assert
Assert.Throws<ArgumentException>(() => { FitConnect.Subscriber.VerifyCallback(_callbackSecret,Request); })
Assert.Throws<ArgumentException>(() => {
FitConnect.Subscriber.VerifyCallback(_callbackSecret, Request);
})
.Message.Should().Be("Request is too old");
}
......@@ -93,7 +110,9 @@ public class CallbackTest {
// Atc
// Assert
Assert.Throws<ArgumentException>(() => { FitConnect.Subscriber.VerifyCallback(_callbackSecret,Request); })
Assert.Throws<ArgumentException>(() => {
FitConnect.Subscriber.VerifyCallback(_callbackSecret, Request);
})
.Message.Should().Be("Request is not authentic");
}
}
......@@ -22,7 +22,7 @@ namespace MockContainer;
public record MockSettings(string PrivateKeyDecryption, string PrivateKeySigning,
string PublicKeyEncryption, string PublicKeySignatureVerification, string SenderClientId,
string SenderClientSecret, string SubscriberClientId, string SubscriberClientSecret,
string DestinationId, string CallbackSecret);
string DestinationId, string LeikaKey, string CallbackSecret);
public class TestFile {
public byte[] Content;
......@@ -74,20 +74,20 @@ public static class Container {
JsonConvert.DeserializeObject<dynamic>(
File.ReadAllText("./encryptionKeys/credentials.json"));
var senderClientId = (string) credentials.sender.clientId;
var senderClientId = (string)credentials.sender.clientId;
var senderClientSecret = (string)credentials.sender.clientSecret;
var subscriberClientId = (string)credentials.subscriber.clientId;
var subscriberClientSecret = (string)credentials.subscriber.clientSecret;
var destinationId = (string)credentials.destinationId;
var callbackSecret = (string)credentials.subscriber.callbackSecret;
var leikaKey = (string)credentials.leikaKey;
var callbackSecret = (string)credentials.callbackSecret;
builder.Register(c => new MockSettings(
privateKeyDecryption, privateKeySigning,
publicKeyEncryption, publicKeySignature,
senderClientId, senderClientSecret,
subscriberClientId, subscriberClientSecret,
destinationId,
callbackSecret))
destinationId, leikaKey, callbackSecret))
.As<MockSettings>();
builder.Register(c => new KeySet {
PrivateKeyDecryption = privateKeyDecryption,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment