How to log in to CouchDB without entering username & password - authentication

When I try to log in to my remote CouchDB instance from my script, a popup login appears.
I want my script to log into CouchDB without the user having to enter the password (I'm authenticating users with the Google People API and then authenticating my offline app against a single CouchDB instance).
Instead of using the username and password in my code, CouchDB is demanding a new one each time, via the popup.
PouchNotesObj = function (databasename, remoteorigin) {
'use strict';
Object.defineProperty(this, 'pdb', {writable: true});
Object.defineProperty(this, 'remote', {writable: true});
Object.defineProperty(this, 'formobject', {writable: true});
Object.defineProperty(this, 'notetable', {writable: true});
Object.defineProperty(this, 'searchformobject', {writable: true});
Object.defineProperty(this, 'errordialog', {writable: true});
Object.defineProperty(this, 'dbname', {writable: true});
var databasename = 'pouchcontacts';
var remoteorigin = 'https://<remote.path>:6984';
var hostUrl = 'https://<myapp>.appspot.com/';
this.dbname = databasename;
this.pdb = new PouchDB(databasename);
var remoteDB = new PouchDB(remoteorigin + '/pouchnotes', {skip_setup: true});
this.remote = remoteDB;
//from https://github.com/pouchdb-community/pouchdb-authentication/issues/121
var user = {
cname: 'myusername',
password: 'mypassword'
};
var pouchOpts = {
skip_setup: true
};
var ajaxOpts = {
ajax: {
headers: {
'X-Auth-CouchDB-UserName': 'myusername',
'X-Auth-CouchDB-Roles': 'user',
'Content-Type': 'application/json; charset=utf-8',
'Authorization': 'Basic ' + window.btoa(user.cname + ':' + user.password),
'Access-Control-Allow-Origin' : hostUrl,
'Access-Control-Allow-Methods': 'GET, DELETE, PUT, OPTIONS',
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With'
}
}
};
remoteDB.login(user.cname, user.password, ajaxOpts, function (err, response) {
if (err) {
if (err.cname === 'unauthorized' || err.cname === 'forbidden') {
console.log('Unauthorised user');
} else {
//return this.remote.all_docs();
console.log('Successful login');
}
}
});
this.remote.info()
.then(console.log.bind(console))
.catch(console.log.bind(console));
// https://glebbahmutov.com/blog/synching-db/
this.pdb.replicate.to(this.remote, {
live: true,
retry: true
}).on('change', function (change) {
console.log('data change (replicate TO): ', change)
}).on('error', function (err) {
console.log('sync error (replicate TO): ', err)
});
this.pdb.replicate.from(this.remote, {
live: true,
retry: true
}).on('change', function (change) {
console.log('data change (replicate from): ', change)
}).on('error', function (err) {
console.log('sync error (replicate from): ', err)
});
};
Update:
I have now configured my CouchDB instance with all the parameters required for Proxy Authentication.
My script now looks like this:
/* Login to CouchDB on remote */
var pouchOpts = {
skip_setup: true
};
var ajaxOpts = {
ajax: {
headers: {
'X-Auth-CouchDB-UserName': 'my-user-name',
'X-Auth-CouchDB-Roles': 'user',
'X-Auth-CouchDB-Token': hex_hmac_sha1('couch_secret', 'Brookes'),
'Content-Type': 'application/json; charset=utf-8',
'Access-Control-Allow-Methods': 'GET, DELETE, PUT, OPTIONS',
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With'
}
}
};
var remoteSession = 'https://my-couch-server:6984/_session';
remoteSession.login(ajaxOpts, function (err, response) {
if (err) {
if (err.cname === 'unauthorized' || err.cname === 'forbidden') {
console.log('Unauthorised user');
} else {
//return this.remote.all_docs();
console.log('Successful login');
var remoteDB = new PouchDB('https://my-couch-server:6984/pouchnotes', {skip_setup: true});
}
}
});
/*end couch login*/
The error message I am getting is remoteSession.login is not a function
If I go to _session on my CouchDB host, I get the following:
{"ok":true,"userCtx":{"name":"my-admin-user","roles":["_admin"]},"info":{"authentication_db":"_users","authentication_handlers":["cookie","default"],"authenticated":"default"}}

CouchDB provides a token based authentication mechanism that may help in your case.
I point you to another answer where this mechanism is proposed.
The Proxy Authentication is described here.

Related

no response after axios.post()

Hey I'm working on a Login system on my vue project and have the problem that there seems to be no response from the backend.
This is the backend function:
auth.post('/login', async function (req, res) {
const { email, password } = req.body;
console.log(req);
if(email !== "" && password !== "") {
const account = await User.findOne({ where: { email: email} });
if (account) {
if (await account.validPassword(password)) {
// Generate an access token
const accessToken = jwt.sign({ id: account.id }, SECRET);
const account_data =
{
'id': account.id,
'firstName': account.firstName,
'lastName': account.lastName,
'email': account.email,
'isAdmin': account.isAdmin
}
res.send({accessToken, account_data});
} else {
res.status(200).json("Username or password incorrect");
}
} else {
res.send('Username or password incorrect');
}
} else {
res.send('Username or password incorrect');
}
})
This is the method were I call the action
methods: {
async loginUser(){
let user = await this.$store.dispatch('loginUser', this.loginInfo);
if(user.error){
alert(user.error)
} else {
alert('Thank you for signing in, ' + user.firstName);
}
},
}
This is the action:
async loginUser({commit}, loginInfo){
console.log(loginInfo)
try{
let response = await axios({
method: 'POST',
url: 'http://localhost:4000/api/auth/login',
data: loginInfo,
headers: {
// Overwrite Axios's automatically set Content-Type
'Content-Type': 'application/json'
}});
let user = response.data;
console.log(user);
commit('SET_CURRENT_USER', user);
} catch (e){
alert(e);
return {e}
}
}
Neither the console.log in the try function or in the catch function is triggered.

How to passing result of http request inside async in ExpressJS?

I have below code
async send(user, data) {
const postData = {
'data': 'john',
'secret': 'secret'
};
const dataJson = JSON.stringify(postData);
const options = {
hostname: 'example.com',
path: '/send',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': dataJson.length
}
};
const req = https.request(options, (res) => {
let data = '';
console.log('Status Code:', res.statusCode);
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Body: ', JSON.parse(data));
});
}).on("error", (err) => {
console.log("Error: ", err.message);
});
req.write(dataJson);
req.end();
//---------------
let postResult = // HERE I WANT TO GET WHAT HTTP POST REQUESTED (e.g dataJson.body?)
//---------------
let result;
try {
result = await this.users.collection('users').updateOne(
{
_id: user
},
{
$set: {
// I WANT TO USE THAT HERE
data1 : postResult,
data2 : data2
}
},
{ maxTimeMS: consts.DB_MAX_TIME_USERS }
);
} catch (err) {
log.error('DB', 'UPDATEFAIL id=%s error=%s', user, err.message);
err.message = 'Database Error, failed to update user';
err.code = 'InternalDatabaseError';
throw err;
}
return { success: true };
}
How to get those data to outside variable?
I almost crazy about this, been searching on google and not found anything
I am using express and native-http to make http request, are there any native-curl maybe?
thank you very much for all the help
Your current code is using callback to retrieve result, so you can initiate data variable to outside callback function
let data = '';
const req = https.request(options, (res) => {
console.log('Status Code:', res.statusCode);
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Body: ', JSON.parse(data));
});
})
And also there are other easier way to make http request with nodejs. you can check axios that support Promise and async/await.
you can use syntax like this with axios
const response = await axios.get('/user?ID=12345');
way more easier.

