Skip to content

Instantly share code, notes, and snippets.

@acamino
Created November 5, 2015 01:49
Show Gist options
  • Save acamino/90d6638a1e03db2cd4e9 to your computer and use it in GitHub Desktop.
Save acamino/90d6638a1e03db2cd4e9 to your computer and use it in GitHub Desktop.
OneTouch Support
using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using Authy.Net;
using Newtonsoft.Json;
namespace Authy.Net
{
public class OneTouchClient
{
private readonly string _apiKey;
private readonly string _userId;
private readonly bool _useSandbox;
/// <summary>
/// Creates a new instance of the OneTouch client
/// </summary>
/// <param name="apiKey">The api key used to access the rest api</param>
/// <param name="userId">The user id used to send approval request</param>
public OneTouchClient(string apiKey, string userId, bool useSandbox = false)
{
_apiKey = apiKey;
_userId = userId;
_useSandbox = useSandbox
}
/// <summary>
/// Send Approval Request to Authy
/// </summary>
/// <param name="message">Message to display in the device</param>
/// <param name="details">Details that will be shown to the user</param>
public SendApprovalRequestResult SendApprovalRequest(string message, NameValueCollection details)
{
var url = string.Format("{0}/onetouch/json/users/{1}/approval_requests?api_key={2}&message={3}",
BaseUrl, _userId, _apiKey, message);
return Execute(client =>
{
var response = client.UploadValues(url, request);
var textResponse = Encoding.ASCII.GetString(response);
var apiResponse = JsonConvert.DeserializeObject<SendApprovalRequestResult>(textResponse);
apiResponse.RawResponse = textResponse;
apiResponse.Status = AuthyStatus.Success;
return apiResponse;
});
}
private TResult Execute<TResult>(Func<WebClient, TResult> execute)
where TResult : AuthyResult, new()
{
var client = new WebClient();
var libraryVersion = AuthyHelpers.GetVersion();
var runtimeVersion = AuthyHelpers.GetSystemInfo();
var userAgent = string.Format("AuthyNet/{0} ({1})", libraryVersion, runtimeVersion);
// Set a custom user agent
client.Headers.Add("user-agent", userAgent);
try
{
return execute(client);
}
catch (WebException webex)
{
var response = webex.Response.GetResponseStream();
string body;
using (var reader = new StreamReader(response))
{
body = reader.ReadToEnd();
}
TResult result = JsonConvert.DeserializeObject<TResult>(body);
switch (((HttpWebResponse)webex.Response).StatusCode)
{
case HttpStatusCode.ServiceUnavailable:
result.Status = AuthyStatus.ServiceUnavailable;
break;
case HttpStatusCode.Unauthorized:
result.Status = AuthyStatus.Unauthorized;
break;
default:
case HttpStatusCode.BadRequest:
result.Status = AuthyStatus.BadRequest;
break;
}
return result;
}
finally
{
client.Dispose();
}
}
private static string BaseUrl
{
get { return _useSandbox ? "http://sandbox-api.authy.com" : "https://api.authy.com"; }
}
}
}
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using WebGrease.Css.Extensions;
namespace Authy.Net
{
public class OneTouchRequestValidator
{
private readonly string _apiKey;
private readonly HttpRequestBase _request;
public OneTouchRequestValidator(string apiKey, HttpRequestBase request)
{
_apiKey = apiKey;
_request = request;
}
public bool Validate()
{
var nonce = _request.Headers["X-Authy-Signature-Nonce"];
var url = string.Format("{0}://{1}{2}",
_request.Url.Scheme, _request.Headers["X-Original-Host"], _request.Url.AbsolutePath);
var serialized = Serialize(Sort(Parameters)).Trim('&');
var data = string.Format("{0}|{1}|{2}|{3}",
nonce, _request.HttpMethod, url, serialized);
var digest = ComputeDigest(data, _apiKey);
var authySignature = _request.Headers["X-Authy-Signature"];
return digest == authySignature;
}
private JObject Parameters
{
get
{
_request.InputStream.Position = 0;
return (JObject) JsonConvert.DeserializeObject(new StreamReader(_request.InputStream).ReadToEnd());
}
}
private static JObject Sort(JObject content)
{
var result = new JObject();
var properties = content.Properties().OrderBy(property => property.Name);
properties.ForEach(property =>
{
var propertyValue = property.Value as JObject;
if (propertyValue != null)
{
result.Add(property.Name, Sort(propertyValue));
}
else
{
result.Add(property);
}
});
return result;
}
private static string Serialize(JObject content)
{
var result = new StringBuilder();
var properties = content.Properties();
properties.ForEach(property =>
{
var propertyValue = property.Value as JObject;
if (propertyValue != null)
{
result.Append(Serialize(propertyValue));
}
else
{
result.Append(string.Format("{0}={1}&",
FormatPath(property.Path), Encode(property.Value.ToString())));
}
});
return result.ToString();
}
private static string FormatPath(string property)
{
var pathComponents = property.Split('.');
var head = pathComponents[0];
if (pathComponents.Length == 1)
{
return head;
}
var tail = pathComponents
.Skip(1)
.Select(component => string.Format("%5B{0}%5D", component));
return string.Format("{0}{1}", head, string.Join("", tail));
}
private static string ComputeDigest(string message, string secret)
{
var encoding = new UTF8Encoding();
using (var hmacsha256 = new HMACSHA256(encoding.GetBytes(secret)))
{
var hashedMessage = hmacsha256.ComputeHash(encoding.GetBytes(message));
return Convert.ToBase64String(hashedMessage);
}
}
private static string Encode(string content)
{
return content
.Replace("@", "%40")
.Replace("=", "%3D")
.Replace("/", "%2F")
.Replace("+", "%2B")
.Replace(" ", "+")
.Replace("False", "false");
}
}
}
using System.Collections.Generic;
using Authy.Net;
using Newtonsoft.Json;
namespace Authy.Net
{
public class SendApprovalRequestResult : AuthyResult
{
[JsonProperty("approval_request")]
public IDictionary<string, string> ApprovalRequest { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment