Facebook graph API me endpoint errors out the first time and successful the next time - facebook-graph-api

I am using facebook login in my coldfusion application. I went through all the steps to get access_token. I am making cfhttp call to "/me" endpoint with access_token and fields parameters and getting "I/O Exception: peer not authenticated" error in the response/Errordetail. I made the same cfhttp call again and able to get successful response of id and name mentioned in fields parameters.
I am expecting the successful response in the first call but have to call twice, so the first call errors out and second one succeeds.
Any help on how to debug or find the issue with the first call. so i should be making one call and get a successful response of current user's information.
i should be making one call and get a successful response of current user's information
<cfscript>
if (code EQ "") {
cfoauth(type = 'facebook', clientid = appId, secretkey = appSecret, result = "res", scope = "public_profile, email", redirecturi = redirectUri);
}
if (code NEQ "") { // code is a parameter I get from url redirect after cfoauth response
try {
cfhttp(method = "GET", charset = "utf-8", url = "https://graph.facebook.com/v15.0/oauth/access_token", result = "fbcoderesponse") {
cfhttpparam(name = "code", type = "url", value = code);
cfhttpparam(name = "client_id", type = "url", value = appId);
cfhttpparam(name = "client_secret", type = "url", value = appSecret);
cfhttpparam(name = "redirect_uri", type = "url", value = redirectUri);
}
dresponse = deserializeJSON(fbcoderesponse.filecontent);
accessToken = dresponse.access_token;
writedump(accessToken);
accessTokenurl = "https://graph.facebook.com/v15.0/me?fields=id,name&access_token=" & accessToken;
writedump(accessTokenurl);
} catch (any e) {
writeoutput(e);
}
if (accessToken NEQ "") {
try {
cfhttp(method = "GET", charset = "utf-8", url = accessTokenurl, result = "fbaccesstokenresponse”){}
writedump(fbaccesstokenresponse);
if (fbaccesstokenresponse.errordetail != "") {
cfhttp(method = "GET", charset = "utf-8", url = accessTokenurl, result = "fbaccesstokenresponse1");
writeoutput("fbaccesstokenresponse1 response");
writedump(fbaccesstokenresponse1);
}
} catch (any e) {
writeoutput("exception of daccesstokenresponse");
writedump(e);
}
} else {
writeoutput("accesstoken is empty");
}
}
</cfscript>

Related

Google.Cloud.RecaptchaEnterprise error on CreateAssessment() - "Request contains an invalid argument"

I'm trying to use the Google.Cloud.RecaptchaEnterprise library to validate captcha requests for the new enterprise key my client has obtained.
string _siteKey = ConfigurationManager.AppSettings["GoogleCaptcha.CheckboxCaptcha.SiteKey"];
string _apiKey = ConfigurationManager.AppSettings["GoogleCaptcha.ApiKey"];
string _projectId = ConfigurationManager.AppSettings["GoogleCaptcha.ProjectId"];
string recaptchaAction = "CreateAccountAssessment";
try {
var appPath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath;
string credential_path = appPath + "googlecredentials.json";
System.Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", credential_path);
RecaptchaEnterpriseServiceClient client =
RecaptchaEnterpriseServiceClient.Create();
CreateAssessmentRequest createAssessmentRequest = new CreateAssessmentRequest()
{
Assessment = new Assessment()
{
Event = new Event()
{
SiteKey = _siteKey,
Token = formResponse,
ExpectedAction = "Create_Account"
},
Name = recaptchaAction,
},
Parent = _projectId
};
Assessment response = client.CreateAssessment(createAssessmentRequest);
if (response.TokenProperties.Valid == false)
{
Sitecore.Diagnostics.Log.Error("The CreateAssessment() call failed " +
"because the token was invalid for the following reason: " +
response.TokenProperties.InvalidReason.ToString(), this);
return "Invalid captcha.";
}
else
{
if (response.Event.ExpectedAction == recaptchaAction)
{
Sitecore.Diagnostics.Log.Error("The reCAPTCHA score for this token is: " +
response.RiskAnalysis.Score.ToString(), this);
return "";
}
else
{
Sitecore.Diagnostics.Log.Error("The action attribute in your reCAPTCHA " +
"tag does not match the action you are expecting to score", this);
return "Invalid captcha.";
}
}
}
catch (Exception ex)
{
Sitecore.Diagnostics.Log.Error("Error validating captcha on " + _url + "; " + ex.Message, this);
return "Unable to connect to captcha service.";
}
As far as I can tell all my properties are correct, but it throws an error on Assessment response = client.CreateAssessment(createAssessmentRequest);:
Status(StatusCode="InvalidArgument", Detail="Request contains an invalid argument.", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"#1621287236.280000000","description":"Error received from peer ipv6:[2607:f8b0:4006:81a::200a]:443","file":"T:\src\github\grpc\workspace_csharp_ext_windows_x64\src\core\lib\surface\call.cc","file_line":1062,"grpc_message":"Request contains an invalid argument.","grpc_status":3}")
I strongly suspect the problem (or at least a problem) is the Parent property of the request.
From the documentation:
The name of the project in which the assessment will be created, in the format "projects/{project}".
... whereas I suspect your project ID is just the ID, rather than the resource name starting with "projects/".
I would recommend using the generated resource name classes as far as possible, with the corresponding properties. So in this case, you'd have:
CreateAssessmentRequest createAssessmentRequest = new CreateAssessmentRequest
{
Assessment = new Assessment
{
Event = new Event
{
SiteKey = _siteKey,
Token = formResponse,
ExpectedAction = "Create_Account"
},
Name = recaptchaAction,
},
ParentAsProjectName = new ProjectName(_projectId)
};

PowerBi - how to authenticate to app.powerbi.com silently

I have tried the method outlined in the Microsoft docs
which involves creating an app in Active Directory and then having code something very similar to:
var authContextUrl = "https://login.windows.net/common/oauth2/authorize";
var authenticationContext = new AuthenticationContext(authContextUrl);
var redirectUri = "https://dev.powerbi.com/Apps/SignInRedirect";
var pp = new PlatformParameters(PromptBehavior.Auto);
var result = authenticationContext.AcquireTokenAsync(PowerBiApiResource, clientId, new Uri(redirectUri), pp).GetAwaiter().GetResult();
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the PowerBI API token");
}
var token = result.AccessToken;
return token;
I got this code working but it always insisted on prompting for a username and password, which is a problem for a function app.
I have also tried the approach in the silent function specified here
https://community.powerbi.com/t5/Developer/Data-Refresh-by-using-API-Need-Steps/m-p/209371#M6614
static string getAccessTokenSilently()
{
HttpWebRequest request = System.Net.HttpWebRequest.CreateHttp("https://login.windows.net/common/oauth2/token");
//POST web request to create a datasource.
request.KeepAlive = true;
request.Method = "POST";
request.ContentLength = 0;
request.ContentType = "application/x-www-form-urlencoded";
//Add token to the request header
request.Headers.Add("Authorization", String.Format("Bearer {0}", token));
NameValueCollection parsedQueryString = HttpUtility.ParseQueryString(String.Empty);
parsedQueryString.Add("client_id", clientID);
parsedQueryString.Add("grant_type", "password");
parsedQueryString.Add("resource", resourceUri);
parsedQueryString.Add("username", username);
parsedQueryString.Add("password", password);
string postdata = parsedQueryString.ToString();
//POST web request
byte[] dataByteArray = System.Text.Encoding.ASCII.GetBytes(postdata); ;
request.ContentLength = dataByteArray.Length;
//Write JSON byte[] into a Stream
using (Stream writer = request.GetRequestStream())
{
writer.Write(dataByteArray, 0, dataByteArray.Length);
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
dynamic responseJson = JsonConvert.DeserializeObject<dynamic>(responseString);
return responseJson["access_token"];
}
}
This code doesn't work.
Also this has issues, although I haven't tried it:
https://learn.microsoft.com/en-us/power-bi/developer/get-azuread-access-token
There doesn't appear to be anything up to date available that works that explains how to do this. Does anyone know how?
This is the best I've got so far. I have to create the application in AD using https://dev.powerbi.com/apps and then login using a powerbi pro userid and password, using the following code:
public static string GetPowerBiAccessToken(string tenantId, string clientId, string userId, string password)
{
var url = $"https://login.windows.net/{tenantId}/oauth2/token";
var request = (HttpWebRequest)WebRequest.Create(url);
request.KeepAlive = true;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
var dataToPost = new Dictionary<string,string>
{
{"client_id", clientId},
{"grant_type", "password"},
{"resource", PowerBiApiResource},
{"username", userId},
{"password", password},
{"redirect_uri", "https://dev.powerbi.com/Apps/SignInRedirect" }
};
var postData = string.Empty;
foreach (var item in dataToPost)
{
if (!string.IsNullOrEmpty(postData))
postData += "&";
postData += $"{item.Key}={item.Value}";
}
var dataByteArray = System.Text.Encoding.ASCII.GetBytes(postData);
request.ContentLength = dataByteArray.Length;
using (var writer = request.GetRequestStream())
{
writer.Write(dataByteArray, 0, dataByteArray.Length);
}
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
var responseJson = JsonConvert.DeserializeObject<dynamic>(responseString);
return responseJson["access_token"];
}
(P.S. I appreciate the request part of this code could be much better but having spent over 2 days trying to work this out, I'm just glad to have something that is working right now. Also the redirectUrl matches the one in my app, I'm not sure if its essential or not).
In the event of errors, I used Fiddler 4 to tell me exactly what the error was rather than just getting 400 or 401 in the app.

