Is it possible to integrate Amazon QuickSight dashboard graphs to a web application? - mysql

I need to display live interactive graphs based on customer data present in MySQL,for generating the graphs, I am planning to use Amazon Quick Sight but i would like to know whether the generated graphs can be integrated with my web application UI ?
Datasource MYSQL is hosted in AWS.
Any other better design solution is also most welcome :)

I don't think so. Even if you want to share the dashboard to
someone, you need to create a user in QuickSight. Any more than 1
user will be charged by AWS.
The dashboard cannot be public and you need to login to view the
dashboard. If it was public, you could have embedded it in your
webpage as an iframe. But you cannot.
So, I think you are having limited options here, when it comes to
QuickSight.
You can always using D3 or Google Charts to display the data by
exposing REST services for your data in MySQL.
If you have a huge database, you may want to consider indexing the
data to Elasticsearch and perform queries on it.
Check if Kibana + Elasticsearch works out of the box for you.
Good luck!
Update: Dec 28 2018
Amazon announced in Nov 2018, that Amazon QuickSight dashboards can now be embedded in applications. Read more here at this AWS QuickSight Update.

AWS has enabled the embedding of the Dashboards into web apps. The feature was released on 27th Nov 2018. Here are a few helpful links:
1. https://aws.amazon.com/blogs/big-data/embed-interactive-dashboards-in-your-application-with-amazon-quicksight/
2. https://docs.aws.amazon.com/quicksight/latest/user/embedded-dashboards-setup.html