React Native - FETCH Response is empty - API Status 500 - POST Call

signIn = () => {
//post data to express backend
fetch('http://10.0.2.2:3000/api/v1/auth', {
method: 'POST',
header: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: `login=${this.state.login}&password=${this.state.password}`
})
.then((response) => response.json())
.then ((res => {
if(res.status === 200) {
AsyncStorage.setItem('user', this.state.login);
this.props.navigation.navigate('Authorized')
} else {
alert("Response:", res);
}
}))
.done();
}
The above is for React-Native. And below is the express backend:
router.post('/', function(req,res){
var login= req.body.login;
var password = req.body.password;
var sql = `SELECT * FROM users WHERE username = '${login}' OR number = '${login}' AND password = ${password}`
db.query(sql, function (err, rows, fields) {
if (err) {
res.status(500).send({error: 'Something went wrong!'})
} else {
if(rows.length > 0) {
if (rows[0].password == password) {
res.status(200).send({success: 'Login Successful'})
}
} else {
res.status(404).send({error: 'Email or Password does not match!'})
}
}
})
});
I think there is nothing getting into a response or maybe some other problem which I am unable to figure out the moment.

OpenId issue for authentication

I have an embarassing issue with cognito.
My authentication strategy works with current usage but when I try to run tests that sign up a new user and then log it in for an access to other APIs in my website
const authenticationData = {
Username: req.body.email,
Password: req.body.password,
};
const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
const poolData = {
UserPoolId: config.development.UserPoolId,
ClientId: config.development.ClientId,
TokenScopesArray : config.development.TokenScopesArray
};
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
const userData = {
Username: req.body.email,
Pool: userPool,
TokenScopesArray : config.development.TokenScopesArray
};
const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('success')
token = result.getAccessToken().jwtToken;
const idToken = result.idToken.jwtToken;
console.log(token)
res.cookie("accessToken",token)
res.status(200).send(token);
},
onFailure: function (err) {
console.log(err)
res.status(404).send(err)
},`
Then when I try to authenticate with the following code :
app.use(function (req, res, next) {
var token = req.body.token || req.query.token || req.cookies.accessToken || req.headers['x-access-token'];
try {
if (token) {
let promise = new Promise((resolve, reject) => {
const data = null;
const xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
console.log('response', this.responseText);
}
})
xhr.open("GET", "https://gridmanager.auth.us-east-1.amazoncognito.com/oauth2/userInfo");
xhr.setRequestHeader("Authorization", "Bearer " + token);
xhr.setRequestHeader("cache-control", "no-cache");
xhr.setRequestHeader("TokenScopesArray", config.development.TokenScopesArray)
xhr.send(data);
resolve(xhr.responseText)
})
.then(function (response) {
if (response != null) {
res.decoded = response
next();
}
else {
return res.status(404).send('User not authenticated')
}
})
}
else {
console.log('No token')
return res.status(403).send('No token')
}
} catch (error) {
// if there is no token
// return an error
console.log('error')
return res.status(403).send({
success: false,
message: error.message
});
}
I get the following error in xhr.responseText :
{"error":"invalid_token","error_description":"Access token does not contain openid scope"}
And when I log the accessToken I get in the login function, it only has 'aws.cognito.signin.user.admin'
I already tried to change the settings in my appclient but nothing works
Thanks for your help
Unfortunately, only access tokens issued by the Cognito hosted UI can include scopes other than aws.cognito.signin.user.admin. Cognito hosted UI supports OpenId Connect and Cognito API doesn't. It's a big gap in terms of functionality provided by those two. The /oauth2/userInfo endpoint is part of the Hosted UI and it also follows the OpenID Connect spec.
Why do you want to call the /oauth2/userInfo endpoint when you have access to the id_token? The id_token payload has all the information about the user that /oauth2/userInfo would return.

Hapi Lab - how do I have shared state between lab experiments

So I made up the user domain
and I've done I think a good suite
test it's like this:
var Code = require('code');
var Lab = require('lab');
var lab = exports.lab = Lab.script();
var server = require('../../');
var Jwt = require('jsonwebtoken');
var Nconf = require('nconf');
var apiConfig = Nconf.get('api');
lab.experiment('Users', function () {
var userId, payload, decoded, token;
lab.test('create joi required', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload: {
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
password: 'mysecret'
}
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(response.statusCode).to.equal(422);
Code.expect(result.message).to.equal('child "firstname" fails because ["firstname" is required]');
done();
});
});
lab.test('create', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload: {
firstname: 'Fabio',
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
password: 'mysecret'
}
};
server.inject(options, function(response) {
token = response.result.token;
payload = options.payload;
Code.expect(response.statusCode).to.equal(201);
try {
decoded = Jwt.verify(token, apiConfig.secret);
}
catch(err) {
}
console.log(decoded.scope);
Code.expect(decoded.username).to.equal(payload.username);
Code.expect(decoded.scope).to.be.an.array();
userId = decoded.jti;
done();
});
});
lab.test('create sequelize unique', function (done) {
var options = {
method: 'POST',
url: '/api/users',
payload: {
firstname: 'Fabio',
lastname: 'Bedini',
username: 'whisher',
email: 'me#ilwebdifabio.it',
password: 'mysecret'
}
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(result.message).to.equal('username must be unique');
Code.expect(response.statusCode).to.equal(422);
done();
});
});
lab.test('update at least one required', function (done) {
var options = {
method: 'PUT',
headers:{'Authorization' : 'Bearer ' + token},
url: '/api/users/'+userId
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(response.statusCode).to.equal(422);
done();
});
});
lab.test('update no password', function (done) {
var options = {
method: 'PUT',
headers:{'Authorization' : 'Bearer ' + token},
url: '/api/users/'+userId,
payload: {
password: 'mysecret_update'
}
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(response.statusCode).to.equal(422);
done();
});
});
lab.test('update not owner', function (done) {
var options = {
method: 'PUT',
headers:{'Authorization' : 'Bearer ' + token},
url: '/api/users/'+userId +1,
payload: {
firstname: 'Fabio_update'
}
};
server.inject(options, function(response) {
Code.expect(response.statusCode).to.equal(403);
done();
});
});
lab.test('update', function (done) {
var updatevalue = Math.random().toString(36).slice(2);
var options = {
method: 'PUT',
headers:{'Authorization' : 'Bearer ' + token},
url: '/api/users/'+userId,
payload: {
firstname: 'Fabio_'+updatevalue,
lastname: 'Bedini_'+updatevalue,
username: 'whisher_'+updatevalue,
email: 'me_'+updatevalue+'#ilwebdifabio.it'
}
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(response.statusCode).to.equal(200);
Code.expect(result).to.equal(1);
done();
});
});
lab.test('findById not owner', function (done) {
var options = {
method: 'GET',
headers:{'Authorization' : 'Bearer ' + token},
url: '/api/users/'+userId +1,
};
server.inject(options, function(response) {
Code.expect(response.statusCode).to.equal(403);
done();
});
});
lab.test('findById', function (done) {
var options = {
method: 'GET',
headers:{'Authorization' : 'Bearer ' + token},
url: '/api/users/'+userId
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(response.statusCode).to.equal(200);
Code.expect(result).to.be.instanceof(Object);
Code.expect(Object.keys(result)).to.have.length(8);
done();
});
});
lab.test('destroy not owner', function (done) {
var options = {
method: 'DELETE',
headers:{'Authorization' : 'Bearer ' + token},
url: '/api/users/'+userId+1
};
server.inject(options, function(response) {
Code.expect(response.statusCode).to.equal(403);
done();
});
});
lab.test('destroy', function (done) {
var options = {
method: 'DELETE',
headers:{'Authorization' : 'Bearer ' + token},
url: '/api/users/'+userId
};
server.inject(options, function(response) {
var result = response.result;
Code.expect(response.statusCode).to.equal(200);
Code.expect(result).to.equal(1);
done();
});
});
});
now I made up the product domain as well and there
is some routes needed access token.
How can I test the product domain using the 'globals'
var userId, payload, decoded, token;
or what's the way to turn in this case ?
Update
after #Matt Harrison answer
var getToken = function(){
var user = {id:1,username:'abcdefghijklmnopqrstuvwxyz'};
var token = JwtUtil.getUserToken(user).token;
return token;
}
lab.experiment('Product', function () {
lab.test('create', function (done) {
var token = getToken();
console.log(token);
var options = {
method: 'POST',
headers:{'Authorization' : 'Bearer ' + token},
url: '/api/products',
payload: {
title: 'myproduct'
}
};
server.inject(options, function(response) {
var result = response.result;
console.log(result);
Code.expect(response.statusCode).to.equal(200);
// Code.expect(result.message).to.equal('child "firstname" fails because ["firstname" is required]');
done();
});
});
});
it workish I've an other problem
with
CONSTRAINT products_ibfk_1 FOREIGN KEY (userId) REFERENCES users
(id) ON UPDATE CASCADE) stack: Error: ER_NO_REFERENCED_ROW
Not sure I have understood your question correctly but I think you're asking:
How do I have shared state between lab experiments?
The simple answer is to move var userId, payload, decoded, token; into the upper "global" scope so they can be accessed everywhere.
However my advice is don't do this. Your test cases should be independent from each other. You should be able to add tests, reorder them and remove them without it affecting other test cases. Otherwise you're creating a maintenance nightmare for yourself.
If you need a resource in multiple test cases, just create a new one for each test:
lab.test('test the thing', function (done) {
var token = getToken();
expect(token).to.equal(...);
});
lab.test('test the other thing', function (done) {
var token = getToken();
expect(token).to.equal(...);
});
If you need to change a resource and then test something, that's a single test case:
lab.test('change something and then test it', function (done) {
var token = getToken();
token.prop = 'value';
expect(token.prop).to.equal('value');
});