wso2 emm 2.2.0 device udid url does not return anything

After successful profile installation, i am unable to get device info from agent-check.jag. It onl returns username{}. Instead it should ideally pass the deviceid, access token, refresh token and client credentials.
var deviceCheckURL = mdmProps["iOSAPIRoot"] + "devices/udid";
var challengeToken = session.get("iOSChallengeToken");
var payload = {"challengeToken" : challengeToken};
serviceInvokers.XMLHttp.post(deviceCheckURL, stringify(payload), function (restAPIResponse) {
var status = restAPIResponse["status"];
if (status == 200) {
var responseContent = parse(restAPIResponse.responseText);
response["status"] = 200;
response["content"] = responseContent; # Here response is just username{}.
} else {
response["status"] = 200;
response["content"] = {"deviceID" : null};
}
});
Not sure why it is happening? Any issues related to roles?

How to set content-length-range for s3 browser upload via boto

The Issue
I'm trying to upload images directly to S3 from the browser and am getting stuck applying the content-length-range permission via boto's S3Connection.generate_url method.
There's plenty of information about signing POST forms, setting policies in general and even a heroku method for doing a similar submission. What I can't figure out for the life of me is how to add the "content-length-range" to the signed url.
With boto's generate_url method (example below), I can specify policy headers and have got it working for normal uploads. What I can't seem to add is a policy restriction on max file size.
Server Signing Code
## django request handler
from boto.s3.connection import S3Connection
from django.conf import settings
from django.http import HttpResponse
import mimetypes
import json
conn = S3Connection(settings.S3_ACCESS_KEY, settings.S3_SECRET_KEY)
object_name = request.GET['objectName']
content_type = mimetypes.guess_type(object_name)[0]
signed_url = conn.generate_url(
expires_in = 300,
method = "PUT",
bucket = settings.BUCKET_NAME,
key = object_name,
headers = {'Content-Type': content_type, 'x-amz-acl':'public-read'})
return HttpResponse(json.dumps({'signedUrl': signed_url}))
On the client, I'm using the ReactS3Uploader which is based on tadruj's s3upload.js script. It shouldn't be affecting anything as it seems to just pass along whatever the signedUrls covers, but copied below for simplicity.
ReactS3Uploader JS Code (simplified)
uploadFile: function() {
new S3Upload({
fileElement: this.getDOMNode(),
signingUrl: /api/get_signing_url/,
onProgress: this.props.onProgress,
onFinishS3Put: this.props.onFinish,
onError: this.props.onError
});
},
render: function() {
return this.transferPropsTo(
React.DOM.input({type: 'file', onChange: this.uploadFile})
);
}
S3upload.js
S3Upload.prototype.signingUrl = '/sign-s3';
S3Upload.prototype.fileElement = null;
S3Upload.prototype.onFinishS3Put = function(signResult) {
return console.log('base.onFinishS3Put()', signResult.publicUrl);
};
S3Upload.prototype.onProgress = function(percent, status) {
return console.log('base.onProgress()', percent, status);
};
S3Upload.prototype.onError = function(status) {
return console.log('base.onError()', status);
};
function S3Upload(options) {
if (options == null) {
options = {};
}
for (option in options) {
if (options.hasOwnProperty(option)) {
this[option] = options[option];
}
}
this.handleFileSelect(this.fileElement);
}
S3Upload.prototype.handleFileSelect = function(fileElement) {
this.onProgress(0, 'Upload started.');
var files = fileElement.files;
var result = [];
for (var i=0; i < files.length; i++) {
var f = files[i];
result.push(this.uploadFile(f));
}
return result;
};
S3Upload.prototype.createCORSRequest = function(method, url) {
var xhr = new XMLHttpRequest();
if (xhr.withCredentials != null) {
xhr.open(method, url, true);
}
else if (typeof XDomainRequest !== "undefined") {
xhr = new XDomainRequest();
xhr.open(method, url);
}
else {
xhr = null;
}
return xhr;
};
S3Upload.prototype.executeOnSignedUrl = function(file, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', this.signingUrl + '&objectName=' + file.name, true);
xhr.overrideMimeType && xhr.overrideMimeType('text/plain; charset=x-user-defined');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var result;
try {
result = JSON.parse(xhr.responseText);
} catch (error) {
this.onError('Invalid signing server response JSON: ' + xhr.responseText);
return false;
}
return callback(result);
} else if (xhr.readyState === 4 && xhr.status !== 200) {
return this.onError('Could not contact request signing server. Status = ' + xhr.status);
}
}.bind(this);
return xhr.send();
};
S3Upload.prototype.uploadToS3 = function(file, signResult) {
var xhr = this.createCORSRequest('PUT', signResult.signedUrl);
if (!xhr) {
this.onError('CORS not supported');
} else {
xhr.onload = function() {
if (xhr.status === 200) {
this.onProgress(100, 'Upload completed.');
return this.onFinishS3Put(signResult);
} else {
return this.onError('Upload error: ' + xhr.status);
}
}.bind(this);
xhr.onerror = function() {
return this.onError('XHR error.');
}.bind(this);
xhr.upload.onprogress = function(e) {
var percentLoaded;
if (e.lengthComputable) {
percentLoaded = Math.round((e.loaded / e.total) * 100);
return this.onProgress(percentLoaded, percentLoaded === 100 ? 'Finalizing.' : 'Uploading.');
}
}.bind(this);
}
xhr.setRequestHeader('Content-Type', file.type);
xhr.setRequestHeader('x-amz-acl', 'public-read');
return xhr.send(file);
};
S3Upload.prototype.uploadFile = function(file) {
return this.executeOnSignedUrl(file, function(signResult) {
return this.uploadToS3(file, signResult);
}.bind(this));
};
module.exports = S3Upload;
Any help would be greatly appreciated here as I've been banging my head against the wall for quite a few hours now.
You can't add it to a signed PUT URL. This only works with the signed policy that goes along with a POST because the two mechanisms are very different.
Signing a URL is a lossy (for lack of a better term) process. You generate the string to sign, then sign it. You send the signature with the request, but you discard and do not send the string to sign. S3 then reconstructs what the string to sign should have been, for the request it receives, and generates the signature you should have sent with that request. There's only one correct answer, and S3 doesn't know what string you actually signed. The signature matches, or doesn't, either because you built the string to sign incorrectly, or your credentials don't match, and it doesn't know which of these possibilities is the case. It only knows, based on the request you sent, the string you should have signed and what the signature should have been.
With that in mind, for content-length-range to work with a signed URL, the client would need to actually send such a header with the request... which doesn't make a lot of sense.
Conversely, with POST uploads, there is more information communicated to S3. It's not only going on whether your signature is valid, it also has your policy document... so it's possible to include directives -- policies -- with the request. They are protected from alteration by the signature, but they aren't encrypted or hashed -- the entire policy is readable by S3 (so, by contrast, we'll call this the opposite, "lossless.")
This difference is why you can't do what you are trying to do with PUT while you can with POST.

