I have some simple javascript functions to interact with an API like this one to login:
login: function(username, password) {
var calledUrl = baseapi + "user/login/" + credentials;
calledUrl.post(
function (content) {
/*console.log("success" + JSON.stringify(content, null, 4));*/
},
function (e) {
console.log("it failed! -> " + e);
},
{
"username": username,
"password": password
},
{"Accept" : "application/json"}
);
},
The problem is, in the URL I must pass some credentials and they look like that:
var credentials = "?api_username=" + api_username + "&api_key=" + api_key;
Right now this variable is hardcoded to make some tests but of course it should change for each person using the function. I don't want to ask for it with each request, in this case I only want to ask for username and password. I would like to ask for it once and for all during an initializing process or whatever it is called and then remember it when executing the various functions.
If .login() is the first method that typically needs the credentials, then you can just make it a required argument for that method and then store the credentials in the object:
login: function(username, password, credentials) {
// save credentials for use in other methods
this.credentials = credentials;
var calledUrl = baseapi + "user/login/" + credentials;
calledUrl.post(
function (content) {
/*console.log("success" + JSON.stringify(content, null, 4));*/
},
function (e) {
console.log("it failed! -> " + e);
},
{
"username": username,
"password": password
},
{"Accept" : "application/json"}
);
},
Then, in other methods, you can access the credentials for this user with this.credentials.
If there are other methods that could also be called first and need the credentials for them, then you can either make the credentials an argument for those also or you can create a .init() method that just establishes the credentials or you can make it an argument in the constructor for this object.
You will probably also have to fix this line:
calledUrl.post(...)
because calledUrl is a string and strings don't have a .post() method unless you're using some sort of 3rd party library that adds one.
I recommend that you read about scope in JavaScript. Without more explanation of what you are trying to do, I would try something like this pattern...
var app = {
baseapi: 'http://some.url.com'
/* assuming the api user/pass are different form the account trying to log in */
,api_username: ''
,api_key: ''
,username: ''
,userpass: ''
,get_creditialString: function() {
return '?api_username=' + this.api_username + '&api_key=' + this.api_key;
}
,init: function(){
// do something to prompt for username and password
this.username = 'myUserName';
this.userpass = 'supersecretpassword';
this.login();
}
,login: function() {
var calledUrl = this.baseapi + "user/login/" + this.get_credentialString();
calledUrl.post(
function (content) {
/*console.log("success" + JSON.stringify(content, null, 4));*/
},
function (e) {
console.log("it failed! -> " + e);
},
{
"username": this.username,
"password": this.userpass
},
{"Accept" : "application/json"}
);
}
}
app.init();
Related
I'm trying to fetch data from Google search console (GSC) through http request.
I'm using google app Maker with javascript.
For my purpose I'm using a service account, all the scopes are already set up for the account.
I've copied the code provided by #Morfinismo.
/*********** SERVICE ACCOUNT CONFIGURATION USING THE OAUTH LIBRARY ***********
** All of the values are obtained from the .json file that is downloaded at
** the time of the service account creation
** Ref: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
** Ref: https://github.com/googlesamples/apps-script-oauth2
*/
var accessData = {
"private_key" : "-----BEGIN PRIVATE KEY-----THE KEY-----END PRIVATE KEY-----\n",
"client_email" : "searchconsolebot#project-id-xxxxxxxxxxxxxxx.iam.gserviceaccount.com",
"user_email" : "user#domain.com" // Why do we need a user mail ?
};
var scopes = ["https://www.googleapis.com/auth/webmasters", "https://www.googleapis.com/auth/webmasters.readonly"]; //GSC api scope
scopes = scopes.join(" "); //join all scopes into a space separated string
function getOAuthService(user) {
console.log("je passe par getOAuthService");
user = user || accessData.user_email;
console.log("user: " + user);
return OAuth2.createService("GSC_Service_Account")
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(accessData.private_key)
.setIssuer(accessData.client_email)
.setSubject(user)
.setPropertyStore(PropertiesService.getScriptProperties())
.setCache(CacheService.getUserCache())
.setParam('access_type', 'offline')
.setScope(scopes);
}
function reset(user) {
var service = getOAuthService(user);
console.log("service: " + service);
service.reset();
return service;
}
function getToken(userEmail){
var totoken = reset(userEmail).getAccessToken();
console.log(totoken);
return reset(userEmail).getAccessToken();
}
function getGCSUrlData(urlGiven){
var token = getToken();
if(token){
var reqBody = {
startDate: "2019-01-01",
endDate: "2020-01-23"
};
var options = {
method : 'POST',
headers : {
Authorization : 'Bearer ' + token,
},
contentType: 'application/json',
payload: JSON.stringify(reqBody),
muteHttpExceptions: true,
};
var url = "https://www.googleapis.com/webmasters/v3/sites/" + encodeURIComponent(urlGiven) + "/searchAnalytics/query";
var response = UrlFetchApp.fetch(url, options);
console.log(response);
}
}
Using the OAuth library seems really great but it does return me an error
Error: Access not granted or expired. at getToken (Service_Account_Config:46)
Also I noticed that getToken() method requires a param but when calling it we don't give any param is it normal ?
And why do we need a user_email since we are using a service account ?
Which email should I enter for the user_email then ?
I would really appreciate some help about this issue and any advice to understand this kind of issue.
Thanks a lot,
Jacky
Integration with a service account is very easy when using the OAuth2 Library for AppsScript. Here are the steps:
1.) In a server side script add the following:
/*********** SERVICE ACCOUNT CONFIGURATION USING THE OAUTH LIBRARY ***********
** All of the values are obtained from the .json file that is downloaded at
** the time of the service account creation
** Ref: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
** Ref: https://github.com/googlesamples/apps-script-oauth2
*/
var accessData= {
"private_key": "-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----\n",
"client_email": "service-account825#08012018.iam.gserviceaccount.com",
"user_email": "user#domain.com"
};
var scopes = ["https://www.googleapis.com/auth/webmasters", "https://www.googleapis.com/auth/webmasters.readonly"]; //drive api scope
scopes = scopes.join(" "); //join all scopes into a space separated string
function getOAuthService(user) {
user = user || accessData.user_email;
return OAuth2.createService("Service Account")
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(accessData.private_key)
.setIssuer(accessData.client_email)
.setSubject(user)
.setPropertyStore(PropertiesService.getScriptProperties())
.setCache(CacheService.getUserCache())
.setParam('access_type', 'offline')
.setScope(scopes);
}
function reset(user) {
var service = getOAuthService(user);
service.reset();
return service;
}
function getToken(userEmail){
return reset(userEmail).getAccessToken();
}
Then you can simplye call the service you need by doing the following:
function getGCSUrlData(urlGiven){
var token = getToken();
if(token){
var reqBody = {
startDate: "2020-01-01",
endDate: "2020-01-23"
};
var options = {
method : 'POST',
headers : {
Authorization : 'Bearer ' + token,
},
contentType: 'application/json',
payload: JSON.stringify(reqBody),
muteHttpExceptions: true,
};
var url = "https://www.googleapis.com/webmasters/v3/sites/" + encodeURIComponent(urlGiven) + "/searchAnalytics/query";
var response = UrlFetchApp.fetch(url, options);
console.log(response);
}
}
I am using the Moxtra JavaScript SDK to implement the timeline functionality in my app.
I want to create a central binder for my user groups and got everything working except the inviting of another of my users to a existing binder.
function getTimeline(access_token, binderID) {
var options = {
access_token: access_token,
binder_id: binderID,
iframe: true,
tagid4iframe: "container",
iframewidth: "920px",
iframeheight: "650px",
autostart_meet: true,
autostart_note: true,
extension: {"show_dialogs": {"meet_invite": true}},
start_timeline: function (event) {
alert("Timeline started session Id: " + event.session_id + " binder id: " + event.binder_id);
},
view_binder: function (event) {
alert("Binder switched session Id: " + event.session_id + " binder id: " + event.binder_id);
},
invite_member: function (event) {
var userID = UNIQUEUSERID
alert("Invite member into binder Id: " + event.binder_id);
console.log(Moxtra.baseUrl + "/" + event.binder_id + "/inviteuser");
var postData = {
"users": [
{
"user": {
"unique_id": userID
}
}
],
"message": "Custom message to join this conversation..."
};
$.ajax("https://api.moxtra.com/v1/" + event.binder_id + "/inviteuser", {
type: 'POST',
data: postData,
contentType: 'application/json',
crossDomain: true,
success: function (data) {
console.log(data);
}
});
},
start_meet: function (event) {
alert("Meet started session key: " + event.session_key + " session id: " + event.session_id);
},
end_meet: function (event) {
alert("Meet end event");
},
save_meet: function (event) {
alert("Meet saved on binder: " + event.binder_id);
},
start_note: function (event) {
alert("session key: " + event.session_key + " session id: " + event.session_id);
},
save_note: function (event) {
alert("Note saved on binder: " + event.destination_binder_id);
},
cancel_note: function (event) {
alert("Note cancelled");
},
error: function (event) {
alert("Timeline error code: " + event.error_code + " error message: " + event.error_message);
}
};
Moxtra.timeline(options);
}
as you can see the getTimeline request handles the invite_member event, there is no build in invite user to chat/binder so I can't use that.
Seeming that the SDK calls the REST API I am using that myself to add the invite user functionality. But when I test the request with their API console I get a error saying that the group is not found.
I assign the users to the same group so that shouldn't be the issue and tried to add a user via my unique-id and the moxtra unique-id. What am I doing wrong? Or what other options do I have?
I assume the binder that you are trying to add users (using their unique id) was also created by an user who got created in Moxtra using unique_id method. In that case using the REST API to add team member is the right approach to add additional users to the binder.
When you use the API console, you will be using the access token generated through your moxtra email account. It cannot be used with the binder that you created through the unique_id method.
Based on the error that you mentioned above it looks like either the user who you are trying to add or the user who created the binder are not in the same group. Please make sure both the users are provisioned using the same SSO method.
From client-side javascript I want to call a share-webscript which returns JSON data.
The response from getTicket.json.ftl looks like:
{
"ticket" : "TICKET_faf851d4a993b62c98908268af07876f09fa86c9"
}
So how can I call this share-webscript from my client-side javascript and extract the value of "ticket" ?
see answer below
Answer:
Alfresco.util.Ajax.jsonGet(
{
url: Alfresco.constants.PROXY_URI + "/auth/getTicket.json",
successCallback:
{
fn: function(response)
{
try {
var json = JSON.parse(response.serverResponse.responseText);
var ticket = json["ticket"];
if (ticket.substring(0, 6) == "TICKET") {
clipboardData.setData("Text", ticket + "&" + file.nodeRef);
location.href = Alfresco.constants.URL_RESCONTEXT + "components/javawebstart/AEF_JNLP.jnlp";
} else {
// handle unknown format
}
} catch (e) {
// handle error
}
},
scope: this
},
failureCallback:
{
fn: function(response)
{
// handle failure case
},
scope: this
}
});
This calles the share tier webscript. So you also need a share tier webscript which calls a repository web script which returns the actual ticket ...
I'm working on a ZenDesk app that pulls customer info from a back-end system. We need to authenticate against that system using OAuth 2's browser-based authentication flow.
It's no problem to include a link to the authentication page, something like:
https://oauth2server.com/auth?
response_type=token&
client_id=CLIENT_ID&
redirect_uri=REDIRECT_URI&
scope=photos
Once the user has logged in, though, the OAuth server wants to redirect the client and include the authorization token. So the REDIRECT_URI would typically look like:
https://example.zendesk.com/agent/#token=ACCESS_TOKEN
However, ZenDesk already makes use of the fragment identifier to indicate what content to show on the page:
https://example.zendesk.com/agent/#/dashboard
https://example.zendesk.com/agent/#/tickets/1234
My ZD App only appears on certain pages, so how can I both
have my app rendered and Javascript run, and
have the fragment identifier with the auth token available?
(I do have control over the backend OAuth server, so if you can't think of a nice clean way to accomplish this, OAuth server-side hack suggestions are also gratefully accepted.)
Here's a really simple ZenDesk App (framework version 0.5) that
authenticates against Google (in a seperate popup window)
fetches a custom ticket field value from the currently visible ticket
retrieves the Google user's name
In manifest.json, this ZenDesk App should specify "location": "ticket_sidebar".
app.js
(function (window) {
return {
zenDeskSubdomain: 'YOUR_ZENDESK_SUBDOMAIN',
googleClientId: 'YOUR_GOOGLE_CLIENT_ID',
events: {
'app.activated': 'onActivate',
'app.deactivated': 'onDeactivate',
'click .loginout': 'onLogInOutClick',
'click .show-username': 'onShowUserNameClick'
},
requests: {
getUserInfo: function (access_token) {
return {
url: 'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' + access_token,
type: 'GET',
proxy_v2: true
};
}
},
onActivate: function () {
console.info("onActivate()");
this.accessToken();
var user_id = this.ticket().customField("custom_field_22931898");
this.$('.userid').text(user_id);
},
onDeactivate: function () {
console.info("onDeactivate()");
if (this.timer) {
clearTimeout(this.timer);
}
},
onShowUserNameClick: function () {
var access_token = this.accessToken();
if (!access_token) {
console.info("Can't do it! No access_token!");
return;
}
this.ajax('getUserInfo', access_token)
.done(function (data) {
console.info(data);
this.$('.username').text(data.name);
});
},
onLogInOutClick: function (event) {
if (this.accessToken()) {
this.logout(event);
} else {
this.login(event);
}
},
login: function (event) {
console.info("login()");
event.preventDefault();
var popup = this.createLoginPopup();
this.awaitAccessToken(popup);
},
logout: function (event) {
console.info("logout()");
event.preventDefault();
this.accessToken(null);
console.info(" access_token = " + this.accessToken());
this.$('.loginout').text('login');
},
createLoginPopup: function () {
console.info("createLoginPopup()");
return window.open(
'https://accounts.google.com/o/oauth2/auth?response_type=token&client_id=' + this.googleClientId + '&redirect_uri=https%3A%2F%2F' + this.zenDeskSubdomain + '.zendesk.com%2Fagent%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile',
'Login Popup',
'width=400,height=400');
},
timer: null,
awaitAccessToken: function (popup) {
console.info("awaitAccessToken()");
if (this.isLoaded(popup)) {
console.info(" popup is loaded");
} else {
console.info(" popup is NOT loaded; sleeping");
var t = this;
this.timer = setTimeout(
function () { t.awaitAccessToken(popup); },
1000);
return;
}
var access_token = this.parseAccessToken(popup.location.href);
if (access_token) {
console.info(' access_token = ' + access_token);
popup.close();
this.accessToken(access_token);
} else {
services.notify('Error requesting code...');
}
},
isLoaded: function (win) {
try {
return ('about:blank' !== win.location.href)
&& (null !== win.document.body.innerHTML);
} catch (err) {
return false;
}
},
parseAccessToken: function (uri) {
var match = uri.match(/[#&]access_token=([^&]*)/i);
return match[1] || null;
},
accessToken: function (value) {
if (1 === arguments.length) {
console.info("Storing access_token = " + value);
this.store({ access_token: value });
}
var token = this.store('access_token');
console.info("access_token = " + value);
var loginout = this.$('.loginout');
if (token) {
loginout.text('logout');
} else {
loginout.text('login');
}
return token;
}
};
}(this));
layout.hdbs
<header>
<span class="logo"/>
<h3>{{setting "name"}}</h3>
</header>
<section data-main/>
<footer>
<div><a class="loginout">login</a></div>
<div><a class="show-username">show username</a></div>
<div><b>user id: </b><span class="userid">unknown</span></div>
<div><b>username: </b><span class="username">unknown</span></div>
</footer>
Google OAuth configuration
Configure Google OAuth to allow traffic from your application.
Redirect URIs
http://localhost:XXX - for the ZAT development/testing environment
https://YOUR_ZENDESK_SUBDOMAIN.zendesk.com/agent/ - for production
Javascript Origins
http://localhost:XXX - for the ZAT development/testing environment
https://YOUR_ZENDESK_SUBDOMAIN.zendesk.com - for production
You can use a lightweight server-side app to manage the authorization flow and tokens. The Zendesk app can communicate with it through the framework's iframe helper. I wrote an extended tutorial for the Zendesk Help Center at https://support.zendesk.com/hc/en-us/articles/205225558.
In my app, every KiiUser has one hash table of data. But I have some problems using KiiObject to save data and get it next time.
I want to create an KiiObject with a specific URI when user registers so every time he logins I can import it with
KiiObject.objectWithURI(<URI>)
Alternatively, how to use objectWithJSON().
I don't want to use kiiQuery if I can avoid, as there is only one object for each user.
Can anyone give me an example of code how to create this Kiiobject when user registers and how to edit it every time he logins ; or every time he edits it and saves.
that's some of my current code but i don't get always the saved data back (the saved hash table "data" undefined on the object)
function performRegistration() {
try {
var user = KiiUser.userWithUsername(<username>, <password>);
user.register({
success: function(Auser) {
var bucket = Auser.bucketWithName("data");
data = bucket.createObject();
data.set("data", {});
data.saveAllFields({
success: function(theObject) {
console.log("Object saved!");
theObject.refresh({success:function(obj){
data=obj;
[....
...Some Code here to edit the...
...data object and save it....
...]
Kii.logger("User registered: " + Auser);
}});
},
failure: function(theObject, errorString) {
console.log("Error saving object: " + errorString);
}
});
},
failure: function(theUser, anErrorString) {
alert("Unable to register: " + anErrorString);
Kii.logger("Unable to register user: " + anErrorString);
}
});
} catch (e) {
alert("Unable to register: " + e.message);
Kii.logger("Unable to register user: " + e.message);
}
}
// the user clicked the 'sign in' button
function performLogin() {
KiiUser.authenticate(<username>, <password>, {
success: function(Auser) {
user = Auser;
var bucket = user.bucketWithName("data"); // a KiiBucket
var query = KiiQuery.queryWithClause();
var queryCallbacks = {
success: function(queryPerformed, r, nextQuery) {
console.log(r.constructor.name,r);
r[0].refresh({success:function(obj){
data=obj;
if(data.get("data")==undefined){
data.set("data",{});
}
data.save({success:function(Sobj){
data=Sobj;
[...
...some data object manipilation then save it
...]
}});
}});
},
failure: function(queryPerformed, anErrorString) {
}
};
bucket.executeQuery(query, queryCallbacks);
},
// callback for failed registration
failure: function(theUser, anErrorString) {
alert("Unable to register: " + anErrorString);
Kii.logger("Unable to register user: " + anErrorString);
}
});
};
Kii.initializeWithSite(<...>, <...>, KiiSite.US);
[...
other code
...]
How about using KiiUser's custom fields?
It is described as "Manipulating the custom fields" in this page.
http://documentation.kii.com/en/guides/javascript/managing-users/user-attributes/
Check these links for detail of API:
http://documentation.kii.com/references/js/storage/latest/symbols/KiiUser.html#set
http://documentation.kii.com/references/js/storage/latest/symbols/KiiUser.html#get