I was looking for a way to Authenticate an IMAP session with google's Service account
But Since we already use Chilkat how do we do it, I found the following:
http://www.cknotes.com/imap-authentication-using-oauth/
allowing me to send a raw command:
imap.SendRawCommand("AUTHENTICATE XOAUTH <base64_data>");
This shows how to strucure the command:
https://developers.google.com/gmail/xoauth2_protocol
But having trouble putting it all together.
limilabs puts things together nicely in this example:
http://www.limilabs.com/blog/oauth2-gmail-imap-service-account
They have a neat imap.LoginOAUTH2(userEmail, credential.Token.AccessToken); that wraps things up into a command. How do I do this as a raw command for Chilkat?
const string serviceAccountEmail = "service-account-xxxxxx#developer.gserviceaccount.com";
const string serviceAccountCertPath = #"service-xxxxxx.p12";
const string serviceAccountCertPassword = "notasecret";
const string userEmail = "user#domain.com";
X509Certificate2 certificate = new X509Certificate2(
serviceAccountCertPath,
serviceAccountCertPassword,
X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://mail.google.com/" },
User = userEmail
}.FromCertificate(certificate));
bool success = credential.RequestAccessTokenAsync(CancellationToken.None).Result;
using (Chilkat.Imap imap = new Chilkat.Imap())
{
imap.UnlockComponent("unlock-code");
imap.Ssl = true;
imap.Port = 993;
imap.Connect("imap.gmail.com");
var authString = String.Format("user={0}" + "\x001" + "auth=Bearer {1}" + "\x001" + "\x001",userEmail, credential.Token.AccessToken);
var encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString));
string response = imap.SendRawCommand("AUTHENTICATE XOAUTH2 " + encoded);
imap.SelectMailbox("Inbox");
bool bUid;
bUid = false;
string mimeStr;
int i;
int n;
n = imap.NumMessages;
for (i = 1; i <= n; i++)
{
// Download the email by sequence number.
mimeStr = imap.FetchSingleAsMime(i, bUid);
Chilkat.Email chilkatEmail = new Chilkat.Email();
chilkatEmail.SetFromMimeText(mimeStr);
Console.WriteLine(chilkatEmail.Subject);
}
imap.CloseMailbox("Inbox");
Console.ReadLine();
}
}
Related
Echofon abandoned their firefox twitter plugin around April 2013, but it's been maintained on github until some recent changes to the twitter API broke it.
In normal use, authentication should follow PIN-based authentication, but instead the request to https://api.twitter.com/oauth/request_token is returning "{"errors":[{"code":32,"message":"Could not authenticate you."}]}'" status='401'
I think the problem is in the TwitterClient.buildOAuthHeader function
TwitterClient.buildOAuthHeader = function (user, method, url, param)
{
var ts = Math.ceil(Date.now() / 1000);
var diff = EchofonUtils.timestampDiff();
if (diff != 0) {
EchofonUtils.debug("local timestamp " + ts + " / server timetsamp " + (ts + diff));
ts += diff;
}
var converter = Cc["#mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
var result = {};
var data = converter.convertToByteArray(user + Date.now() + url + Math.random(), result);
var ch = Cc["#mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
ch.init(ch.MD5);
ch.update(data, data.length);
var hash = ch.finish(false);
var s = convertToHexString(hash);
var oauthparam = {"oauth_consumer_key" : OAUTH_CONSUMER_KEY,
"oauth_timestamp" : ts,
"oauth_signature_method" : "HMAC-SHA1",
"oauth_nonce" : s + Math.random(),
"oauth_version" : "1.0"};
if (user.oauth_token) {
oauthparam["oauth_token"] = EchofonAccountManager.instance().get(user.user_id).oauth_token;
}
var dict = {};
for (var key in param) dict[key] = param[key];
for (var key in oauthparam) dict[key] = oauthparam[key];
var paramStr = encodeURLParameter(dict);
var base = [method, RFCEncoding(url), RFCEncoding(paramStr)].join("&");
var signature;
var secret = user.oauth_token_secret ? EchofonAccountManager.instance().get(user.user_id).oauth_token_secret : "";
var signature = EchofonSign.OAuthSignature(base, secret);
oauthparam['oauth_signature'] = signature;
var headers = [];
for (var key in oauthparam) {
headers.push(key + '="' + RFCEncoding(oauthparam[key]) + '"');
}
headers.sort();
return headers.join(",");
}
I've registered a new application at dev.twitter.com and I'm using the consumer key from that instead of the one in the repository.
Also, I've added the oauth_callback attribute to the oauthparam object, with the value set to "oob" as detailed in the PIN-based authentication link above, but the plugin is not authenticating correctly with the API.
What needs to be changed in the authorization header to correct this?
This issue has been resolved.
Instructions on how to install a patched version of the plugin here - https://github.com/echofox-team/echofon-firefox-unofficial/issues/85#issuecomment-581843812
We have a program that sends an email for approval and extracting the reply using SSE.
In local computer the program is working, no error encountered. However when the program is published in our server we're experiencing some SockectException with the native error code 10060
Error Message
We're using SSE to check all the inbox using this code
public class MailRepository
{
private readonly string mailServer, login, password;
private readonly int port;
private readonly bool ssl;
public MailRepository(string mailServer, int port, bool ssl, string login, string password)
{
this.mailServer = mailServer;
this.port = port;
this.ssl = ssl;
this.login = login;
this.password = password;
}
public IEnumerable<IMessage> GetAllMails()
{
var messages = new List<IMessage>();
using (var client = new ImapClient())
{
client.Connect(mailServer, port, ssl);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
client.AuthenticationMechanisms.Remove("XOAUTH2");
client.Authenticate(login, password);
// The Inbox folder is always available on all IMAP servers...
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadOnly);
var results = inbox.Search(SearchOptions.All, SearchQuery.All);
foreach (var uniqueId in results.UniqueIds)
{
var message = inbox.GetMessage(uniqueId);
var sender = message.From.FirstOrDefault();
messages.Add(new IMessage() {
htmlBody = message.HtmlBody,
body = message.TextBody,
subject = message.Subject,
sender = sender == null ? "": (sender as MailboxAddress).Address,
messageID = uniqueId
});
//Mark message as read
//inbox.AddFlags(uniqueId, MessageFlags.Seen, true);
}
client.Disconnect(true);
}
return messages;
}
}
Usage:
var mail = new MailRepository("imap.gmail.com", 993, true, ConfigurationManager.AppSettings["approvalEmail"], "passwordhere")
And the program also sends email
public string SendContract()
{
//var img = Regex.Match(Request["orderscontent"], #"data:image/(?<type>.+?),(?<data>.+)").Groups["data"].Value;
email mail = new email();
mail.server = new SmtpClient("smtp.gmail.com");
mail.server.Port = 587;
mail.server.EnableSsl = true;
mail.server.Credentials = new NetworkCredential(ConfigurationManager.AppSettings["approvalEmail"], "passwordhere");
//mail.server.Timeout = 10000;
mail.From = new MailAddress(ConfigurationManager.AppSettings["approvalEmail"]);
mail.To.Add(Request["email"]);
mail.Subject = "DO Copy";
mail.IsBodyHtml = true;
mail.Attachments.Add(
new Attachment(
new MemoryStream(Convert.FromBase64String(Request["orderscontent"])),
string.Format("DO_copy_{0:MMddyyyyHHmm}.pdf", DateTime.Now)
)
);
mail.Send();
return "Sent!";
}
Both of the program return Error Message. But we notice when we sign in the server the program is working. But when the server is signed out it returns an error, So im not sure if the problem is from the server or the code.
I trying get an access token from Dropbox Sync & Datastore xamarin component in Xamarin iOS project.I have used dropnet to get the access token but I'm getting an http exception :( let me know where I'm doing wrong
private static void GetAccessToken()
{
try
{
const string DropboxSyncKey = "XXXXXXXX";
const string DropboxSyncSecret = "XXXXXX";
//this url is obtained fromDBAccountManager.SharedManager.HandleOpenURL(url);
string token = "db-XXXXXX://1/connect?oauth_token_secret=XXXXXXX&state=xXXXXXXX&uid=XXXXXX&oauth_token=xXXXXXXXX";
token = token.Substring(token.LastIndexOf("&") + 13);
string stoken = "db-XXXXXX://1/connect?oauth_token_secret=XXXXXXX&state=xXXXXXXX&uid=XXXXXX&oauth_token=xXXXXXXXX";
int indexof = stoken.IndexOf("?") + 20;
int lastindex = stoken.IndexOf("&") - indexof;
string secrettoken = stoken.Substring(indexof, lastindex);
var client = new DropNetClient(DropboxSyncKey, DropboxSyncSecret, token, secrettoken);
var accesskey = client.GetAccessToken();
Console.WriteLine(token);
Console.WriteLine(secrettoken);
Console.WriteLine(accesskey);
}
catch (Exception)
{
}
}
I'm attempting to replicate the OAuth steps normally done via the "Connect to QuickBooks" button using HttpWebRequest/HttpWebResponse.
It's easy at first grabbing the request token and generating the authorization link:
private const string oauthBaseUrl = "https://oauth.intuit.com/oauth/v1";
private const string urlRequestToken = "/get_request_token";
private const string urlAccessToken = "/get_access_token";
private const string verifyUrl = "https://appcenter.intuit.com";
private const string authorizeUrl = "https://appcenter.intuit.com/Connect/Begin";
...
var consumerContext = new OAuthConsumerContext
{
ConsumerKey = System.Utilities.Cryptography.Encryption.ConvertToUnsecureString(ckSS),
ConsumerSecret = System.Utilities.Cryptography.Encryption.ConvertToUnsecureString(csSS),
SignatureMethod = SignatureMethod.HmacSha1
};
IOAuthSession session = new OAuthSession(consumerContext, oauthBaseUrl + urlRequestToken, authorizeUrl, oauthBaseUrl + urlAccessToken);
IToken requestToken = session.GetRequestToken();
string authorizationLink = session.GetUserAuthorizationUrlForToken(requestToken, callbackUrl);
Then I walk through grabbing the request verification code that is generated in the set-cookie string when requesting the site at the authorization link:
var requestAuth = (HttpWebRequest) WebRequest.Create(authorizationLink);
requestAuth.Method = "GET";
requestAuth.ContentType = "application/x-www-form-urlencoded";
requestAuth.Accept = "text/html, application/xhtml+xml, */*";
requestAuth.Headers.Add("Accept-Encoding", "gzip, deflate");
requestAuth.Headers.Add("Accept-Language", "en-us");
requestAuth.Host = "appcenter.intuit.com";
requestAuth.KeepAlive = true;
var responseAuth = (HttpWebResponse) requestAuth.GetResponse();
Stream answerAuth = responseAuth.GetResponseStream();
var _answerAuth = new StreamReader(answerAuth);
string htmlAuth = _answerAuth.ReadToEnd();
// Need to grab the request verification code embedded in the set-cookie string
string cookies = responseAuth.Headers.Get("Set-Cookie");
int idx = cookies.IndexOf("__RequestVerificationToken", StringComparison.Ordinal);
if (idx > 0)
{
int startIndex = cookies.IndexOf("=", idx, StringComparison.InvariantCultureIgnoreCase);
int endIndex = cookies.IndexOf(";", startIndex + 1, StringComparison.InvariantCultureIgnoreCase);
requestVerificationCode = cookies.Substring(startIndex + 1, endIndex - (startIndex + 1));
postDataString += requestVerificationCode;
}
As I understand it, the request verification code is needed in order to get the OAuth verification code that is returned in the postdata appended to the callback URL, which is in turn needed to get the access token.
This is where the difficulty begins. Using Fiddler2, I find that the login URL for generating the OAuth verification code is https://appcenter.intuit.com/Account/LogOnJson. But no matter how much I try to replicate the HTTP POST using HttpWebRequest, all I get in return is a 500 error. I'm wondering if anyone has a working example of this step? Is this even possible? I hope so, because the alternative of pulling up IE and walking through the same steps like a macro is too ugly.
Any help on this? Thanks!
You can download the dotnet sample app for understanding how the OAUTH flow works:
https://github.com/IntuitDeveloperRelations/IPP_Sample_Code
Set your app keys in web.config.
We need to make a few callouts to a service that is using OAuth 1.0 and requires each request to be signed with HMAC-SHA1.
The service doesn't have any APEX client API. Thus, we have to do it manually.
Unfortunately,
EncodingUtil.base64Encode(Crypto.generateMac('hmacSHA1', Blob.valueOf(data), Blob.valueOf(key)));
returns a different string from what we expect. We have compared the output for the same input with libraries for other languages. And the output was different.
I have no problems calling out to OAuth 1.0. Here's some sample Apex for signing your request:
EDIT: Added additional code
private Map<String,String> getUrlParams(String value)
{
Map<String,String> res = new Map<String,String>();
if(value==null || value=='')
{
return res;
}
for(String s : value.split('&'))
{
List<String> kv = s.split('=');
if(kv.size()>1)
{
res.put(kv[0],kv[1]);
}
}
return res;
}
private String createBaseString(Map<String,String> oauthParams, HttpRequest req)
{
Map<String,String> p = oauthParams.clone();
if(req.getMethod().equalsIgnoreCase('post') && req.getBody()!=null && req.getHeader('Content-Type')=='application/x-www-form-urlencoded')
p.putAll(getUrlParams(req.getBody()));
String host = req.getEndpoint();
Integer n = host.indexOf('?');
if(n > -1)
{
p.putAll(getUrlParams(host.substring(n+1)));
host = host.substring(0,n);
}
List<String> keys = new List<String>();
keys.addAll(p.keySet());
keys.sort();
String s = keys.get(0)+'='+p.get(keys.get(0));
for(Integer i=1; i<keys.size(); i++)
s = s + '&' + keys.get(i) + '=' + p.get(keys.get(i));
return req.getMethod().toUpperCase() + '&' + EncodingUtil.urlEncode(host, 'UTF-8') + '&' + EncodingUtil.urlEncode(s, 'UTF-8');
}
public void sign(HttpRequest req)
{
nonce = String.valueOf(Crypto.getRandomLong());
timestamp = String.valueOf(DateTime.now().getTime() / 1000);
refreshParameters();
String s = createBaseString(parameters, req);
Blob sig = Crypto.generateMac('HmacSHA1', Blob.valueOf(s),
Blob.valueOf(consumerSecret+'&'+ (tokenSecret!=null ? tokenSecret : '')));
signature = EncodingUtil.urlEncode(EncodingUtil.base64encode(sig), 'UTF-8');
String header = 'OAuth ';
for (String key : parameters.keySet())
{
header = header + key + '="'+parameters.get(key)+'", ';
}
header = header + 'oauth_signature="'+signature+'"';
req.setHeader('Authorization',header);
}
This might be reaching, but could there be a case-sensitivity issue? Notice I'm calling 'HmacSHA1' not 'hmacSHA1'