server to server auth request gives "error": "invalid_grant", "error_description": "Bad Request"

I am actually using the Java libraries in Cold Fusion. I am getting "error": "invalid_grant", "error_description": "Bad Request".
I have verified my EPOCH, header, and claim are base64 encoded correctly. If I could use a sample p12 and other data to match the output to then maybe I could verify my issue. I really need some help. Here is the sample coldfusion script I found that I am using on Adobe CF9.
<cfscript>
component displayname="GoogleOauth2" output="false"
{
variables.my.p12FileLocation = ""; //your p12 file location goes here
variables.my.emailOfServiceAccount = ''; //the email address listed on Google Developer's console for your project and service account goes here (https://cloud.google.com/console?redirected=true#/project)
variables.my.scopesForToken = ''; //list of services to authorize token for (i.e. https://www.googleapis.com/auth/admin.directory.user)
variables.my.emailOfSuperAccount = ''; //email of super account...seemingly needed for the scope I was was requesting...
variables.my.googleOauthURL = 'https://accounts.google.com/o/oauth2/token';
public GoogleOauth2 function init () {
if (structKeyExists(arguments,'p12FileLocation')){
variables.my.p12FileLocation = arguments.p12FileLocation;
}
if (structKeyExists(arguments,'emailOfServiceAccount')){
variables.my.emailOfServiceAccount = arguments.emailOfServiceAccount;
}
if (structKeyExists(arguments,'emailOfSuperAccount')){
variables.my.emailOfSuperAccount = arguments.emailOfSuperAccount;
}
if (structKeyExists(arguments,'p12FileLocation')){
variables.my.p12FileLocation = arguments.p12FileLocation;
}
return this;
}
public struct function getToken() {
if (structKeyExists(arguments,'scopesForToken')){
variables.my.scopesForToken = arguments.scopesForToken
}
//create the JWT Packet
local.JWTPacket = assembleJWTPacket();
//send it to Google
local.result = sendPacket(JWT=local.JWTPacket);;
try {
//set the return token to a variable
local.returnJson = DeserializeJSON(result.fileContent.toString());
local.token = returnJson.access_token;
local.returnPkg = {"token"=local.token,"success"=true};
}
catch(any e) {
local.returnPkg = {"success"=false};
}
return local.returnPkg;
}
private struct function sendPacket(required string JWT ) {
/* get token from Google */
local.httpService = new http();
local.httpService.setMethod("post");
local.httpService.setCharset("utf-8");
local.httpService.setUrl("#variables.my.googleOauthURL#");
local.httpService.addParam(type="header",name="Content-Type",value="application/x-www-form-urlencoded");
local.httpService.addParam(type="formfield",name="grant_type",value="urn:ietf:params:oauth:grant-type:jwt-bearer");
local.httpService.addParam(type="formfield",name="assertion",value="#arguments.jwt#");
local.result = httpService.send().getPrefix();
return local.result;
}
private string function assembleJWTPacket() {
//first create the header hard code the base64 value since there is only one
// same as {"alg":"RS256","typ":"JWT"} in base64
local.jwtHeader = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9';
//use epoch time and one hour more for duration of token...exp could be dynamic under 60 minutes
local.epoch = dateDiff('s', dateConvert('utc2Local', createDateTime(1970, 1, 1, 0, 0, 0)), now());
local.exp = dateDiff('s', dateConvert('utc2Local', createDateTime(1970, 1, 1, 0, 0, 0)), dateAdd('n',60,now()));
//claim set includes email from developers console, scope which is service you want to access, and authorization time up to one hour
local.jwtClaimSet = serializeJSON({
"iss"=variables.my.emailOfServiceAccount,
"scope"=variables.my.scopesForToken,
"aud"=variables.my.googleOauthURL,
"exp"=local.exp,
"iat"=local.epoch,
"sub"=variables.my.emailOfSuperAccount
});
// base 64 and url encode it
local.jwtClaimSet = Base64URLEncode(jwtClaimSet);
// combine header and claim set to be signed
local.signString = '#jwtHeader#.#jwtClaimSet#';
//sign it
local.signedString = signTheString(String=local.signString);
//encode it
local.encodedSignedString = Base64URLEncode(String=toString(local.signedString));
//you now have enough to send full jwt to google to get a token back
local.jwt = '#local.jwtHeader#.#local.jwtClaimSet#.#local.encodedSignedString#';
return local.jwt;
}
private array function signTheString(required string String ) {
//get the certificate (p12) and extract the privateKey
// create input file stream from certificate
local.fileStream = CreateObject( "java", "java.io.FileInputStream" ).init( variables.my.p12FileLocation );
local.keystore = CreateObject( "java", "java.security.KeyStore" ).getInstance("PKCS12");
//password from google never changes...hard coded for now
local.password = "notasecret";
local.keystore.load(fileStream, password.toCharArray());
local.key = local.keystore.getKey("privatekey", password.toCharArray());
//now you've got the key
local.privateKey = local.key.getEncoded();
//use it to sign the header and claimset
local.signature = createObject("java", "java.security.Signature");
local.keyFactory = createObject("java","java.security.KeyFactory");
local.keySpec = createObject("java","java.security.spec.PKCS8EncodedKeySpec");
local.signature = signature.getInstance("SHA256withRSA");
local.signature.initSign(keyFactory.getInstance("RSA").generatePrivate(keySpec.init(local.privateKey)));
local.jMsg = JavaCast("string",arguments.String).getBytes('utf-8');
local.signature.update(local.jMsg);
local.signBytes = local.signature.sign();
return local.signBytes;
}
private string function Base64URLEncode(required string String ){
return Replace( Replace( Replace( toBase64(Arguments.String), "=", "", "all"), "+", "-", "all"), "/", "_", "all");
}
}
</cfscript>