I am trying to get the token from JS using ADAL.js for authentication.
In the below code, I am trying to get the cached user and token. If the user and token is not cached, I open a pop-up to a dummy page in CRM and then cache the user and the token.
var getUser = function () {
return new Promise(function (resolve, reject) {
// If the user is cached, resolve the promise immediately.
var user = authContext.getCachedUser();
if (user) {
var cachedToken = authContext.getCachedToken(clientId);
resolve(cachedToken);
return;
}
// The user was not cached. Open a popup window which
// performs the OAuth login process, then signals
// the result.
authContext.config.displayCall = function (url) {
authContext.config.displayCall = null;
var popup = window.open(url, 'auth-popup', 'toolbar=no,status=no,menubar=no,scrollbars=no,resizable=no,left=10000, top=10000, width=10, height=10, visible=none');
var intervalId = window.setInterval(function () {
try {
if (popup.location.pathname.indexOf('/' + dummyAuthPage) >= 0) {
authContext.handleWindowCallback(popup.location.hash);
popup.close();
token = authContext.getCachedToken(clientId);
if (token) {
window.clearInterval(intervalId);
resolve(token);
}
else {
reject(authContext.getLoginError());
}
}
} catch (whatever) {
if (popup.closed) {
reject();
}
}
}, 100);
};
Do we have a better way to do the same especially without getting the pop-up?
I am currently trying to do the Silent Authentication. But here it is mentioned "In the tab's content page, call microsoftTeams.getContext() to get a login hint for the current user". This command is not returning anything in CRM.
Thanks
You can try silent authentication described in the following docs.microsoft Link Silent authentication
Related
The project aims to study a new social media:
https://booyah.live/
My needs are:
1 - Collect data from profiles that follow a specific profile.
2 - My account use this data to follow the collected profiles.
3 - Among other possible options, also unfollow the profiles I follow.
The problem found in the current script:
The profile data in theory is being collected, the script runs perfectly until the end, but for some reason I can't specify, instead of following all the collected profiles, it only follows the base profile.
For example:
I want to follow all 250 profiles that follow the ID 123456
I activate the booyahGetAccounts(123456); script
In theory the end result would be my account following 250 profiles
But the end result I end up following only the 123456 profile, so the count of people I'm following is 1
Complete Project Script:
const csrf = 'MY_CSRF_TOKEN';
async function booyahGetAccounts(uid, type = 'followers', follow = 1) {
if (typeof uid !== 'undefined' && !isNaN(uid)) {
const loggedInUserID = window.localStorage?.loggedUID;
if (uid === 0) uid = loggedInUserID;
const unfollow = follow === -1;
if (unfollow) follow = 1;
if (loggedInUserID) {
if (csrf) {
async function getUserData(uid) {
const response = await fetch(`https://booyah.live/api/v3/users/${uid}`),
data = await response.json();
return data.user;
}
const loggedInUserData = await getUserData(loggedInUserID),
targetUserData = await getUserData(uid),
followUser = uid => fetch(`https://booyah.live/api/v3/users/${loggedInUserID}/followings`, { method: (unfollow ? 'DELETE' : 'POST'), headers: { 'X-CSRF-Token': csrf }, body: JSON.stringify({ followee_uid: uid, source: 43 }) }),
logSep = (data = '', usePad = 0) => typeof data === 'string' && usePad ? console.log((data ? data + ' ' : '').padEnd(50, '━')) : console.log('━'.repeat(50),data,'━'.repeat(50));
async function getList(uid, type, follow) {
const isLoggedInUser = uid === loggedInUserID;
if (isLoggedInUser && follow && !unfollow && type === 'followings') {
follow = 0;
console.warn('You alredy follow your followings. `follow` mode switched to `false`. Followings will be retrieved instead of followed.');
}
const userData = await getUserData(uid),
totalCount = userData[type.slice(0,-1)+'_count'] || 0,
totalCountStrLength = totalCount.toString().length;
if (totalCount) {
let userIDsLength = 0;
const userIDs = [],
nickname = userData.nickname,
nicknameStr = `${nickname ? ` of ${nickname}'s ${type}` : ''}`,
alreadyFollowedStr = uid => `User ID ${uid} already followed by ${loggedInUserData.nickname} (Account #${loggedInUserID})`;
async function followerFetch(cursor = 0) {
const fetched = [];
await fetch(`https://booyah.live/api/v3/users/${uid}/${type}?cursor=${cursor}&count=100`).then(res => res.json()).then(data => {
const list = data[type.slice(0,-1)+'_list'];
if (list?.length) fetched.push(...list.map(e => e.uid));
if (fetched.length) {
userIDs.push(...fetched);
userIDsLength += fetched.length;
if (follow) followUser(uid);
console.log(`${userIDsLength.toString().padStart(totalCountStrLength)} (${(userIDsLength / totalCount * 100).toFixed(4)}%)${nicknameStr} ${follow ? 'followed' : 'retrieved'}`);
if (fetched.length === 100) {
followerFetch(data.cursor);
} else {
console.log(`END REACHED. ${userIDsLength} accounts ${follow ? 'followed' : 'retrieved'}.`);
if (!follow) logSep(targetList);
}
}
});
}
await followerFetch();
return userIDs;
} else {
console.log(`This account has no ${type}.`);
}
}
logSep(`${follow ? 'Following' : 'Retrieving'} ${targetUserData.nickname}'s ${type}`, 1);
const targetList = await getList(uid, type, follow);
} else {
console.error('Missing CSRF token. Retrieve your CSRF token from the Network tab in your inspector by clicking into the Network tab item named "bug-report-claims" and then scrolling down in the associated details window to where you see "x-csrf-token". Copy its value and store it into a variable named "csrf" which this function will reference when you execute it.');
}
} else {
console.error('You do not appear to be logged in. Please log in and try again.');
}
} else {
console.error('UID not passed. Pass the UID of the profile you are targeting to this function.');
}
}
This current question is a continuation of that answer from the link:
Collect the full list of buttons to follow without having to scroll the page (DevTools Google Chrome)
Since I can't offer more bounty on that question, I created this one to offer the new bounty to anyone who can fix the bug and make the script work.
Access account on Booyah website to use for tests:
Access by google:
User: teststackoverflowbooyah#gmail.com
Password: quartodemilha
I have to admit that it is really hard to read your code, I spent a lesser amount of time rewriting everything from scratch.
Stated that we need a code piece to be cut/pasted in the JavaScript console of web browsers able to store some data (i.e. expiration of followings and permanent followings) we need some considerations.
We can consider expiration of followings as volatile data: something that if lost can be reset to 1 day later from when we loose this data. window.localStorage is a perfect candidate to store these kind of data. If we change web browser the only drawback is that we loose the expiration of followings and we can tolerate to reset them to 1 day later from when we change browser.
While to store the list of permanent followings we need a permanent store even if we change web browser. The best idea that came to my mind is to create an alternative account with which to follow the users we never want to stop following. In my code I used uid 3186068 (a random user), once you have created your own alternative account, just replace the first line of the code block with its uid.
Another thing we need to take care is error handling: API could always have errors. The approach I chosen is to write myFetch which, in case of errors, retries twice the same call; if the error persists, probably we are facing a temporary booyah.live outage. Probably we just need to retry a bit later.
To try to provide a comfortable interface, the code blocks gathers the uid from window.location: to follow the followers of users, just cut/paste the code block on tabs opened on their profiles. For example I run the code from a tab open on https://booyah.live/studio/123456?source=44.
Last, to unfollow users the clean function is called 5 minutes later we paste the code (to not conflict with calls to follow followers) and than is executed one hour later it finishes its job. It is written to access the localStorage in an atomic way, so you can have many of them running simultaneously on different tabs of the same browser, you can not care about it. The only thing you need to take care it that when the window.location changes, all the JavaScript events in the tab are reset; so I suggest to keep a tab open on the home page, paste the code block on it, and forget about this tab; it will be the tab responsible of unfollowing users. Then open other tabs to do what you need, when you hit a user you want to follow the followers, paste the block on it, wait the job is finished and continue to use the tab normally.
// The account we use to store followings
const followingsUID = 3186068;
// Gather the loggedUID from window.localStorage
const { loggedUID } = window.localStorage;
// Gather the CSRF-Token from the cookies
const csrf = document.cookie.split("; ").reduce((ret, _) => (_.startsWith("session_key=") ? _.substr(12) : ret), null);
// APIs could have errors, let's do some retries
async function myFetch(url, options, attempt = 0) {
try {
const res = await fetch("https://booyah.live/api/v3/" + url, options);
const ret = await res.json();
return ret;
} catch(e) {
// After too many consecutive errors, let's abort: we need to retry later
if(attempt === 3) throw e;
return myFetch(url, option, attempt + 1);
}
}
function expire(uid, add = true) {
const { followingsExpire } = window.localStorage;
let expires = {};
try {
// Get and parse followingsExpire from localStorage
expires = JSON.parse(followingsExpire);
} catch(e) {
// In case of error (ex. new browsers) simply init to empty
window.localStorage.followingsExpire = "{}";
}
if(! uid) return expires;
// Set expire after 1 day
if(add) expires[uid] = new Date().getTime() + 3600 * 24 * 1000;
else delete expires[uid];
window.localStorage.followingsExpire = JSON.stringify(expires);
}
async function clean() {
try {
const expires = expire();
const now = new Date().getTime();
for(const uid in expires) {
if(expires[uid] < now) {
await followUser(parseInt(uid), false);
expire(uid, false);
}
}
} catch(e) {}
// Repeat clean in an hour
window.setTimeout(clean, 3600 * 1000);
}
async function fetchFollow(uid, type = "followers", from = 0) {
const { cursor, follower_list, following_list } = await myFetch(`users/${uid}/${type}?cursor=${from}&count=50`);
const got = (type === "followers" ? follower_list : following_list).map(_ => _.uid);
const others = cursor ? await fetchFollow(uid, type, cursor) : [];
return [...got, ...others];
}
async function followUser(uid, follow = true) {
console.log(`${follow ? "F" : "Unf"}ollowing ${uid}...`);
return myFetch(`users/${loggedUID}/followings`, {
method: follow ? "POST" : "DELETE",
headers: { "X-CSRF-Token": csrf },
body: JSON.stringify({ followee_uid: uid, source: 43 })
});
}
async function doAll() {
if(! loggedUID) throw new Error("Can't get 'loggedUID' from localStorage: try to login again");
if(! csrf) throw new Error("Can't get session token from cookies: try to login again");
console.log("Fetching current followings...");
const currentFollowings = await fetchFollow(loggedUID, "followings");
console.log("Fetching permanent followings...");
const permanentFollowings = await fetchFollow(followingsUID, "followings");
console.log("Syncing permanent followings...");
for(const uid of permanentFollowings) {
expire(uid, false);
if(currentFollowings.indexOf(uid) === -1) {
await followUser(uid);
currentFollowings.push(uid);
}
}
// Sync followingsExpire in localStorage
for(const uid of currentFollowings) if(permanentFollowings.indexOf(uid) === -1) expire(uid);
// Call first clean task in 5 minutes
window.setTimeout(clean, 300 * 1000);
// Gather uid from window.location
const match = /\/studio\/(\d+)/.exec(window.location.pathname);
if(match) {
console.log("Fetching this user followers...");
const followings = await fetchFollow(parseInt(match[1]));
for(const uid of followings) {
if(currentFollowings.indexOf(uid) === -1) {
await followUser(uid);
expire(uid);
}
}
}
return "Done";
}
await doAll();
The problem: I strongly suspect a booyah.live API bug
To test my code I run it from https://booyah.live/studio/123456?source=44.
If I run it multiple times I continue to get following output:
Fetching current followings...
Fetching permanent followings...
Syncing permanent followings...
Following 1801775...
Following 143823...
Following 137017...
Fetching this user followers...
Following 16884042...
Following 16166724...
There is bug somewhere! The expected output for subsequent executions in the same tab would be:
Fetching current followings...
Fetching permanent followings...
Syncing permanent followings...
Fetching this user followers...
After seeking the bug in my code without success, I checked booyah.live APIs: if I navigate following URLs (the uids are the ones the code continue to follow in subsequent executions)
https://booyah.live/studio/1801775
https://booyah.live/studio/143823
https://booyah.live/studio/137017
https://booyah.live/studio/16884042
https://booyah.live/studio/16166724
I can clearly see I follow them, but if I navigate https://booyah.live/following (the list of users I follow) I can't find them, neither if I scroll the page till the end.
Since I do exactly the same calls the website does, I strongly suspect the bug is in booyah.live APIs, exactly in the way they handle the cursor parameter.
I suggest you to open a support ticket to booyah.live support team. You could use the test account you provided us: I already provided you the details to do that. ;)
I am using Firebase authentication in my iOS app. Is there any way in Firebase when user login my app with Firebase then logout that user all other devices(sessions)? Can I do that with Firebase admin SDK?
When i had this issue i resolved it with cloud functions
Please visit this link for more details https://firebase.google.com/docs/auth/admin/manage-sessions#revoke_refresh_tokens
Do the following;
Set up web server with firebase cloud functions (if none exists)
use the admin sdk(thats the only way this method would work) - [Visit this link] (
(https://firebase.google.com/docs/admin/setup#initialize_the_sdk).
Create an api that receives the uid and revokes current sessions as specified in the first link above
admin.auth().revokeRefreshTokens(uid)
.then(() => {
return admin.auth().getUser(uid);
})
.then((userRecord) => {
return new Date(userRecord.tokensValidAfterTime).getTime() / 1000;
})
.then((timestamp) => {
//return valid response to ios app to continue the user's login process
});
Voila users logged out. I hope this gives insight into resolving the issue
Firebase doesn't provide such feature. You need to manage it yourself.
Here is the Firebase Doc and they haven't mentioned anything related to single user sign in.
Here is what you can do for this-
Take one token in User node (Where you save user's other data) in Firebase database and regenerate it every time you logged in into application, Match this token with already logged in user's token (Which is saved locally) in appDidBecomeActive and appDidFinishLaunching or possibly each time you perform any operation with Firebase or may be in some fixed time interval. If tokens are different logged out the user manually and take user to authenticate screen.
What i have done is:
Created collection in firestore called "activeSessions".User email as an id for object and "activeID" field for holding most recent session id.
in sign in page code:
Generating id for a user session every time user is logging in.
Add this id to localstorage(should be cleaned everytime before adding).
Replace "activeID" by generated id in collection "activeSessions" with current user email.
function addToActiveSession() {
var sesID = gen();
var db = firebase.firestore();
localStorage.setItem('userID', sesID);
db.collection("activeSessions").doc(firebase.auth().currentUser.email).set({
activeID: sesID
}).catch(function (error) {
console.error("Error writing document: ", error);
});
}
function gen() {
var buf = new Uint8Array(1);
window.crypto.getRandomValues(buf);
return buf[0];
}
function signin(){
firebase.auth().signInWithEmailAndPassword(email, password).then(function (user) {
localStorage.clear();
addToActiveSession();
}
}), function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode === 'auth/wrong-password') {
alert('wrong pass');
} else {
alert(errorMessage);
}
console.log(error);
};
}
Then i am checking on each page if the id session in local storage is the same as "activeID" in firestore,if not then log out.
function checkSession(){
var db = firebase.firestore();
var docRef = db.collection("activeSessions").doc(firebase.auth().currentUser.email);
docRef.get().then(function (doc) {
alert(doc.data().activeID);
alert(localStorage.getItem('userID'));
if (doc.data().activeID != localStorage.getItem('userID')) {
alert("bie bie");
firebase.auth().signOut().then(() => {
window.location.href = "signin.html";
}).catch((error) => {
// An error happened.
});
window.location.href = "accountone.html";
} else{alert("vse ok");}
}).catch(function (error) {
console.log("Error getting document:", error);
});
}
PS: window has to be refreshed to log inactive session out.
I have implemented the browser push notification functionality and its working fine. I used this guide as the reference https://developers.google.com/web/fundamentals/getting-started/push-notifications/step-01?hl=en
However as payload is still not supported, I decided to query my server to get the notification data for each user which is also working fine.
There is one issue though. For some cases, after getting data from the server, I want to control whether to show the notification or not. I am not able to figure out how to do this. I tried returning false, throwing errors etc. But is always shows the default notification even if I don't call showNotification method. Let me know how to solve this. Following is the relevant code
self.addEventListener('push', function(event) {
event.waitUntil(
fetch('/getPushNotificationData/').then(function(response){
if (response.status !== 200) {
// I don't want to show any notification in this case
console.log('Looks like there was a problem. Status Code: ' + response.status);
throw new Error();
}
return response.json().then(function(data){
var shouldDisplay = data.shouldDisplay;
if (shouldDisplay=='1'){
var title = data.title;
var message = data.message;
var url = data.url;
return self.registration.showNotification(title, {
body: message,
data: url
});
}
else{
// I don't want to show any notification in this case also
return true;
}
});
})
);
});
Firefox has launched a feature called Tracking protection in v42.0. It blocks several tracking scripts such as Google Analytics, Marketo, LinkedIn, etc.
I was trying to detect it through navigator.DoNotTrack, but it returns unspecified in both cases – browsing in regular mode, and browsing in private mode – using Firefox 42.0 on Mac.
How can I detect in JavaScript whether a user is viewing the website with the Tracking protection on, since navigator.DoNotTrack fails?
navigator.donottrack only shows the setting of the "Do not track" preference. It does not tell if tracking protection, which is a different feature, is enabled. Tracking protection is enabled automatically when in private browsing mode, but users can change a setting in about:config to have it enabled full time.
While you can't tell directly if the feature is enabled, you can check for its effects with something like this:
var canreach = false;
$(function() {
$('<img/>')
.attr("src", "//apps.facebook.com/favicon.ico")
.load(function(){canreach = true;})
.css("display", "none")
.appendTo(document.body);
});
Firefox uses a list obtained from Disconnect for its tracking protection; just use a domain that you know is on that list, and an image that you know will exist.
Of course, this could flag any number of causes for the image not to load, including network connectivity problems, ad blocking software, filtering proxies, etc.
Here is slightly improved version of miken32's answer using Deferred:
function whenNoTrackingProtection() {
if (!whenNoTrackingProtection.promise) {
var dfd = new $.Deferred();
whenNoTrackingProtection.promise = dfd.promise();
var time = Date.now();
$('<img/>')
.attr('src', '//apps.facebook.com/favicon.ico')
.on('load', dfd.resolve)
.on('error', function() {
if ((Date.now() - time) < 50) {
dfd.reject();
} else {
// The request took to long, it seems this is a network error.
dfd.resolve();
}
});
}
return whenNoTrackingProtection.promise;
}
or without jQuery, using Promise:
function whenNoTrackingProtection() {
if (!whenNoTrackingProtection.promise) {
whenNoTrackingProtection.promise = new Promise(function(resolve, reject) {
var time = Date.now();
var img = new Image();
img.onload = resolve;
img.onerror = function() {
if ((Date.now() - time) < 50) {
reject();
} else {
// The request took to long, it seems this is a network error.
resolve();
}
};
img.src = '//apps.facebook.com/favicon.ico';
});
}
return whenNoTrackingProtection.promise;
}
Here is an updated solution without using jQuery, using Promise. Thanks #miken32 and #sleepwalker.
Why do I preferer this solution instead of a navigator.doNotTrack based one? In Firefox, navigator.doNotTrack returns 1 on strict Enhanced Tracking Protection but in Google Chrome user needs to enable Send a "Do Not Track" request with your browsing traffic.
function whenNoTrackingProtection() {
if (!whenNoTrackingProtection.promise) {
whenNoTrackingProtection.promise = new Promise(function(resolve, reject) {
var time = Date.now();
var img = new Image();
img.onload = resolve;
img.onerror = function() {
if ((Date.now() - time) < 50) {
reject(new Error("Rejected."));
} else {
resolve(new Error("Takes too long."));
}
};
img.src = '//www.facebook.com/tr/';
}).then((result) => {
console.log("Tracking OK");
}).catch(e => {
console.log("Tracking KAO");
console.log(e)
});
}
}
whenNoTrackingProtection()
Using the gapi.auth.authorize function, the user can close the popup without clicking any option (no accept or deny button). When this case happens, my callback function doesn't fire, so that I can't handle this case. What's the way to resolve this scenario?
Thanks.
This question has been around for a while, but when I looked into the issue (I want to show a spinner while the google authentication window is open, and hide it if the user decides not to authenticate), and found that gapi is throwing an error popup_closed_by_user. There is a two-second delay (which is kind of long, Facebook's is instant) before it is thrown, but it does work. Hooray, Google!
Some sample code (angular 1.x), prompting is the attribute to show the spinner:
_google_obj.prompting = true;
gapi.auth2.getAuthInstance().signIn().then(function(googleResponse){
var token = googleResponse.getAuthResponse().id_token;
SVC_exec_.post('/q/goog', 1000, { token: token }, 'signing you in through Google', function (response) {
if (response.code === 'ok') {
// update the UI
}
_google_obj.prompting = false;
});
},
function(error){
$timeout(function () {
console.log('user probably closed the google popup window: '+error);
_google_obj.prompting = false;
});
});
They don't appear to mention it in any documentation, but gapi.auth.authorize() returns the popup Window. So you can save the returned Window and set an interval or timeout to check Window.closed.
So you the auth function from Google returns promise, not a window. But then you can wrap original window into the function, which will set interval, to check if opened window closed already.
// variable to store our deferred object
var authDefer = null;
function auth() {
// right before the auth call, wrap window.open
wrap();
// Call auth
authDefer = window.gapi.auth.authorize({
client_id: ...,
scope: ...,
immediate: ...
}).then(
// onSuccess,
// onReject,
// onNotify
);
}
function wrap() {
(function(wrapped) {
window.open = function() {
// re-assign the original window.open after one usage
window.open = wrapped;
var win = wrapped.apply(this, arguments);
var i = setInterval(function() {
if (win.closed) {
clearInterval(i);
if (authDefer) {
authDefer.cancel();
}
}
}, 100);
return win;
};
})(window.open);
}
Taken from one of the thread on Google forums. Really works.
External link to Source