Note: This answer is applicable only if you are using AWS Cognito
In order to generate Quicksight secure dashboard URL, follow the below steps:
Step 1: Create a new Identity Pool. Go to https://console.aws.amazon.com/cognito/home?region=u-east-1 , click ‘Create new Identity Pool’
Give an appropriate name.
Go to the Authentication Providers section, select Cognito.
Give the User Pool ID(your User pool ID) and App Client ID (go to App
Clients in user pool and copy id).
Click ‘Create Pool’. Then click ‘Allow’ to create roles of the
identity pool in IAM.
Step 2: Assign Custom policy to the Identity Pool Role
Create a custom policy with the below JSON.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "quicksight:RegisterUser",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "quicksight:GetDashboardEmbedUrl",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "sts:AssumeRole",
"Resource": "*",
"Effect": "Allow"
}
]
}
Note: if you want to restrict the user to only one dashboard, replace the * with the dashboard ARN name in quicksight:GetDashboardEmbedUrl,
then goto the roles in IAM.
select the IAM role of the Identity pool and assign the custom policy
to the role.
Step 3: Configuration for generating the temporary IAM(STS) user
Login to your application with the user credentials.
For creating temporary IAM user, we use Cognito credentials.
When user logs in, Cognito generates 3 token IDs - IDToken,
AccessToken, RefreshToken. These tokens will be sent to your application server.
For creating a temporary IAM user, we use Cognito Access Token and credentials will look like below.
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId:"Identity pool ID",
Logins: {
'cognito-idp.us-east-1.amazonaws.com/UserPoolID': AccessToken
}
});
For generating temporary IAM credentials, we call sts.assume role
method with the below parameters.
var params = {
RoleArn: "Cognito Identity role arn",
RoleSessionName: "Session name"
};
sts.assumeRole(params, function (err, data) {
if (err) console.log( err, err.stack); // an error occurred
else {
console.log(data);
})
You can add additional parameters like duration (in seconds) for the
user.
Now, we will get the AccessKeyId, SecretAccessKey and Session
Token of the temporary user.
Step 4: Register the User in Quicksight
With the help of same Cognito credentials used in Step 3, we will
register the user in quicksight by using the quicksight.registerUser
method with the below parameters
var params = {
AwsAccountId: “account id”,
Email: 'email',
IdentityType: 'IAM' ,
Namespace: 'default',
UserRole: ADMIN | AUTHOR | READER | RESTRICTED_AUTHOR | RESTRICTED_READER,
IamArn: 'Cognito Identity role arn',
SessionName: 'session name given in the assume role creation',
};
quicksight.registerUser(params, function (err, data1) {
if (err) console.log("err register user”); // an error occurred
else {
// console.log("Register User1”);
}
});
Now the user will be registered in quicksight.
Step5: Update AWS configuration with New credentials.
Below code shows how to configure the AWS.config() with new
credentials generated Step 3.
AWS.config.update({
accessKeyId: AccessToken,
secretAccessKey: SecretAccessKey ,
sessionToken: SessionToken,
"region": Region
});
Step6: Generate the EmbedURL for Dashboards:
By using the credentials generated in Step 3, we will call the
quicksight.getDashboardEmbedUrl with the below parameters
var params = {
AwsAccountId: "account ID",
DashboardId: "dashboard Id",
IdentityType: "IAM",
ResetDisabled: true,
SessionLifetimeInMinutes: between 15 to 600 minutes,
UndoRedoDisabled: True | False
}
quicksight.getDashboardEmbedUrl(params,
function (err, data) {
if (!err) {
console.log( data);
} else {
console.log(err);
}
}
);
Now, we will get the embed url for the dashboard.
Call the QuickSightEmbedding.embedDashboard from front end with the
help of the above generated url.
The result will be the dashboard embedded in your application with
filter controls.

I know this is a very late reply, but just in case someone else stumbles across this question... We use periscopedata.com to embed BI dashboards in our SaaS app. All that's needed is knowledge of SQL (to create the charts/dashboards) and enough dev knowledge to call their API endpoint to display the dash in your own app.

Related

Google Sheets API OAuth Refresh Token Only Issued Once Per Account [duplicate]

I want to get the access token from Google. The Google API says that to get the access token, send the code and other parameters to token generating page, and the response will be a JSON Object like :
{
"access_token" : "ya29.AHES6ZTtm7SuokEB-RGtbBty9IIlNiP9-eNMMQKtXdMP3sfjL1Fc",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/HKSmLFXzqP0leUihZp2xUt3-5wkU7Gmu2Os_eBnzw74"
}
However, I'm not receiving the refresh token. The response in my case is:
{
"access_token" : "ya29.sddsdsdsdsds_h9v_nF0IR7XcwDK8XFB2EbvtxmgvB-4oZ8oU",
"token_type" : "Bearer",
"expires_in" : 3600
}
The refresh_token is only provided on the first authorization from the user. Subsequent authorizations, such as the kind you make while testing an OAuth2 integration, will not return the refresh_token again. :)
Go to the page showing Apps with access to your account:
https://myaccount.google.com/u/0/permissions.
Under the Third-party apps menu, choose your app.
Click Remove access and then click Ok to confirm
The next OAuth2 request you make will return a refresh_token (providing that it also includes the 'access_type=offline' query parameter.
Alternatively, you can add the query parameters prompt=consent&access_type=offline to the OAuth redirect (see Google's OAuth 2.0 for Web Server Applications page).
This will prompt the user to authorize the application again and will always return a refresh_token.
In order to get the refresh token you have to add both approval_prompt=force and access_type="offline"
If you are using the java client provided by Google it will look like this:
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, getClientSecrets(), scopes)
.build();
AuthorizationCodeRequestUrl authorizationUrl =
flow.newAuthorizationUrl().setRedirectUri(callBackUrl)
.setApprovalPrompt("force")
.setAccessType("offline");
I'd like to add a bit more info on this subject for those frustrated souls who encounter this issue. The key to getting a refresh token for an offline app is to make sure you are presenting the consent screen. The refresh_token is only returned immediately after a user grants authorization by clicking "Allow".
The issue came up for me (and I suspect many others) after I'd been doing some testing in a development environment and therefore already authorized my application on a given account. I then moved to production and attempted to authenticate again using an account which was already authorized. In this case, the consent screen will not come up again and the api will not return a new refresh token. To make this work, you must force the consent screen to appear again by either:
prompt=consent
or
approval_prompt=force
Either one will work but you should not use both. As of 2021, I'd recommend using prompt=consent since it replaces the older parameter approval_prompt and in some api versions, the latter was actually broken (https://github.com/googleapis/oauth2client/issues/453). Also, prompt is a space delimited list so you can set it as prompt=select_account%20consent if you want both.
Of course you also need:
access_type=offline
Additional reading:
Docs: https://developers.google.com/identity/protocols/oauth2/web-server#request-parameter-prompt
Docs: https://developers.google.com/identity/protocols/oauth2/openid-connect#re-consent
Discussion about this issue: https://github.com/googleapis/google-api-python-client/issues/213
I searched a long night and this is doing the trick:
Modified user-example.php from admin-sdk
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$authUrl = $client->createAuthUrl();
echo "<a class='login' href='" . $authUrl . "'>Connect Me!</a>";
then you get the code at the redirect url
and the authenticating with the code and getting the refresh token
$client()->authenticate($_GET['code']);
echo $client()->getRefreshToken();
You should store it now ;)
When your accesskey times out just do
$client->refreshToken($theRefreshTokenYouHadStored);
This has caused me some confusion so I thought I'd share what I've come to learn the hard way:
When you request access using the access_type=offline and approval_prompt=force parameters you should receive both an access token and a refresh token. The access token expires soon after you receive it and you will need to refresh it.
You correctly made the request to get a new access token and received the response that has your new access token. I was also confused by the fact that I didn't get a new refresh token. However, this is how it is meant to be since you can use the same refresh token over and over again.
I think some of the other answers assume that you wanted to get yourself a new refresh token for some reason and sugggested that you re-authorize the user but in actual fact, you don't need to since the refresh token you have will work until revoked by the user.
Rich Sutton's answer finally worked for me, after I realized that adding access_type=offline is done on the front end client's request for an authorization code, not the back end request that exchanges that code for an access_token. I've added a comment to his answer and this link at Google for more info about refreshing tokens.
P.S. If you are using Satellizer, here is how to add that option to the $authProvider.google in AngularJS.
In order to get the refresh_token you need to include access_type=offline in the OAuth request URL. When a user authenticates for the first time you will get back a non-nil refresh_token as well as an access_token that expires.
If you have a situation where a user might re-authenticate an account you already have an authentication token for (like #SsjCosty mentions above), you need to get back information from Google on which account the token is for. To do that, add profile to your scopes. Using the OAuth2 Ruby gem, your final request might look something like this:
client = OAuth2::Client.new(
ENV["GOOGLE_CLIENT_ID"],
ENV["GOOGLE_CLIENT_SECRET"],
authorize_url: "https://accounts.google.com/o/oauth2/auth",
token_url: "https://accounts.google.com/o/oauth2/token"
)
# Configure authorization url
client.authorize_url(
scope: "https://www.googleapis.com/auth/analytics.readonly profile",
redirect_uri: callback_url,
access_type: "offline",
prompt: "select_account"
)
Note the scope has two space-delimited entries, one for read-only access to Google Analytics, and the other is just profile, which is an OpenID Connect standard.
This will result in Google providing an additional attribute called id_token in the get_token response. To get information out of the id_token, check out this page in the Google docs. There are a handful of Google-provided libraries that will validate and “decode” this for you (I used the Ruby google-id-token gem). Once you get it parsed, the sub parameter is effectively the unique Google account ID.
Worth noting, if you change the scope, you'll get back a refresh token again for users that have already authenticated with the original scope. This is useful if, say, you have a bunch of users already and don't want to make them all un-auth the app in Google.
Oh, and one final note: you don't need prompt=select_account, but it's useful if you have a situation where your users might want to authenticate with more than one Google account (i.e., you're not using this for sign-in / authentication).
1. How to get 'refresh_token' ?
Solution: access_type='offline' option should be used when generating authURL.
source : Using OAuth 2.0 for Web Server Applications
2. But even with 'access_type=offline', I am not getting the 'refresh_token' ?
Solution: Please note that you will get it only on the first request, so if you are storing it somewhere and there is a provision to overwrite this in your code when getting new access_token after previous expires, then make sure not to overwrite this value.
From Google Auth Doc : (this value = access_type)
This value instructs the Google authorization server to return a
refresh token and an access token the first time that your application
exchanges an authorization code for tokens.
If you need 'refresh_token' again, then you need to remove access for your app as by following the steps written in Rich Sutton's answer.
I'm using nodejs client for access to private data
The solution was add the promp property with value consent to the settings object in oAuth2Client.generateAuthUrl function.
Here is my code:
const getNewToken = (oAuth2Client, callback) => {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
prompt: 'consent',
scope: SCOPES,
})
console.log('Authorize this app by visiting this url:', authUrl)
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
rl.question('Enter the code from that page here: ', (code) => {
rl.close()
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error while trying to retrieve access token', err)
oAuth2Client.setCredentials(token)
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err)
console.log('Token stored to', TOKEN_PATH)
})
callback(oAuth2Client)
})
})
}
You can use the online parameters extractor to get the code for generate your token:
Online parameters extractor
Here is the complete code from google official docs:
https://developers.google.com/sheets/api/quickstart/nodejs
I hope the information is useful
Setting this will cause the refresh token to be sent every time:
$client->setApprovalPrompt('force');
an example is given below (php):
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->addScope("email");
$client->addScope("profile");
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
For me I was trying out CalendarSampleServlet provided by Google. After 1 hour the access_key times out and there is a redirect to a 401 page. I tried all the above options but they didn't work. Finally upon checking the source code for 'AbstractAuthorizationCodeServlet', I could see that redirection would be disabled if credentials are present, but ideally it should have checked for refresh token!=null. I added below code to CalendarSampleServlet and it worked after that. Great relief after so many hours of frustration . Thank God.
if (credential.getRefreshToken() == null) {
AuthorizationCodeRequestUrl authorizationUrl = authFlow.newAuthorizationUrl();
authorizationUrl.setRedirectUri(getRedirectUri(req));
onAuthorization(req, resp, authorizationUrl);
credential = null;
}
Using offline access and prompt:consent worked well to me:
auth2 = gapi.auth2.init({
client_id: '{cliend_id}'
});
auth2.grantOfflineAccess({prompt:'consent'}).then(signInCallback);
In order to get new refresh_token each time on authentication the type of OAuth 2.0 credentials created in the dashboard should be "Other". Also as mentioned above the access_type='offline' option should be used when generating the authURL.
When using credentials with type "Web application" no combination of prompt/approval_prompt variables will work - you will still get the refresh_token only on the first request.
To get a refresh token using postman, here is an example of the configurations
Expected Response
now google had refused those parameters in my request (access_type, prompt)... :( and there is no "Revoke Access" button at all. I'm frustrating because of getting back my refresh_token lol
UPDATE:
I found the answer in here :D you can get back the refresh token by a request
https://developers.google.com/identity/protocols/OAuth2WebServer
curl -H "Content-type:application/x-www-form-urlencoded" \
https://accounts.google.com/o/oauth2/revoke?token={token}
The token can be an access token or a refresh token. If the token is an access token and it has a corresponding refresh token, the refresh token will also be revoked.
If the revocation is successfully processed, then the status code of the response is 200. For error conditions, a status code 400 is returned along with an error code.
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010_000;
use utf8;
binmode STDOUT, ":encoding(utf8)";
use Text::CSV_XS;
use FindBin;
use lib $FindBin::Bin . '/../lib';
use Net::Google::Spreadsheets::V4;
use Net::Google::DataAPI::Auth::OAuth2;
use lib 'lib';
use Term::Prompt;
use Net::Google::DataAPI::Auth::OAuth2;
use Net::Google::Spreadsheets;
use Data::Printer ;
my $oauth2 = Net::Google::DataAPI::Auth::OAuth2->new(
client_id => $ENV{CLIENT_ID},
client_secret => $ENV{CLIENT_SECRET},
scope => ['https://www.googleapis.com/auth/spreadsheets'],
);
my $url = $oauth2->authorize_url();
# system("open '$url'");
print "go to the following url with your browser \n" ;
print "$url\n" ;
my $code = prompt('x', 'paste code: ', '', '');
my $objToken = $oauth2->get_access_token($code);
my $refresh_token = $objToken->refresh_token() ;
print "my refresh token is : \n" ;
# debug p($refresh_token ) ;
p ( $objToken ) ;
my $gs = Net::Google::Spreadsheets::V4->new(
client_id => $ENV{CLIENT_ID}
, client_secret => $ENV{CLIENT_SECRET}
, refresh_token => $refresh_token
, spreadsheet_id => '1hGNULaWpYwtnMDDPPkZT73zLGDUgv5blwJtK7hAiVIU'
);
my($content, $res);
my $title = 'My foobar sheet';
my $sheet = $gs->get_sheet(title => $title);
# create a sheet if does not exit
unless ($sheet) {
($content, $res) = $gs->request(
POST => ':batchUpdate',
{
requests => [
{
addSheet => {
properties => {
title => $title,
index => 0,
},
},
},
],
},
);
$sheet = $content->{replies}[0]{addSheet};
}
my $sheet_prop = $sheet->{properties};
# clear all cells
$gs->clear_sheet(sheet_id => $sheet_prop->{sheetId});
# import data
my #requests = ();
my $idx = 0;
my #rows = (
[qw(name age favorite)], # header
[qw(tarou 31 curry)],
[qw(jirou 18 gyoza)],
[qw(saburou 27 ramen)],
);
for my $row (#rows) {
push #requests, {
pasteData => {
coordinate => {
sheetId => $sheet_prop->{sheetId},
rowIndex => $idx++,
columnIndex => 0,
},
data => $gs->to_csv(#$row),
type => 'PASTE_NORMAL',
delimiter => ',',
},
};
}
# format a header row
push #requests, {
repeatCell => {
range => {
sheetId => $sheet_prop->{sheetId},
startRowIndex => 0,
endRowIndex => 1,
},
cell => {
userEnteredFormat => {
backgroundColor => {
red => 0.0,
green => 0.0,
blue => 0.0,
},
horizontalAlignment => 'CENTER',
textFormat => {
foregroundColor => {
red => 1.0,
green => 1.0,
blue => 1.0
},
bold => \1,
},
},
},
fields => 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment)',
},
};
($content, $res) = $gs->request(
POST => ':batchUpdate',
{
requests => \#requests,
},
);
exit;
#Google Sheets API, v4
# Scopes
# https://www.googleapis.com/auth/drive View and manage the files in your Google D# # i# rive
# https://www.googleapis.com/auth/drive.file View and manage Google Drive files and folders that you have opened or created with this app
# https://www.googleapis.com/auth/drive.readonly View the files in your Google Drive
# https://www.googleapis.com/auth/spreadsheets View and manage your spreadsheets in Google Drive
# https://www.googleapis.com/auth/spreadsheets.readonly View your Google Spreadsheets
My solution was a bit weird..i tried every solution i found on internet and nothing. Surprisely this worked: delete the credentials.json, refresh, vinculate your app in your account again. The new credentials.json file will have the refresh token. Backup this file somewhere.
Then keep using your app until the refresh token error comes again. Delete the crendetials.json file that now is only with an error message (this hapenned in my case), then paste you old credentials file in the folder, its done!
Its been 1 week since ive done this and had no more problems.
Adding access_type=offline to the authorisation Google authorisation URL did the trick for me. I am using Java and Spring framework.
This is the code that creates the client registration:
return CommonOAuth2Provider.GOOGLE
.getBuilder(client)
.scope("openid", "profile", "email", "https://www.googleapis.com/auth/gmail.send")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth?access_type=offline")
.clientId(clientId)
.redirectUriTemplate("{baseUrl}/{action}/oauth2/code/{registrationId}")
.clientSecret(clientSecret)
.build();
The important part here is the authorization URI, to which ?access_type=offline is appended.

