I'm trying doing the following: I have a local database (using PouchDB), I check if user is logged in (with pouchdb-authentication login function) and if true I sync the locale db with the remote one.
Unfortunately, when I try to create a new database on CouchDB (I want one db for every user) I always get the error {"error":"not_found","reason":"no_db_file"}. I saw this is a common error described in PouchDB documentation (https://pouchdb.com/guides/databases.html#remote-databases) but CORS is enabled and I can't figure out where is the problem.
My couchdb configuration is:
I do the login as follow:
var user = {
name: 'name',
password: 'password'
};
var url = "http://ip/";
var pouchOpts = {
skipSetup: true
};
var ajaxOpts = {
ajax: {
headers: {
Authorization: 'Basic ' + window.btoa(user.name + ':' + user.password)
}
}
};
var db = new PouchDB(url+'auth', pouchOpts);
db.login(user.name, user.password, ajaxOpts).then(function() {
return db.allDocs();
}).then(function(docs) {
//HERE I TRY TO CREATE THE NEW DATABASE
pouchDBService.sync(url+"newDatabase", user);
}).catch(function(error) {
console.error(error);
});
And, in my pouchDBService I have:
var database;
//I call this function as app starts
this.setDatabase = function(databaseName) {
database = new PouchDB(databaseName, {
adapter: 'websql'
});
}
this.sync = function(remoteDatabase, user) {
var remoteDB = new PouchDB(remoteDatabase, {
auth: {
username: user.name,
password: user.password
},
skip_setup: true //without this I get the login popup! Why if I'm passing the auth params???
});
remoteDB.info().then(function (info) {
console.log(info);
database.sync(remoteDB, {live:true, retry: true})
})
}
Is there something wrong? Any help is appreciated.
Thanks
To create databases on the CouchDB server, you need to be an admin. You could create a small custom API on the server for this (e.g. with a small node http server), or use the couchperuser plugin for CouchDB.
Related
I am trying to make a custom authentication flow using AWS Cognito so that i can send MFA codes via email instead through the cognito triggers. I am using the initiateAuth() method to do this which is correct according to the documentation;
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#initiateAuth-property
My payload seems to be valid but when i try login with a user i get the error 't.getauthparameters is not a function'
I've had a look through some other stackoverflow posts but nothing is helping
Any ideas what is going wrong?
This is a snippet from my code below:
const payload = {
AuthFlow: 'CUSTOM_AUTH',
ClientId: 'my client id',
AuthParameters: {
USERNAME: $('input[name=username]').val(),
PASSWORD: $('input[name=password]').val(),
CHALLENGE_NAME: 'SRP_A'
}
};
cognitoUser.initiateAuth(payload, {
onSuccess: function(result) {
// User authentication was successful
},
onFailure: function(err) {
// User authentication was not successful
},
customChallenge: function(challengeParameters) {
// User authentication depends on challenge response
var verificationCode = prompt('Please input OTP code' ,'');
cognitoUser.sendCustomChallengeAnswer(verificationCode, this);
},
});
So i ended up finding out that initiateAuth() is not the correct method to use.
The right method to use is cognitoUser.authenticateUser() (since i am using SRP-based authentication then adding a custom challenge) - My updated code is below
This was a similar example that i followed to help me find the answer
I couldnt find very much online for doing it with just the Amazon Cognito Identity SDK so hopefully this is helpful for anyone doing the same!
AWSCognito.config.region = 'region';
var poolData = {
UserPoolId : 'user pool id',
ClientId : 'client id'
};
var userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
var userData = {
Username: $('input[name=username]').val(),
Pool: userPool,
};
var authenticationData = {
Username : $('input[name=username]').val(),
Password : $('input[name=password]').val(),
};
var authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.setAuthenticationFlowType('CUSTOM_AUTH');
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
console.log('success');
var resultStr = 'Login Successful';
console.log(resultStr);
$('#resultsSignIn').html(resultStr);
},
onFailure: function(err) {
alert(err);
},
customChallenge: function(challengeParameters) {
// User authentication depends on challenge response
var verificationCode = prompt('Please input OTP code' ,'');
cognitoUser.sendCustomChallengeAnswer(verificationCode, this);
},
});
return false;`
A downside to the authenticateUser() method is that you won't be able to get user's input mid-execution during the authenticateUser workflow (i.e, having to use prompts in the callbacks for customchallenge etc). I believe initiateAuth() would solve this issue.
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-define-auth-challenge.html
I am releasing access to pages using connect-roles and loopback but I have a pertinent question about how I can collect the customer's role and through the connect-roles to read the session and respond to a route.
Example, when the client logs in I load a string containing the client's role and access it in a function that controls access to pages.
I have this doubt because I'm finalizing a large scale service that usually there are multiple client sessions that are accessed instantly using a same storage and check function.
It would be efficient to store the customer's role using app.set() and app.get()?
app.get('/session-details', function (req, res) {
var AccessToken = app.models.AccessToken;
AccessToken.findForRequest(req, {}, function (aux, accesstoken) {
// console.log(aux, accesstoken);
if (accesstoken == undefined) {
res.status(401);
res.send({
'Error': 'Unauthorized',
'Message': 'You need to be authenticated to access this endpoint'
});
} else {
var UserModel = app.models.user;
UserModel.findById(accesstoken.userId, function (err, user) {
// console.log(user);
res.status(200);
res.json(user);
// storage employee role
app.set('employeeRole', user.accessLevel);
});
}
});
});
Until that moment everything happens as desired I collect the string loaded with the role of the client and soon after I create a connect-roles function to validate all this.
var dsConfig = require('../datasources.json');
var path = require('path');
module.exports = function (app) {
var User = app.models.user;
var ConnectRoles = require('connect-roles');
const employeeFunction = 'Developer';
var user = new ConnectRoles({
failureHandler: function (req, res, action) {
// optional function to customise code that runs when
// user fails authorisation
var accept = req.headers.accept || '';
res.status(403);
if (~accept.indexOf('ejs')) {
res.send('Access Denied - You don\'t have permission to: ' + action);
} else {
res.render('access-denied', {action: action});
// here
console.log(app.get('employeeRole'));
}
}
});
user.use('authorize access private page', function (req) {
if (employeeFunction === 'Manager') {
return true;
}
});
app.get('/private/page', user.can('authorize access private page'), function (req, res) {
res.render('channel-new');
});
app.use(user.middleware());
};
Look especially at this moment, when I use the
console.log(app.get('employeeRole')); will not I have problems with simultaneous connections?
app.get('/private/page', user.can('authorize access private page'), function (req, res) {
res.render('channel-new');
});
Example client x and y connect at the same time and use the same function to store data about your session?
Being more specific when I print the string in the console.log(app.get('employeeRole')); if correct my doubt, that I have no problem with simultaneous connections I will load a new variable var employeeFunction = app.get('employeeRole'); so yes my function can use the object containing the role of my client in if (employeeFunction === 'Any Role') if the role that is loaded in the string contain the required role the route it frees the page otherwise it uses the callback of failureHandler.
My test environment is limited to this type of test so I hope you help me on this xD
Instead of using app.set you can create a session map(like hashmaps). I have integrated the same in one of my projects and it is working flawlessly. Below is the code for it and how you can access it:
hashmap.js
var hashmapSession = {};
exports.auth = auth = {
set : function(key, value){
hashmapSession[key] = value;
},
get : function(key){
return hashmapSession[key];
},
delete : function(key){
delete hashmapSession[key];
},
all : function(){
return hashmapSession;
}
};
app.js
var hashmap = require('./hashmap');
var testObj = { id : 1, name : "john doe" };
hashmap.auth.set('employeeRole', testObj);
hashmap.auth.get('employeeRole');
hashmap.auth.all();
hashmap.auth.delete('employeeRole');
I am implementing Amazon Cognito User Pool authentication in my web app, and I ran in this problem - how to redirect user on newPasswordRequired callback to '/new-password' and await for inputs?
So in other words, my expected flow is User logs in with temporary password(here I fire cognitoUser.authenitcateUser), after that I redirect user to '/new-password' route and user sees the new password form(newPasswordRequired callback is triggered here).
Problem now is that SDK expects me to pass passwords in the newPassword callback but I don't know them yet(since the user will put it in new password form).
Code:
async login(values) {
const details = {
Username: values.username,
Password: values.password,
};
const authDetails = new AuthenticationDetails(details);
const userData = {
Username: details.Username,
Pool: poolData,
};
const cognitoUser = await new CognitoUser(userData);
await cognitoUser.authenticateUser(authDetails, {
onSuccess: (result) => {
console.log(userData);
axios.defaults.headers.common.Authorization = result.getIdToken().getJwtToken();
browserHistory.push('/entities');
},
onFailure: (error) => {
console.log(userData);
throw new Error(error);
},
newPasswordRequired: () => {
browserHistory.push('/new-password');
console.log(cognitoUser);
console.log(cognitoUser.getAuthenticationFlowType(), 'YOU NEED TO CHANGE PASSWORD');
const userData = {
Username: cognitoUser.username,
Pool: poolData,
};
cognitoUser.completeNewPasswordChallenge(
values.newPassword,
{},
{
onSuccess: (user) => {
console.log('success', user);
},
onFailure: (error) => {
console.log(error);
},
},
);
},
});
}
Another thing I tried was to create separate method which is responsible for calling cognitoUser.completeNewPasswordChallenge but then User Pool thinks that I am not authenticated.
My react component looks like this:
<form onSubmit={this.props.handleSubmit(AWSApi.login.bind(this))}></form>
You need cognitoUser.Session for do cognitoUser.completeNewPasswordChallenge call after redirection. You can keep cognitoUser.Session in localstorage or set as a get parameter (yoururl?session=cognitoUser.Session) before redirection. Then in new page you can create new cognitoUser and set your old response session.
e.g.:
var userPool = new AmazonCognitoIdentity.CognitoUserPool(POOL_DATA);
var userData = {
Username: username,
Pool: userPool
};
var cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.Session = <your saved session>
cognitoUser.completeNewPasswordChallenge(....
i have a problem regarding cross-file calls and their lifecycles. I want to query the dynamodb for a username to login the entered user.I query in an external file to minimize queries as i need to use the data in another file too. The cycle seems to be off though and I really don't know why. The query call comes after the POST / login although the input in the form is available earlier. It would print the queryparams before the POST/login call too if i'd call a console log on it. The callback does not wait for the actual data. I have browsed other posts containing information regarding asynchronous callback functions but couldn't figure out why the callback completely ignores the query function. The problem is not the communication between browser and server but rather between files/classes in the node script. The data is available for query before the post statement but the query gets executed after. This is What can i do to prevent that?
the console output (for debugging purposes) is:
GET /stylesheets/style.css 304 3ms
callback
undefined
POST /login 500 38ms - 280b
querying ...
information found
[queryResponse Object]
the query file:
const AWS = require("aws-sdk");
var exports = module.exports = {};
const dynamodb = new AWS.DynamoDB({
apiVersion: "2012-08-10",
// accessKeyId: "", //TODO
// secretAccessKey: "", //TODO
// region: "eu-central-1" //? TODO
//testing purposes
"region": "us-west-2",
"accessKeyId": "abcde",
"secretAccessKey": "abcde",
"endpoint": "http://localhost:8000"
});
var dataAfterQuery = null;
exports.query = function(queryParams,callback) {
/*prevent unneccessary queries*/
var queryNow = dynamodb.query(queryParams,
function(err,data) {
if(err) {
console.error(err);
return done(err);
}
console.log('querying ...');
if(data.Count > 0) {
console.log('information found');
} else {
console.log('"' + JSON.stringify(queryParams) + '" is not in the db yet');
}
dataAfterQuery = data;
console.log(JSON.stringify(dataAfterQuery));
return dataAfterQuery;
});
if(typeof callback == 'function') {
console.log("callback");
callback();
return queryNow;
}
}
/*function to recieve queried data*/
exports.getQueriedData = function() {
return dataAfterQuery;
}
the login file:
module.exports = function(passport) {
passport.use("login", new LocalStrategy({
passReqToCallback: true
},
function(req,username,password,done) {
var queryParams = {
TableName: "users",
KeyConditionExpression: "username = :user",
ExpressionAttributeValues: {
//username entered in jade form
":user":{"S":username}
}
};
queryFile.query(queryParams,function(err,data){
if(err) console.log(data);
//console.log(data);
//kommt vor information found?
console.error(data);
/* response can only be null or not null*/
if(data != null) {
console.error('error, more than one user with username: "' + username + '" in the db');
console.error("Entries :" + data.Count);
return done(null,false,req.flash("message", "more than version of the username in the db"));
} else {
//only one user exists in db, query for username was successful
var parsedQueryPw = data.Items[0].password.S;
userAfterQuery = data.Items[0];
//checking if entered password is wrong
if(!isValidPassword(parsedQueryPw, password)) {
console.error("invalid password");
return done(null,false,req.flash("message","invalid user-password combination"));
}
//successful login - user and password match
console.log("login successful");
//return user object for serialization
return done(null,data);
}
I'm currently working with Node.js using the watson-developer-cloud Node.js SDK and I'm having problems when sending a query that includes an entity.
This is my code:
// require watson's node sdk and fs
var watson = require('watson-developer-cloud');
var fs = require('fs');
// Define output file
var outputJSONFile = '/home/vagrant/Desktop/node/dir/data.json';
// Create alchemy_data_news object using our api_key
var alchemy_data_news = watson.alchemy_data_news({
api_key: ''
});
// Define params for the query and what values to return
// Accepted returne values:
// docs.alchemyapi.com/v1.0/docs/full-list-of-supported-news-api-fields
var params = {
start: 'now-1m',
end: 'now',
count: 2,
qs: ['q.enriched.url.enrichedTitle.entities.entity.text=apple'],
return: ['enriched.url.url,enriched.url.title']
};
// Call getNews method and return json
alchemy_data_news.getNews(params, function (err, news) {
if (err) {
console.log('error:', err);
} else {
fs.writeFile(outputJSONFile, JSON.stringify(news, null, 2), function(err) {
if (err) {
console.log('WriteFile Error:', err);
} else {
console.log("JSON saved to " + outputJSONFile);
}
});
}
});
I'm still trying to figure out how to send the entities parameters using the params object.
While digging up through some code I came across qs so I have been using that to test but I haven't had success at all.
Any suggestions are greatly appreciated.
P.S: I'm trying to pass:
q.enriched.url.enrichedTitle.entities.entity.text=apple
q.enriched.url.enrichedTitle.entities.entity.type=company
If you look at the node-sdk source code for AlchemyDataNews, you will see that the top level parameters are being sent as query strings.
Then params map should be:
var params = {
start: 'now-1m',
end: 'now',
count: 2,
return: ['enriched.url.url,enriched.url.title'],
// fields here
'q.enriched.url.enrichedTitle.entities.entity.text': 'apple',
'q.enriched.url.enrichedTitle.entities.entity.type': 'company'
};