How do sync PoucDB to Cloudant by using user login? My Cloudant is set up with IAM and Legacy.
How do I register users and use the credentials to login to use Cloudant and PouchDB
I have not found answers in Cloudant, PouchDB docs. And do not see my answer in Stackoverflow
I am using the PouchDB docs:
var db = new PouchDB('todos');
const remoteUrl = "https://some address"
const remoteOptions = {
'auth': { 'username': user , 'password': pass } }
const remoteDb = new PouchDB(remoteUrl, remoteOptions);
localDB.sync(remoteDB).on('complete', function () {
// yay, we're in sync!
}).on('error', function (err) {
// boo, we hit an error!
});
Replication does work from Cloudant to on-premise Couchdb. Therefore I can connect to Coudant remotely.
As you have Legacy Auth available, I won't worry about IAM authentication. We can simply generate an API key/password for your mobile client and use legacy authentication.
Creating an API key
In the Cloudant Dashboard, choose the "Permissions" menu and click the "Generate API Key" button. You should see a generated Key and Password - these become the Username & Password in the URL you feed to PouchDB.
If you're doing a sync (i.e data is to flow in both directions) then your API key needs both _reader & _writer permissions. If data is to flow from server to mobile only, then _reader & _replicator will suffice.
Client-side code
Your client side code is pretty much correct:
remember that Cloudant only accepts HTTPS connections
the Key from your API Key/Password pair becomes your username
the Password from your API Key/Password pair becomes your password
e.g.
const PouchDB = require('pouchdb')
const db = new PouchDB('todos')
const username = 'KEY'
const password = 'PASSWORD'
const host = 'mycloudservice.cloudant.com'
const databaseName = 'todos'
const remoteDB = `https://${username}:${password}#${host}/${databaseName}`
db.sync(remoteDB).on('complete', async function () {
console.log('done')
}).on('error', function (err) {
console.error(err)
});
Related
I am using Firebase Cloud Functions with Express and Firebase Hosting to serve my multi-page site. I have successfully implemented server-side cookies as explained here and as implemented below:
function verifyLogin(request, response, next) {
const sessionCookie = request.cookies.__session || '';
firebase.auth().verifySessionCookie(sessionCookie, true /** checkRevoked */ )
.then((decodedClaims) => {
//serve content for user
return next();
}).catch(error => {
// Session cookie is unavailable or invalid. Force user to log in.
console.log(error);
response.redirect('/login');
return;
});
}
app.get('/', verifyLogin, (request, response) => {
var page_title = "Home";
response.render('index', {
page_title,
});
});
I am using the Firebase Web SDK (JavaScript) to access the Firebase Cloud Firestore. In order to do this, I need to get the idToken on the client side for the currently-logged-in user, like so:
firebase.auth().onAuthStateChanged(firebaseUser => {
if (firebaseUser) {
firebase.auth().currentUser.getIdTokenResult()
.then((idTokenResult) => {
// Access the Firebase Cloud Firestore here
});
}
});
This seems redundant to me, since I already know which user is signed in via my server cookie, but how else can I get the idToken without another call to Firebase Auth?
Is there a way to extrapolate it from my session cookie somehow and pass it to my client from my cloud function as a variable?
Or is there another way to look at this philosophically that I am overlooking?
In the same way you can listen to user sign-in state changes using onAuthStateChanged(), you can also listen to user ID token changes using onIdTokenChanged(). You will only need to use a new token when the observer you attach shows that a new one appears (for example, when it's refreshed every hour by the SDK).
I want to authenticate users using Cognito, with option to use Facebook. User can sign_in/sign_up using either of those options.
I have created Cognito User Pool and Cognito Federated Identity, and also I have created Facebook App for authentication. Both User Pool and Facebook app are connected to Federated identity.
When I sign_up and later authenticate Cognito User via Cognito User Pool, then Cognito returns accessToken, which I store in localStorage on front and use whenever needed for athentication.
I have /authenticate endpoint (express), that takes in username & password, and returns accessToken if all went well. Whenever I make API call that requires auth, I send accessToken that I have in local storage. It goes, more or less as this:
// POST user/authenticate
const authenticationData = {
Username: username,
Password: password
}
authenticationDetails = new AuthenticationDetails(authenticationData)
const userData = {
Username: username,
Pool: userPool()
}
cognitoUser = new CognitoUser(userData)
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: (res) => resolve(res), // here I get accessToken
onFailure: (err) => {
console.log('[authenticateUser error]', err)
reject(err)
},
//...
However
When I use Facebook, I do not get accessToken I could use in same fashion. I get accessToken from Facebook via FB.login, I pass it to Cognito to authenticate, and then I don't know what to do, because I cannot get any token that could be used to authenticate API calls, that require Cognito Authentication.
Here's what I do:
await window.FB.login((response) => {
props.userFacebookSignIn(response)
})
// ...
call(foo, 'users/facebook_sign_in', { accessToken: payload.facebookAccessToken })
// ...
// users/facebook_sign_in
AWS.config.region = config.AWSRegion
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'foo',
Logins: {
'graph.facebook.com': facebookAccessToken
}
})
AWS.config.credentials.get((err) => {
// Here I get no errors, I presume that I have logged Facebook user in
const accessKeyId = AWS.config.credentials.accessKeyId
const secretAccessKey = AWS.config.credentials.secretAccessKey
const sessionToken = AWS.config.credentials.sessionToken
// here I can do stuff probably,
// but I would like to receive token that would allow me to do stuff,
// rather than context I can do stuff in
})
While I am doing all of this, I have this feeling, that devs at AWS implemented Cognito as frontend solution, rather than something to be used in backend. Correct me if I am wrong.
Nevertheless, I would like to be able authenticate api calls using Cognito and Facebook interchangeably in express middleware.
Is that possible? Thanks.
I have used federated identity for salesforce single sign on but i imagine the steps will the same. After authenticating with facebook you will recieve and id_token from them in response. You have to pass this as a parameter in the getId method:
var params = {
IdentityPoolId: 'STRING_VALUE', /* required */
AccountId: 'STRING_VALUE',
Logins: {
'<IdentityProviderName>': 'STRING_VALUE',
/* 'graph.facebook.com': ... */
}
};
cognitoidentity.getId(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
In the result you will get an identity id which you can save somewhere so that you don't have to make this call everytime while authenticating. Now take this identity id and make the getCredentialsForIdentity call:
response = client.get_credentials_for_identity(
IdentityId='string',
Logins={
'string': 'string'
},
CustomRoleArn='string'
)
This will finally give you the temporary access key, secret key and session key you need.
I decided to use oAuth.
Here's quick & dirty look on how it's done
In AWS Cognito
1) Set up Cognito User Pool. Add App Client save App client id & App client secret as COGNITO_CLIENT_ID and COGNITO_CLIENT_SECRET
2) Go to Federation > Identity providers and add your Facebook app ID and App secret (both you will find in Facebook app panel)
3) Go to App integration > App client settings click "Select all", set up your Callback URL, mine is localhost:5000/facebook also select Authorization code grant and Allowed OAuth Scopes (save scopes to say: COGNITO_SCOPES)
4) Now go to App integration > Domain name and enter your custom domain; let's say example-app-debug so it's: https://example-app-debug.auth.us-east-1.amazoncognito.com
That's all there is to Cognito
no the Facebook part
5) Settings > Basic add example-app-debug.auth.us-east-1.amazoncognito.com to your App domains - Save Changes
6) In Facebook Login > Settings in Valid OAuth Redirect URIs add this URL: https://example-app-debug.auth.us-east-1.amazoncognito.com/oauth2/idpresponse and Save Changes
and the code
In browser, redirect user to this url when Login w. Facebook button is clicked:
window.location.href =
`https://example-app-debug.auth.us-east-1.amazoncognito.com/oauth2/authorize` +
`?identity_provider=Facebook` +
`&redirect_uri=http://localhost:5000/facebook` +
`&response_type=code` +
`&client_id=${COGNITO_CLIENT_ID}` +
`&scope=${COGNITO_SCOPES}`
this call should come back to you with a code, like this: http://localhost:5000/facebook?code=foo-bar-code Send this code to your backend.
In backend, do this:
const axios = require('axios')
const url = `` +
`https://${COGNITO_CLIENT_ID}:${COGNITO_CLIENT_SECRET}` +
`#example-app-debug.auth.us-east-1.amazoncognito.com/oauth2/token` +
`?grant_type=authorization_code` +
`&code=foo-bar-code` + // <- code that came from Facebook
`&redirect_uri=http://localhost:5000/facebook` +
`&client_id=${COGNITO_CLIENT_ID}`
const response = await axios.post(url)
// response should have access_token, refresh_token and id_token in data
You send access_token, refresh_token and id_token back to frontend and save them in local storage and use them to authenticate and Done.
I have an android app in which I send push notification using firebase admin sdk with nodejs.
I was able to send notification from the nodejs raw script when I ran the script.
However, I just built an admin dashboard for sending notification to the app with angular 6 but don't know how to integrate the nodejs script with the new angular app so that I can send notification from the angular app by just a click.
I'd also encourage new ideas on how best to to this.
attached is a screenshot from the nodejs admin script
Setup your node to behave as API server, using Express for example.
Wrap your script as Express module (named send-message.js), basically just make that a function that you export:
const sendMessage = (...params) => {
//your send message logic, I would do copy paste of your code here however it is an image
}
module.exports = sendMessage;
Well and then setup API route that calls the script:
var express = require('express')
var sendMessage = require('./send-message')
var app = express()
app.get('/send-message', function (req, res) {
sendMessage(....);
res.status(200).end();
})
app.listen(3000)
And finally in Angular use HttpClient to call the API.
I finally solved the problem by using firebase cloud functions.
First I set up cloud functions on firebase with this guide
Then I created a cloud function named sendNotification() which is triggered each time new objects are inserted to the firebase realtime database.
Then I placed my existing notification code inside sendNotification() function
Deployed the function to my firebase console
Then hurray, the notification was sent to my device after some db triggers
`
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
//This functions listens to the node '/Food menu/date/Food' for new insert and sends notification to a client
exports.sendNotification = functions.database.ref('/Food menu/date/Food')
.onCreate((snapshot, context) => {
//place your client app registration token here, this is created when a user first opens the app and it is stored in the db.
//You could also retrieve the token from the db but in this case it is hard coded
var registrationToken = "{my-registration-token}";
//This is the payload for notification
var payload = {
data: {
'title': 'Tomorrow\'s Menu',
'message': 'Hello, kindly check the menu available for today',
'is_background': 'true',
'image': 'http://www.allwhitebackground.com/images/3/3430.jpg',
'timestamp': '234'
}
};
// Send a message to the device corresponding to the provided
// registration token.
admin.messaging().sendToDevice(registrationToken, payload)
.then((response) => {
// Response is a message ID string.
console.log('Successfully sent message:', response);
//return a promise here since this function is asynchronous
return "Yes";
})
.catch((error) => {
console.log('Error sending message:', error);
});
//return snapshot.ref.parent.child('uppercaseFood').set(uppercase);
});
`
After this, you run firebase deploy --only functionsto deploy the cloud function
Read this guide for more info on cloud functions
I'm using only the Real-Time Database part of Firebase.
I have a Python Firebase admin on the server, a Javascript Firebase on the web client. No Android or iOS. My web Javascript client receives a token from the Python server for its authentication.
Only the Python server writes to Firebase, the Javascript client never does. But the client registers a "child_added" listener with Firebase to receive database changes in data.
Everything works except for this event listener.
The Firebase docs say:
The Python Admin SDK currently only supports blocking reads. It cannot
be used to add event listeners that receive realtime update
notifications.
But as I understand it, that applies to my server, not the client. Or does getting a custom token from a Python Admin disqualify the Javascript client for these events?
Maybe it's a problem that the server initializes itself with service account credentials, while the client does so with an api key? If so, why doesn't Firebase issue an error?
Here's the relevant Python code (views.py):
import firebase_admin
from firebase_admin import credentials, auth, db
def getFirebaseInstance():
try:
# using fresh download of key from service account for this app
cred = credentials.Certificate('myapp/static/myap/firebase/firebase-adminsdk-4mcup-144fd3c404.json')
firebase = firebase_admin.initialize_app(cred,{'databaseURL': 'https://my-db-url'})
except:
raise
#login_required()
def firebaseClientToken(request):
try:
global __firebase
if __firebase is None:
__firebase = getFirebaseInstance()
token = firebase_admin.auth.create_custom_token('1')
return HttpResponse(token)
except Exception as err:
return HttpResponse("System error:" + str(err), status=406)
def sendToFirebase(request, data):
try:
global __firebase
if not __firebase:
__firebase = getFirebaseInstance()
db = firebase_admin.db
ref = db.reference("messages/")
ref.push(json.dumps(data))
except Exception:
raise
All that works fine as far as the server being able to write to firebase and for generating a custom Token.
The javascript web client receives a token from the Python server (not shown), and registers the event listener with:
(
function authClient2Firebase() {
var waiting;
$.ajax({
url: "firebaseClientToken/",
method: "POST",
success: function(response) { step2(response); },
error: function(xhr) { alert(xhr.responseText); }
});
function step2(customToken) {
try {
firebase.auth().signInWithCustomToken(customToken).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
alert(errorMessage + ' code:' + errorCode);
});
var db = firebase.database();
var fbRef = db.ref("myapp");
fbRef.startAt(loadTransaction.time).on ( "child_added",
function(snapshot) { alert(snapshot.val()); });
}
}
}
)();
It doesn't have to do with tokens or authentication at all, but with a difference between the push(data) command in the Firebase Python and Javascript SDK vs. their documentation.
See the accepted answer at:
Why does Firebase event return empty object on second and subsequent events?
I've created a dashboard that aggregates order processing for multiple Shopify stores. It's a private app so I save each Shopify store's API credentials and webhook secret in my database. I've encrypted all this sensitive stuff with a key that's stored as an environment variable. For any given event, I direct all Shopify stores to the same callback URL. Hence for each url I'll have to verify the request body against all possible webhook secrets, which are stored in the database. I have 2 questions:
Is this an acceptable approach for securing the stores' API credentials and webhook secrets?
How do I synchronously verify the request body if my secrets are stored in the database? My verify function is below.
verify: (buffer, hmacHeader) => {
Brand.find().exec((err, brands) => {
if (!err && brands) {
const allWebhookSecrets = brands.map(brand => {
console.log(`encrypted webhook secret is: ${brand.shopify_webhook_secret_encrypted}`)
return encryption.decrypt(brand.shopify_webhook_secret_encrypted);
});
const webhookIsValid = allWebhookSecrets.some(secret => {
var hmac = crypto.createHmac('sha256', secret)
.update(buffer)
.digest('base64');
return hmac == hmacHeader;
});
console.log(`webhookIsValid: ${webhookIsValid}`);
return webhookIsValid;
}
return false;
});
}
Generally the only thing you should really be storing in your database is the stores access token for your app and any other verification data for the store login. Webhook signatures and generated differently every time a request is sent to your app using your apps shared secret and the data the webhook is about to send to you.
Webhooks are verified using your apps shared secret and the post request data to the HMAC signature sent in the header: X-Shopify-Hmac-SHA256.
This is my simplified nodejs implementation of a webhook verification middleware
function verify_webhook(postData, hmac) {
if (!hmac || !postData || typeof postData !== 'object') {
return false;
}
const calculatedSignature = crypto.createHmac('sha256', process.env.SHOPIFY_SHARED_SECRET)
.update(postData)
.digest('hex');
return calculatedSignature === hmac;
}
where postData and hmac are JSON.stringify(req.body) and req.get('X-Shopify-Hmac-SHA256')
There's generally no point storing this information in a database and just calculating it and verifying it on a fly using middleware. I hope this helps.