What is the correct way to add a Custom Authentication strategy to a Feathers application?

My Feathers application needs to be able to have two JWT authentication strategies. For the users service, I need to have, for example, all: [authenticate('carrier')] instead of all: [authenticate('jwt')] in my hooks. For the rest of the services, authenticate['jwt'] is needed.
For this, I have registered a custom strategy in authentication.js called CarrierStrategy as following:
module.exports = function auth(app) {
const authentication = new AuthenticationService(app)
// register all of the strategies with authentication service
authentication.register('carrier', new CarrierStrategy())
authentication.register('jwt', new JWTStrategy())
// register the authentication service with your app
app.use('/api/authentication', authentication)
}
In config/default.json, I have also registered this strategy as following:
authStrategies: ["carrier", "jwt"]
The CarrierStrategy needs to handle the incoming Authorization header a little differently with some custom logic.
When I use Postman to send requests for this service, i.e., localhost:3030/users with a JWT token in the header, I get the following error.
Invalid authentication information (strategy not allowed in authStrategies)'
Please guide me if this is the right way to add a custom strategy to the application.
I had a similar problem to this. I wanted both Stateful and Stateless JWT authentication. The problem being that if you just do this in authentication.js
authentication.register('jwt', new JWTStrategy());
authentication.register('jwt-stateless', new JWTStrategy());
Then when you submit a request with a JWT token it will match on either one and you'll end up with a problem in one of your services somewhere.
I ended up creating a custom strategy like this in authentication.js:
class StatelessJWTStrategy extends JWTStrategy {
get configuration () {
const authConfig = this.authentication.configuration;
const config = super.configuration;
return {
...config,
entity: authConfig.entity,
service: authConfig.service,
header: 'Authorization',
schemes: [ 'STATELESS' ]
};
}
}
which is basically a slightly modified JWTStrategy that uses STATELESS in the Authorization header instead of Bearer or JWT. It's not a great solution, but it works.
Then I did this also in authentication.js
authentication.register('jwt', new JWTStrategy());
authentication.register('jwt-stateless', new StatelessJWTStrategy());
Then you need to modify your config.json file. In the authentication section add this:
"jwt-stateless": {
"entity": null
},
"jwt": {
"entity": "user",
"service": "users"
},
"entity": "user",
"service": "users",
"authStrategies": [
"jwt-stateless",
"jwt",
"local"
],
Now you should be able to use the jwt-stateless auth mechanism in your hooks like this:
authenticate('jwt-stateless')
Head over to here to create your stateless JWT. Fill in iss with the issuer and aud with audience details from your config.json, and add a user ID to the sub field. Pop your secret from config.json in the bottom signature verification field and the token on the left should authenticate.

