Issue with the submitAdapterAuthentication() method of the ChallengeHandler in MobileFirst v.6.3 - callback

We have an issue with the submitAdapterAuthentication() method of the ChallengeHandler in IBM MobileFirst v.6.3.
We assign callback functions to the properties 'onSuccess' and 'onFailure' in the options object.
We then provide the options object to submitAdapterAuthentication(invocationData, options) and execute it.
var ch = WL.Client.createChallengeHandler(securityTest);
//////////////////
function login (user, pass) {
tempUser = {username: user, password: pass};
userObj.user = user;
var auth = "Basic " + window.btoa(user + ":" + pass);
var invocationData = {
parameters: [auth, user],
adapter: "SingleStepAuthAdapter",
procedure: "submitLogin"
};
var options = {
onSuccess: iWon,
onFailure: iLost,
invocationContext: {invocationData: invocationData},
timeout: 10000
};
ch.submitAdapterAuthentication(invocationData, options);
});
function iWon(response) {
WL.Logger.debug('Login success! Response: ' + JSON.stringify(response));
//update user info, as somehow isUserAuthenticated return false without it
WL.Client.updateUserInfo(function(response) {
WL.Logger.debug('Updated User Info success! Response: ' + JSON.stringify(response));
});
}
function iLost(response) {
WL.Logger.debug('ERROR. Login failed! Response: ' + JSON.stringify(response));
}
Neither the onSuccess (iWon) or the onFailure (iLost) is called after executing submitAdapterAuthentication(invocationData, options).
How do we know if the authentication was successful?
Which options, events, callbacks or promises shall we look for and use?
We have also posted the issue here:
submitAdapterAuthentication not working

You are missing the definition of the functions
ch.isCustomResponse = function(response){...}
ch.handleChallenge = function(response){...}
Your code should look more like this
var ch = WL.Client.createChallengeHandler(securityTest);
ch.isCustomResponse = function(response) {
if (!response||!response.responseJSON||response.responseText === null) {
return false;
}
if (typeof(response.responseJSON.authRequired) !== 'undefined'){
return true;
} else {
return false;
}
};
ch.handleChallenge = function(response){
var authRequired = response.responseJSON.authRequired;
if (authRequired == true){
// handle the case when authentication is needed, i.e., show login form etc.
if (response.responseJSON.errorMessage) {
// authentication failed, show a message to the user indicating what went wrong
// call the login failed function or move it's contents here
iLost(response);
}
} else if (authRequired == false){
// no authentication is needed
ch.submitSuccess();
// call the login success function or move it's contents here
iWon(response);
}
};
//////////////////
function login (user, pass) {
tempUser = {username: user, password: pass};
userObj.user = user;
// is the first parameter expected by submitLogin the username or the
// Basic Authentication encoded string ???
var auth = "Basic " + window.btoa(user + ":" + pass);
var invocationData = {
parameters: [auth, user],
adapter: "SingleStepAuthAdapter",
procedure: "submitLogin"
};
ch.submitAdapterAuthentication(invocationData, {});
});
function iWon(response) {
WL.Logger.debug('Login success! Response: ' + JSON.stringify(response));
//update user info, as somehow isUserAuthenticated return false without it
WL.Client.updateUserInfo(function(response) {
WL.Logger.debug('Updated User Info success! Response: ' + JSON.stringify(response));
});
}
function iLost(response) {
WL.Logger.debug('ERROR. Login failed! Response: ' + JSON.stringify(response));
}
For more information on adapter-based authentication visit http://www-01.ibm.com/support/knowledgecenter/SSHS8R_6.3.0/com.ibm.worklight.dev.doc/devref/t_adapter_based_authenticator.html?lang=en
You should also check the getting started module on adapter-based authentication for hybrid applications https://developer.ibm.com/mobilefirstplatform/documentation/getting-started-6-3/authentication-security/adapter-based-authentication/adapter-based-authentication-hybrid-applications/

