How to authenticate with twitter from a firefox plugin - twitter

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

Related

IMAP OAuth2 with Chilkat

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();
}
}

Thinktecture Identity Server with OAuth Implicit Login

I am trying to use javascript to login to identity server in OAuths Client. I can login and return to the return webpage successful.
I met a problem is why the Thinktecture identity sevrer always return '#' not '?' before parameters in querystring ,is that a bug?
the other question is how can I get the uses claims when I have access_token?
Implicit flow uses a hash fragment not a query string - that is not a bug (check the OAuth2 spec).
The client does not consume the access token in OAuth2 - it is opaque to the client - the access token is meant to be used by the backend.
I write a javascript function with regular expression to extract the "access_token" parameter as below:
var _access_token = getParameterByName('access_token');
function getParameterByName(name) {
var str = location.hash.substring(1);
var patt1 = new RegExp(name + "\\s*=\\s*([^&]+)", "g");
var result = patt1.exec(str);;
if (result == null)
return "";
else
return result[1];
}
then I try to use base64 to decode the access_token to get to extract the claims:
var Claims = base64decode(access_token)
function base64decode(input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64._utf8_decode(output);
return output;
}

Retrieving OAuth Verification Code via .NET HttpWebRequest

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.

Create a POST body using Google Apps Script

I'm trying to create a Google Apps Script that adds a new owner to the user's Google calendar. The first code block below works correctly (returns the calendar ACL in JSON format). How can I add a new user to the acl using Google Apps Script? The second code block shows my attempt to insert a new rule into the acl.
function getCalendarACL() {
// Get Calendar ID, script user's email, and the API Key for access to Calendar API
var calId = 'abc123#group.calendar.google.com';
var userEmail = Session.getActiveUser().getEmail();
var API_KEY = '012345abc123';
// Get authorization to access the Google Calendar API
var apiName = 'calendar';
var scope = 'https://www.googleapis.com/auth/calendar';
var fetchArgs = googleOAuth_(apiName, scope);
// Get the authorization information and the given calendar
fetchArgs.method = 'GET';
// Get the requested content (the ACL for the calendar)
var base = 'https://www.googleapis.com/calendar/v3/calendars/';
var url = base + calId + '/acl?key=' + API_KEY;
var content = UrlFetchApp.fetch(url, fetchArgs).getContentText();
Logger.log(content);
}
function googleOAuth_(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey("anonymous");
oAuthConfig.setConsumerSecret("anonymous");
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
Here's the second code block that returns server error 400 ("parse error"):
function insertRule() {
// Get Calendar ID, script user's email, and the API Key for access to Calendar API
var calId = 'abc123#group.calendar.google.com';
var userEmail = Session.getActiveUser().getEmail();
var API_KEY = '012345abc123';
var newUserEmail = 'person#gmail.com';
// Get authorization to access the Google Calendar API
var apiName = 'calendar';
var scope = 'https://www.googleapis.com/auth/calendar';
var fetchArgs = googleOAuth_(apiName, scope);
// Get the authorization information and the given calendar
fetchArgs.method = 'GET';
// Create the POST request body
var rawXML = "<entry xmlns='http://www.w3.org/2005/Atom' " +
"xmlns:gAcl='http://schemas.google.com/acl/2007'>" +
"<category scheme='http://schemas.google.com/g/2005#kind'" +
"term='http://schemas.google.com/acl/2007#accessRule'/>" +
"<gAcl:scope type='user' value='"+newUserEmail+"'></gAcl:scope>" +
"<gAcl:role='writer'>" +
"</gAcl:role>" +
"</entry>";
// Get the requested content (the ACL for the calendar)
var base = 'https://www.googleapis.com/calendar/v3/calendars/';
var url = base + calId + '/acl?key=' + API_KEY;
var content = UrlFetchApp.fetch(url, fetchArgs).getContentText();
Logger.log(content);
}
I would encode the newUserEmail variable like this encodeURIComponent(newUserEmail) when making the rawXML string and then retry.
You are using:
fetchArgs.method = 'GET';
but at the Acl:insert page found here says this:
HTTP Request
POST https://www.googleapis.com/calendar/v3/calendars/calendarId/acl
So, it should be,
fetchArgs.method = 'POST';

Issues with Crypto.generateMac() in SalesForce APEX

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'

Resources