Google Classroom, delete teacher from course backend Error 500

I'm able to delete a teacher from a course but in this particular case I get this error (In Classroom API Reference ):
{
"error": {
"code": 500,
"message": "Internal error encountered.",
"status": "INTERNAL"
}
}
From my NodeJS app:
{ code: 500,
message: 'Internal error encountered.',
errors:
[ { message: 'Internal error encountered.',
domain: 'global',
reason: 'backendError' } ],
status: 'INTERNAL' }
The code (NodeJS app):
let classroom = google.classroom('v1');
let data = {
auth : auth,
courseId : idCurso,
userId : emailDocente
};
classroom.courses.teachers.delete(data, (err, response) => {
//...code
});
I get this error from the UI.
More Info:
There are two teachers in the course: myemail#mydomain.com and
admin#mydomain.com admin#mydomain.com is the owner.
I need to remove myemail#mydomain.com The user is active and exists in GSuite Admin
SDK.
courseState is ACTIVE
The error was caused by: the teacher was the owner of the google drive's folder. I had to delete from drive and the trash too. Developers should validate this and show a more clear message or delete the drive folder before deleting the teacher. Hope you guys update this. Thanks.
Update: I haven't been able to transfer ownership using drive API due to the fact that the user which I'm using to connect to the API doesn't have permission to change ownership I cannot have the real user to log in and change ownership because it's an automated process which runs every x minutes. I have to assign ownership of the drive folder to the user I connect to the API -not the real user- otherwise this will not work. I hope Google devs fix this.