Related

AWS Lambda - MongoDB resource optimization

I'm building facebook chatbot using AWS Lambda and MongoDB. At the moment, my application is pretty simple but I'm trying to nail down the basics before I move onto the complex stuff.
I understand AWS Lambda is stateless but I've read adding below line in handler along with variables initialized outside handler, I don't have to establish DB connection on every request.
context.callbackWaitsForEmptyEventLoop = false;
(I've read this from this article; https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs)
I'm adding my entire code below
'use strict'
const
axios = require('axios'),
mongo = require('mongodb'),
MongoClient = mongo.MongoClient,
assert = require('assert');
var VERIFY_TOKEN = process.env.VERIFY_TOKEN;
var PAGE_ACCESS_TOKEN = process.env.PAGE_ACCESS_TOKEN;
var MONGO_DB_URI = process.env.MONGO_DB_URI;
let cachedDb = null;
let test = null;
exports.handler = (event, context, callback) => {
var method = event.context["http-method"];
context.callbackWaitsForEmptyEventLoop = false;
console.log("test :: " + test);
if (!test) {
test = "1";
}
// process GET request --> verify facebook webhook
if (method === "GET") {
var queryParams = event.params.querystring;
var rVerifyToken = queryParams['hub.verify_token']
if (rVerifyToken === VERIFY_TOKEN) {
var challenge = queryParams['hub.challenge'];
callback(null, parseInt(challenge))
} else {
var response = {
'body': 'Error, wrong validation token',
'statusCode': 403
};
callback(null, response);
}
// process POST request --> handle message
} else if (method === "POST") {
let body = event['body-json'];
body.entry.map((entry) => {
entry.messaging.map((event) => {
if (event.message) {
if (!event.message.is_echo && event.message.text) {
console.log("BODY\n" + JSON.stringify(body));
console.log("<<MESSAGE EVENT>>");
// retrieve message
let response = {
"text": "This is from webhook response for \'" + event.message.text + "\'"
}
// facebook call
callSendAPI(event.sender.id, response);
// store in DB
console.time("dbsave");
storeInMongoDB(event, callback);
}
} else if (event.postback) {
console.log("<<POSTBACK EVENT>>");
} else {
console.log("UNHANDLED EVENT; " + JSON.stringify(event));
}
})
})
}
}
function callSendAPI(senderPsid, response) {
console.log("call to FB");
let payload = {
recipient: {
id: senderPsid
},
message: response
};
let url = `https://graph.facebook.com/v2.6/me/messages?access_token=${PAGE_ACCESS_TOKEN}`;
axios.post(url, payload)
.then((response) => {
console.log("response ::: " + response);
}).catch(function(error) {
console.log(error);
});
}
function storeInMongoDB(messageEnvelope, callback) {
console.log("cachedDB :: " + cachedDb);
if (cachedDb && cachedDb.serverConfig.isConnected()) {
sendToAtlas(cachedDb.db("test"), messageEnvelope, callback);
} else {
console.log(`=> connecting to database ${MONGO_DB_URI}`);
MongoClient.connect(MONGO_DB_URI, function(err, db) {
assert.equal(null, err);
cachedDb = db;
sendToAtlas(db.db("test"), messageEnvelope, callback);
});
}
}
function sendToAtlas(db, message, callback) {
console.log("send to Mongo");
db.collection("chat_records").insertOne({
facebook: {
messageEnvelope: message
}
}, function(err, result) {
if (err != null) {
console.error("an error occurred in sendToAtlas", err);
callback(null, JSON.stringify(err));
} else {
console.timeEnd("dbsave");
var message = `Inserted a message into Atlas with id: ${result.insertedId}`;
console.log(message);
callback(null, message);
}
});
}
I did everything as instructed and referenced a few more similar cases but somehow on every request, "cachedDb" value is not saved from previous request and the app is establishing the connection all over again.
Then I also read that there is no guarantee the Lambda function is using the same container on multiple requests so I made another global variable "test". "test" variable value is logged "1" from the second request which means it's using the same container but again, "cachedDb" value is not saved.
What am I missing here?
Thanks in advance!
In short AWS Lambda function is not a permanently running service of any kind.
So, far I know AWS Lambda works on idea - "one container processes one request at a time".
It means when request comes and there is available running container for the Lambda function AWS uses it, else it starts new container.
If second request comes when first container executes Lambda function for first request AWS starts new container.
and so on...
Then there is no guarantee in what container (already running or new one) Lambda function will be executed, so... new container opens new DB connection.
Of course, there is an inactivity period and no running containers will be there after that. All will start over again by next request.

Angular2 Stripe integration stripeResponseHandler cannot access this

I'm integrating Stripe payments with Angular2 (actually Ionic but the code is the same)
the call to Stripe.card.createToken is successful and returns a token
but in stripeResponseHandler which is an async callback, I cannot access any of the "this" variables. for example I cannot set this.amount = 10 and I cannot call this._http.post
how can I access the "this" variables ? I'm trying to http post the token and the amount to an API to make the payment
constructor(private _navController: NavController,
private _http: Http) { }
submitPayment() {
Stripe.setPublishableKey(this.key);
this.card = new Card();
this.card.number = this.cardNumber;
this.card.cvc = this.cardCVC;
this.card.exp_month = this.cardExpMonth;
this.card.exp_year = this.cardExpYear;
this.card.address_zip = this.cardAddressZip;
try {
Stripe.card.createToken(this.card, this.stripeResponseHandler);
}
catch (e) {
alert(e.message);
}
// Prevent the form from being submitted:
return false;
}
stripeResponseHandler(status, response) {
if (response.error) { // Problem!
alert(response.error);
} else { // Token was created!
// Get the token ID:
alert(response.id);
try {
this.amount = 10;
let payment = new Payment();
payment.token = response.id;
payment.amount = this.amount;
let body = JSON.stringify(payment);
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
this._http.post(this.url, body, options)
.map(res => res.json())
.catch(this.handleError);
}
catch (e) {
alert(e.message);
}
}
}
handleError(error: Response) {
// may send the error to some remote logging infrastructure
// instead of just logging it to the console
console.error(error);
alert('error' + error.text + " " + error.statusText);
return Observable.throw(error.json().error || 'Server error');
}
If you just pass the function reference, then JavaScript doesn't keep the this reference. You have to take care of this explicitely:
Instead of
Stripe.card.createToken(this.card, this.stripeResponseHandler);
use
Stripe.card.createToken(this.card, (status, person) => this.stripeResponseHandler(status, person));
See also https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions
or
Stripe.card.createToken(this.card, this.stripeResponseHandler.bind(this));

ionic 2 http request after oauth not working

I have a button redirect to this function
loginGoogle() {
this.cordovaOauthG.login().then((success) => {
console.log("Google Login DONE ");
if (success != null) {
console.log(JSON.stringify(success));
//alert(success.access_token);
if (success.access_token != null && success.access_token != '') {
var params = "google_id=" + success.access_token;
// var token = success.access_token;
// this.postLoginGoogle(params);
this.tesLogin();
}
}
}, (error) => {
alert(error);
});
}
and this is the function for http request
tesLogin(){
// var params = "google_id="+gid;
var params = "google_id="+'ya29.Ci_4ApeVHCD7av30Y82JRZPLG4T9ZUgmU1SNLUGIlVV_ufAcCoBc4ILqsY6Ah55i-g';
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
console.log(params);
this.http.post(Config.base_url + 'api/user-account/login-google', params, {headers: headers})
.map(res => res.json()).subscribe(data => {
console.log(data);
if (data.success) {
this.local.set('access_token', data.message);
this.menu.enable(true);
this.nav.setRoot(HomePage);
}
else {
this.doAlert("Incorrect email or password");
}
});
}
My problem is, whenever I tried to call using success.access_token, it doesnt work
but If I am calling the request without any parameters(just throwing in some random strings) then it works
I tried to debug it using mobile inspector from chrome, either way it is returning a error like this (working post & not working post both returning error)
EXCEPTION: SyntaxError: Unexpected token < in JSON at position 1
I suggest to send the authentication token in the header not in the parameters. Something like that:
var headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
headers.append('Authorization', 'Bearer ' + authToken);

parse.com afterDelete trigger doesn't work completely

I have a Parse class Comment. Each Comment is submitted by a ParseUser and I wanna track in my parse user how many comments he has submitted, so I've created the following triggers in Parse Cloud Code:
/*
* ------------- AFTER SAVE
*/
Parse.Cloud.afterSave("Comment", function(request) {
//if it's the first save
if(!request.object.existed()) {
//increment the User's comments
query = new Parse.Query(Parse.User);
query.get(request.object.get("createdBy").id, {
success: function(user) {
user.increment("comments");
user.save();
}, error: function(error) {
console.log("Error searching the User");
}
});
}
});
/*
* ------------- AFTER DELETE
*/
Parse.Cloud.afterDelete("Comment", function(request) {
//decrement the User's comments
console.log("The user who submitted the comment has the id: " + request.object.get("createdBy").id);
query = new Parse.Query(Parse.User);
query.get(request.object.get("createdBy").id, {
success: function(user) {
console.log("Retrieved the user. He has username: " + user.get("username"));
user.increment("comments", -1);
user.save();
}, error: function(error) {
console.log("Error searching the user - error afterDelete#Comment 2");
}
});
});
The problem is that the afterSave trigger works, but the afterDelete doesn't and I can't figure out why. I can retrieve the User because the two console.log show out which is the correct user, but I can't save it, after the increment(user, -1). Thanks in advance.
EDIT:
Here the User trigger:
/*
* ------------- BEFORE SAVE
*/
Parse.Cloud.beforeSave("User", function(request, response) {
var comments = request.object.get("comments");
if(comments == null || comments < 0)
request.object.set("comments", 0);
var itemSaved = request.object.get("itemSaved");
if(itemSaved == null || itemSaved < 0)
request.object.set("itemSaved", 0);
var username = request.object.get("username");
if(username.length < 6 || username.length > 20)
response.error("username must be longer than 6");
else {
response.success();
}
});
/*
* ------------- AFTER DELETE
*/
Parse.Cloud.afterDelete("User", function(request) {
query = new Parse.Query("Comment");
query.equalTo("createdBy", request.object.id);
query.find({
success: function(comments) {
Parse.Object.destroyAll(comments, {
success: function() {},
error: function(error) {
console.error("Error deleting related comments " + error.code + ": " + error.message);
}
});
},
error: function(error) {
console.error("Error finding related comments " + error.code + ": " + error.message);
}
});
});
Per Parse's documentation, "If you want to use afterDelete for a predefined class in the Parse JavaScript SDK (e.g. Parse.User), you should not pass a String for the first argument. Instead, you should pass the class itself."

Twitter OAuth with WinJS

Trying to authenticate with Twitter since over a week trough my Windows 8 app, but no success.
My app is registered at Twitter and it should be able to read, write and sign in.
I think I've tried all the descriptions at Twitter documentation, but nothing works. Guess the problem is at me, but can't find it.
I get always the 403 forbidden response.
My code:
function getTwitterCredentials() {
WinJS.xhr({
type:"get",
url: "https://api.twitter.com/oauth/authenticate",
headers: {
consumerKey: "ZSNRXXXXXXXXX",
userKey: "GVknHzXXXXXXXXXXXXXXXXXXX",
Authorization: "OAuth",
oauth_consumer_key: "ZSNRtXXXXXXXXXXXXX",
oauth_nonce: "b7efbXXXXXXXXXXXXXXXx",
oauth_signature: "23zb0XXXXXXXXXXXXXXx",
oauth_signature_method: "HMAC-SHA1",
oauth_timestamp: "1368555677",
oauth_token: "1408XXXXXXXXXXXXXXXXXXXXXXXXXXXXx",
oauth_version: "1.0"
}
}).done(function (response) {
//it it works here some will be some action
}, function error(response) {
console.log(response.status);
});
}
Someone has experience whit this issue?
Thanks Marlowe
Here's some demo JS code I slightly modified from an existing sample on our site from the oAuth Web Authentication Broker for Win8 demo. Search 'oob' for my changes, they are minor.
In addition, the Linq to Twitter project is pretty awesome so may want to consider checking that out as well and would prob be a bit easier. It handles the auth fairly automatically and doesn't require having to enter in the token response.
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//// PARTICULAR PURPOSE.
////
//// Copyright (c) Microsoft Corporation. All rights reserved
(function () {
"use strict";
var page = WinJS.UI.Pages.define("/html/oAuthTwitter.html", {
ready: function (element, options) {
document.getElementById("oAuthTwitterLaunch").addEventListener("click", launchTwitterWebAuth, false);
//did read that this is required for oAuth in a win8 app, however twitter uses 'oob' for a desktop app's callback url.
//in fact your app will show it.
//var endURI = Windows.Security.Authentication.Web.WebAuthenticationBroker.getCurrentApplicationCallbackUri();
//document.getElementById("TwitterCallbackURL").innerText = endURI.displayUri;
}
});
function sendRequest(url) {
try {
var request = new XMLHttpRequest();
request.open("GET", url, false);
request.send(null);
return request.responseText;
} catch (err) {
WinJS.log("Error sending request: " + err, "Web Authentication SDK Sample", "error");
}
}
function sendPostRequest(url, authzheader) {
try {
var request = new XMLHttpRequest();
request.open("POST", url, false);
request.setRequestHeader("Authorization", authzheader);
request.send(null);
if (request.status != "200") {
console.log(request);
}
return request.responseText;
} catch (err) {
WinJS.log("Error sending request: " + err, "Web Authentication SDK Sample", "error");
}
}
function isValidUriString(uriString) {
var uri = null;
try {
uri = new Windows.Foundation.Uri(uriString);
}
catch (err) {
}
return uri !== null;
}
var authzInProgress = false;
function launchTwitterWebAuth() {
var twitterURL = "https://api.twitter.com/oauth/request_token";
// Get all the parameters from the user
var clientID = document.getElementById("TwitterClientID").value;
if (clientID === null || clientID === "") {
WinJS.log("Please enter a ClientID for Twitter App", "Web Authentication SDK Sample", "error");
return;
}
var clientSecret = document.getElementById("TwitterSecret").value;
if (clientSecret === null || clientSecret === "") {
WinJS.log("Please enter a Secret for Twitter App", "Web Authentication SDK Sample", "error");
return;
}
var callbackURL = document.getElementById("TwitterCallbackURL").value;
//if (!isValidUriString(callbackURL)) {
// WinJS.log("Please enter a Callback URL for Twitter", "Web Authentication SDK Sample", "error");
// return;
//}
if (authzInProgress) {
document.getElementById("TwitterDebugArea").value += "\r\nAuthorization already in Progress ...";
return;
}
// Acquiring a request token
var timestamp = Math.round(new Date().getTime() / 1000.0);
var nonce = Math.random();
nonce = Math.floor(nonce * 1000000000);
// Compute base signature string and sign it.
// This is a common operation that is required for all requests even after the token is obtained.
// Parameters need to be sorted in alphabetical order
// Keys and values should be URL Encoded.
var sigBaseStringParams = "oauth_callback=" + encodeURIComponent(callbackURL);
sigBaseStringParams += "&" + "oauth_consumer_key=" + clientID;
sigBaseStringParams += "&" + "oauth_nonce=" + nonce;
sigBaseStringParams += "&" + "oauth_signature_method=HMAC-SHA1";
sigBaseStringParams += "&" + "oauth_timestamp=" + timestamp;
sigBaseStringParams += "&" + "oauth_version=1.0";
var sigBaseString = "POST&";
sigBaseString += encodeURIComponent(twitterURL) + "&" + encodeURIComponent(sigBaseStringParams);
var keyText = clientSecret + "&";
var keyMaterial = Windows.Security.Cryptography.CryptographicBuffer.convertStringToBinary(keyText, Windows.Security.Cryptography.BinaryStringEncoding.Utf8);
var macAlgorithmProvider = Windows.Security.Cryptography.Core.MacAlgorithmProvider.openAlgorithm("HMAC_SHA1");
var key = macAlgorithmProvider.createKey(keyMaterial);
var tbs = Windows.Security.Cryptography.CryptographicBuffer.convertStringToBinary(sigBaseString, Windows.Security.Cryptography.BinaryStringEncoding.Utf8);
var signatureBuffer = Windows.Security.Cryptography.Core.CryptographicEngine.sign(key, tbs);
var signature = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(signatureBuffer);
var dataToPost = "OAuth oauth_callback=\"" + encodeURIComponent(callbackURL) + "\", oauth_consumer_key=\"" + clientID + "\", oauth_nonce=\"" + nonce + "\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"" + timestamp + "\", oauth_version=\"1.0\", oauth_signature=\"" + encodeURIComponent(signature) + "\"";
var response = sendPostRequest(twitterURL, dataToPost);
var oauth_token;
var oauth_token_secret;
var keyValPairs = response.split("&");
for (var i = 0; i < keyValPairs.length; i++) {
var splits = keyValPairs[i].split("=");
switch (splits[0]) {
case "oauth_token":
oauth_token = splits[1];
break;
case "oauth_token_secret":
oauth_token_secret = splits[1];
break;
}
}
document.getElementById("TwitterDebugArea").value += "\r\nOAuth Token = " + oauth_token;
document.getElementById("TwitterDebugArea").value += "\r\nOAuth Token Secret = " + oauth_token_secret;
// Send the user to authorization
twitterURL = "https://api.twitter.com/oauth/authorize?oauth_token=" + oauth_token;
document.getElementById("TwitterDebugArea").value += "\r\nNavigating to: " + twitterURL + "\r\n";
var startURI = new Windows.Foundation.Uri(twitterURL);
//var endURI = new Windows.Foundation.Uri(callbackURL);
//we use 'oob' in the request_auth, but now for authorize, we use the apps URI.
var endURI = Windows.Security.Authentication.Web.WebAuthenticationBroker.getCurrentApplicationCallbackUri();
authzInProgress = true;
Windows.Security.Authentication.Web.WebAuthenticationBroker.authenticateAsync(
Windows.Security.Authentication.Web.WebAuthenticationOptions.none, startURI, endURI)
.done(function (result) {
document.getElementById("TwitterReturnedToken").value = result.responseData;
document.getElementById("TwitterDebugArea").value += "Status returned by WebAuth broker: " + result.responseStatus + "\r\n";
if (result.responseStatus === Windows.Security.Authentication.Web.WebAuthenticationStatus.errorHttp) {
document.getElementById("TwitterDebugArea").value += "Error returned: " + result.responseErrorDetail + "\r\n";
}
authzInProgress = false;
}, function (err) {
WinJS.log("Error returned by WebAuth broker: " + err, "Web Authentication SDK Sample", "error");
document.getElementById("TwitterDebugArea").value += " Error Message: " + err.message + "\r\n";
authzInProgress = false;
});
}
})();