/
AuthProvider.cs
97 lines (86 loc) · 4.08 KB
/
AuthProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
namespace WebhookSampleBot.Models
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
public class AuthProvider
{
/// <summary>
/// A dictionary for storing signing keys. Here, the look up key is based on the value of the query parameter 'id'.
/// The signing keys must be valid 256 bit base64 encoded strings that are provided during custom bot registration in MS Teams client.
/// </summary>
private static readonly Dictionary<string, string> SigningKeyDictionary = new Dictionary<string, string>()
{
// Update HMAC generated by outgoing-webhook here
{"contoso", "" },
{"fabrikam", "" }
};
/// <summary>
/// Validates the specified authentication header value.
/// </summary>
/// <param name="authenticationHeaderValue">The authentication header value present on request.</param>
/// <param name="messageContent">Content of the HTTP message read as string.</param>
/// <param name="claimedSenderId">The claimed sender identifier.</param>
/// <returns>Response containing result of validation.</returns>
public static AuthResponse Validate(AuthenticationHeaderValue authenticationHeaderValue, string messageContent, string claimedSenderId)
{
if (string.IsNullOrEmpty(claimedSenderId))
{
return new AuthResponse(false, "Id not present on request.");
}
if (authenticationHeaderValue == null)
{
return new AuthResponse(false, "Authentication header not present on request.");
}
if (!string.Equals("HMAC", authenticationHeaderValue.Scheme))
{
return new AuthResponse(false, "Incorrect authorization header scheme.");
}
claimedSenderId = claimedSenderId.ToLower();
string signingKey = null;
if (!AuthProvider.SigningKeyDictionary.TryGetValue(claimedSenderId, out signingKey))
{
return new AuthResponse(false, string.Format("Signing key for {0} is not configured", claimedSenderId));
}
// Reject all empty messages
if (string.IsNullOrEmpty(messageContent))
{
return new AuthResponse(false, "Unable to validate authentication header for messages with empty body.");
}
string providedHmacValue = authenticationHeaderValue.Parameter;
string calculatedHmacValue = null;
try
{
byte[] serializedPayloadBytes = Encoding.UTF8.GetBytes(messageContent);
byte[] keyBytes = Convert.FromBase64String(signingKey);
using (HMACSHA256 hmacSHA256 = new HMACSHA256(keyBytes))
{
byte[] hashBytes = hmacSHA256.ComputeHash(serializedPayloadBytes);
calculatedHmacValue = Convert.ToBase64String(hashBytes);
}
if (string.Equals(providedHmacValue, calculatedHmacValue))
{
return new AuthResponse(true, null);
}
else
{
string errorMessage = string.Format(
"AuthHeaderValueMismatch. Expected:'{0}' Provided:'{1}'",
calculatedHmacValue,
providedHmacValue);
return new AuthResponse(false, errorMessage);
}
}
catch (Exception ex)
{
Trace.TraceError("Exception occcured while verifying HMAC on the incoming request. Exception: {0}", ex);
return new AuthResponse(false, "Exception thrown while verifying MAC on incoming request.");
}
}
}
}