How to prevent multiple logins in mobile applications

If we want to prevent multiple logins with same credentials in mobile application how can we do that and we do not have any sessions in mobile so can we do with token based authentication please give me some ideas how to do this.
I am implementing ionic 2 application with back end as node js. I would be very grateful to get suggestions. Thank you in advance.
You can save every login connection and disconnect all previously saved login every time a new connection is created.
If you want to restrict multiple logins on same device and same application then
Two possible solutions:
1.
you can use device uuid Device plugin
Device uuid will be unique. (Don't use device uuid as authentication token)
Install it:
$ ionic plugin add cordova-plugin-device
ans usage
import { Device } from 'ionic-native';
console.log('Device UUID is: ' + Device.uuid);
On login you can use token base authentication.when ever user login save token in localStorage.and when ever app open (index page) check if token is present and still valid than takes user to dashboard otherwise to the login page.
If you don't have any sessions, then it's best to utilize tokens.
There's a great Node.JS API project that already implements tokens on Github, which I've forked and used in the past. You could browse this as an example.
Inside of this project, there's a config for the tokens like so:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// AccessToken
var AccessToken = new Schema({
userId: {
type: String,
required: true
},
clientId: {
type: String,
required: true
},
token: {
type: String,
unique: true,
required: true
},
created: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('AccessToken', AccessToken);
All you would need to do is change the logic to check for the clientId and token for authentication. If the clientId (which is auto-generated each time) is different, then log the user out; forcing them to re-authenticate.
With modern mobile devices, this can easily be done in Javascript with local storage:
var testObject = { 'one': 1, 'two': 2, 'three': 3 };
// Put the object into storage
localStorage.setItem('testObject', JSON.stringify(testObject))…
This allows infinite use on one device, but only ever one device at a time. You could also easily set an expiration on the token as well if you'd like to auto-log out after a period of time.

How to Dynamically change sync function in sync gateway couchbase

Is there a way I can dynamically change sync function for eg. Lets ssy my documents have a field ID and I want to get documents belonging to a particular ID so ID is my variable here. eg. below is a sync function for ID=4
"sync":
function (doc) {
if(doc.ID==4){
channel (doc.channels);
}
else{
throw({forbidden: "Missing required properties"});
}
},
Now This will only work for ID=4. How Can I make my sync function dynamic. Is there a way I can supply arguments to my sync function?
EDIT 1 Added Use Case
Ok so my use case is like this.I have an app in which when a user logs in I need to get user specific data from CouchBase Server to CouchBase lite. In My CouchBase Server I have 20000 documents and for each user there are 5 documents so I have (20000/5) 4000 users. So When a user logs in to my app my CouchBase server should send only 5 documents which are related to that user and not all 20000 documents
EDIT 2
This is how I have implemented the replication
private URL createSyncURL(boolean isEncrypted){
URL syncURL = null;
String host = "http://172.16.25.108";
String port = "4986";
String dbName = "sync_gateway";
try {
//syncURL = new URL("http://127.0.0.1 :4986/sync_gateway");
syncURL = new URL(host + ":" + port + "/" + dbName);
} catch (Exception me) {
me.printStackTrace();
}
Log.d(syncURL.toString(),"URL");
return syncURL;
}
private void startReplications() throws CouchbaseLiteException {
Log.d(TAG, "");
Replication pull = database.createPullReplication(this.createSyncURL(false));
Replication push = database.createPushReplication(this.createSyncURL(false));
Authenticator authenticator = AuthenticatorFactory.createBasicAuthenticator("an", "1234");
pull.setAuthenticator(authenticator);
//push.setAuthenticator(authenticator);
List<String> channels1 = new ArrayList<String>();
channels1.add("u1");
pull.setChannels(channels1);
pull.setContinuous(true);
// push.setContinuous(true);
pull.start();
//push.start();
if(!push.isRunning()){
Log.d(TAG, "MyBad");
}
/*if(!push.isRunning()) {
Log.d(TAG, "Replication is not running due to " +push.getLastError().getMessage());
Log.d(TAG, "Replication is not running due to " +push.getLastError().getCause());
Log.d(TAG, "Replication is not running due to " +push.getLastError().getStackTrace());
Log.d(TAG, "Replication is not running due to " +push.getLastError().toString());
}*/
}
The easiest way to achieve this is to qualify each user to one channel, named like the user, and to give to a document the channel (= user) names of all users for whom this document is relevant (maybe just one channel name per document, but that's completely up to you).
So with the standard sync function (without any if condition), if your config.json contains
"users": {
"u1": {
"admin_channels": ["u1"],
"password": "abracadabra"
},
"u2": {
"admin_channels": ["u2"],
"password": "simsalabim"
...
and you have documents having
{"channels": "u1",...
{"channels": "u2",...
{"channels": ["u1", "u2"],...
then the first will be transferred to u1, the second to u2, and the third to both of them. You don't need to make your channel names identical to the user name, but for this scenario it's the easiest way to go.
The programmatic assignment of channels to users can be done via the Sync Gateway Admin REST API, see http://developer.couchbase.com/documentation/mobile/1.2/develop/references/sync-gateway/admin-rest-api/user-admin/post-user/index.html. (Note that the Admin API should run on a port that is opened only to the local server where CB runs, not to the public.)