I am building SPA with react and I run into a problem with code directly from Azure Portal - Quickstart for Javascript. (you can download full code there)
If I create-react-app and I use the code it works just fine and I can authenticate, get token and use it in the post request.
But If I use the SAME code in my app (which is already styled and with all the functionality I need) it gives me Multiple authorities found in the cache. Pass authority in the API overload.|multiple_matching_tokens_detected` error when I authenticate.
Just to clarify authentication goes through and I see I am authenticated, just this error is bugging me and I have no idea how to debug it.
function signIn() {
myMSALObj.loginPopup(applicationConfig.graphScopes).then(function (idToken) {
//Login Success
console.log(idToken); //note that I can get here!
showWelcomeMessage();
acquireTokenPopupAndCallMSGraph();
}, function (error) {
console.log(error);
});
}
function acquireTokenPopupAndCallMSGraph() {
//Call acquireTokenSilent (iframe) to obtain a token for Microsoft Graph
myMSALObj.acquireTokenSilent(applicationConfig.graphScopes).then(function (accessToken) {
callMSGraph(applicationConfig.graphEndpoint, accessToken, graphAPICallback);
}, function (error) {
console.log(error); //this is where error comes from
// Call acquireTokenPopup (popup window) in case of acquireTokenSilent failure due to consent or interaction required ONLY
if (error.indexOf("consent_required") !== -1 || error.indexOf("interaction_required") !== -1 || error.indexOf("login_required") !== -1) {
myMSALObj.acquireTokenPopup(applicationConfig.graphScopes).then(function (accessToken) {
callMSGraph(applicationConfig.graphEndpoint, accessToken, graphAPICallback);
}, function (error) {
console.log(error);
});
}
});
}
The main thing I don`t understand is that the same code works just fine in the fresh create-react-app project, but as I use it in an already existing project (just without authentication) it breaks with mentioned error.
Full code
import React, { Component } from 'react'
import * as Msal from 'msal'
export class test extends Component {
render() {
var applicationConfig = {
clientID: '30998aad-bc60-41d4-a602-7d4c14d95624', //This is your client ID
authority: "https://login.microsoftonline.com/35ca21eb-2f85-4b43-b1e7-6a9f5a6c0ff6", //Default authority is https://login.microsoftonline.com/common
graphScopes: ["30998aad-bc60-41d4-a602-7d4c14d95624/user_impersonation"],
graphEndpoint: "https://visblueiotfunctionapptest.azurewebsites.net/api/GetDeviceList"
};
var myMSALObj = new Msal.UserAgentApplication(applicationConfig.clientID, applicationConfig.authority, acquireTokenRedirectCallBack,
{storeAuthStateInCookie: true, cacheLocation: "localStorage"});
function signIn() {
myMSALObj.loginPopup(applicationConfig.graphScopes).then(function (idToken) {
//Login Success
console.log(idToken);
showWelcomeMessage();
acquireTokenPopupAndCallMSGraph();
}, function (error) {
console.log(error);
});
}
function signOut() {
myMSALObj.logout();
}
function acquireTokenPopupAndCallMSGraph() {
//Call acquireTokenSilent (iframe) to obtain a token for Microsoft Graph
myMSALObj.acquireTokenSilent(applicationConfig.graphScopes).then(function (accessToken) {
callMSGraph(applicationConfig.graphEndpoint, accessToken, graphAPICallback);
}, function (error) {
console.log(error);
// Call acquireTokenPopup (popup window) in case of acquireTokenSilent failure due to consent or interaction required ONLY
if (error.indexOf("consent_required") !== -1 || error.indexOf("interaction_required") !== -1 || error.indexOf("login_required") !== -1) {
myMSALObj.acquireTokenPopup(applicationConfig.graphScopes).then(function (accessToken) {
callMSGraph(applicationConfig.graphEndpoint, accessToken, graphAPICallback);
}, function (error) {
console.log(error);
});
}
});
}
function callMSGraph(theUrl, accessToken, callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200)
callback(JSON.parse(this.responseText));
console.log(this.response);
}
xmlHttp.open("POST", theUrl, true); // true for asynchronous
xmlHttp.setRequestHeader('Authorization', 'Bearer ' + accessToken);
var dataJSON = JSON.stringify({ userEmail: null, FromDataUTC: "2012-04-23T18:25:43.511Z" })
xmlHttp.send(dataJSON);
}
function graphAPICallback(data) {
//Display user data on DOM
// var divWelcome = document.getElementById('WelcomeMessage');
// divWelcome.innerHTML += " to Microsoft Graph API!!";
// document.getElementById("json").innerHTML = JSON.stringify(data, null, 2);
}
function showWelcomeMessage() {
console.log("You are looged: " + myMSALObj.getUser().name);
// var divWelcome = document.getElementById('WelcomeMessage');
// divWelcome.innerHTML += 'Welcome ' + myMSALObj.getUser().name;
// var loginbutton = document.getElementById('SignIn');
// loginbutton.innerHTML = 'Sign Out';
// loginbutton.setAttribute('onclick', 'signOut();');
}
// This function can be removed if you do not need to support IE
function acquireTokenRedirectAndCallMSGraph() {
//Call acquireTokenSilent (iframe) to obtain a token for Microsoft Graph
myMSALObj.acquireTokenSilent(applicationConfig.graphScopes).then(function (accessToken) {
callMSGraph(applicationConfig.graphEndpoint, accessToken, graphAPICallback);
}, function (error) {
console.log(error);
//Call acquireTokenRedirect in case of acquireToken Failure
if (error.indexOf("consent_required") !== -1 || error.indexOf("interaction_required") !== -1 || error.indexOf("login_required") !== -1) {
myMSALObj.acquireTokenRedirect(applicationConfig.graphScopes);
}
});
}
function acquireTokenRedirectCallBack(errorDesc, token, error, tokenType)
{
if(tokenType === "access_token")
{
callMSGraph(applicationConfig.graphEndpoint, token, graphAPICallback);
} else {
console.log("token type is:"+tokenType);
}
}
// Browser check variables
var ua = window.navigator.userAgent;
var msie = ua.indexOf('MSIE ');
var msie11 = ua.indexOf('Trident/');
var msedge = ua.indexOf('Edge/');
var isIE = msie > 0 || msie11 > 0;
var isEdge = msedge > 0;
//If you support IE, our recommendation is that you sign-in using Redirect APIs
//If you as a developer are testing using Edge InPrivate mode, please add "isEdge" to the if check
if (!isIE) {
if (myMSALObj.getUser()) {// avoid duplicate code execution on page load in case of iframe and popup window.
showWelcomeMessage();
acquireTokenPopupAndCallMSGraph();
}
}
else {
document.getElementById("SignIn").onclick = function () {
myMSALObj.loginRedirect(applicationConfig.graphScopes);
};
if (myMSALObj.getUser() && !myMSALObj.isCallback(window.location.hash)) {// avoid duplicate code execution on page load in case of iframe and popup window.
showWelcomeMessage();
acquireTokenRedirectAndCallMSGraph();
}
}
return (
<div>
<h2>Please log in from VisBlue app</h2>
<button id="SignIn" onClick={signIn}>Sign In</button>
<button id="SignOut" onClick={signOut}>Sign Out</button>
<h4 id="WelcomeMessage"></h4>
<br/><br/>
<pre id="json"></pre>
</div>
)
}
}
export default test
it gives me Multiple authorities found in the cache. Pass authority in
the API overload.|multiple_matching_tokens_detected` error when I
authenticate
This error is caused because the auth SDK finds multiple matching tokens in cache for the input given to acquireTokenSilent.
Try adding the authority, and user if necessary:
myMSALObj
.acquireTokenSilent(
applicationConfig.graphScopes,
applicationConfig.authority
)
.then(
...
Just to get back to it. I solve it by moving the whole project into fresh create-react-app. It looks like there was more than 1 instance of MSAL object thus more than one call/token at the same time.
Weird but solved my problem.
I know this is an old question, but I'll answer it anyway since I had the same issue.
My workaround for this problem was to just clear the cache whenever this error happened. It worked in my case since this wan't a regularly occurring error for my use case.
In my project's configuration, the site is also set up to refresh and retry when an error like this occurs. So after clearing the cache, the site would reload and would work as expected since there would be no conflicting tokens in the cache.
import { AuthCache } from 'msal/lib-commonjs/cache/AuthCache';
...
const authProvider = new MsalAuthProvider(
configuration,
authenticationParameters,
msalProviderConfig,
);
authProvider.registerErrorHandler((authError: AuthError | null) => {
if (!authError) {
return;
}
console.error('Error initializing authProvider', authError);
// This shouldn't happen frequently. The issue I'm fixing is that when upgrading from 1.3.0
// to 1.4.3, it seems that the new version creates and stores a new set of auth credentials.
// This results in the "multiple_matching_tokens" error.
if (authError.errorCode === 'multiple_matching_tokens') {
const authCache = new AuthCache(
configuration.auth.clientId,
configuration.cache.cacheLocation,
configuration.cache.storeAuthStateInCookie,
);
authCache.clear();
console.log(
'Auth cache was cleared due to incompatible access tokens existing in the cache.',
);
}
Related
I am trying to implement MSAL auth via Redirect in Vue js.
I have followed the official guide but when I open an app, handleRedirect is started, then it redirects to a blank page and hangs there, the console looks like this:
handleRedirectPromise called but there is no interaction in progress, returning null.
Emitting event: msal:acquireTokenStart
Emitting event: msal:handleRedirectEnd
Null, no response
Emitting event: msal:acquireTokenStart
I cannot get how to implement the redirect flow (popup flow is not an option).
My config:
// login
var msalConfig = {
auth: {
clientId: clientId,
authority: this.authority, //https://login.microsoftonline.com/common/ default
redirectUri: this.redirect + 'redirects/login-msal-powerbi/index.html', // blank page
postLogoutRedirectUri: null
},
cache: {
cacheLocation: "localStorage", // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
},
allowRedirectInIframe: true
}
My main LoginUser function:
// login
loginUser: function() {
var that = this;
// Prepare config
this.msalInstance = new msal.PublicClientApplication(msalConfig);
// Register Callbacks for Redirect flow
var request = this.msalGetRequestLoginRedirect(this.powerbi.scopes, this.login.aadTenant); // returns an object with authority and scopes
this.msalInstance.handleRedirectPromise()
.then(function(responce) {
that.msalHandleResponse(responce, that.msalInstance);
})
.catch((error) => {
console.log(error);
});
My msalHandleResponse function:
msalHandleResponse: function(response, msalInstance) {
if (response !== null) {
console.log(response);
}else {
console.error('null!!') // returns null
return msalInstance.loginRedirect({scopes:[
"https://analysis.windows.net/powerbi/api/Dashboard.Read.All",
"https://analysis.windows.net/powerbi/api/Dataset.Read.All",
"https://analysis.windows.net/powerbi/api/Report.Read.All",
"https://analysis.windows.net/powerbi/api/Group.Read.All",
"https://analysis.windows.net/powerbi/api/Workspace.Read.All",
"https://analysis.windows.net/powerbi/api/UserState.ReadWrite.All"
], responseMode:"query"});
}
},
My acquireTokenRedirect function:
msalGetTokenRedirect: function(msalInstance) {
var that = this;
msalInstance.acquireTokenSilent(this.requestsObj.silentRequest)
.then(function(tokenResponse) {
// Optionally make something with tokenResponse
console.log("Access token acquired silently...");
return tokenResponse;
}).catch(function(error) {
if (error instanceof InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return msalInstance.acquireTokenRedirect(this.requestsObj.request)
.then(function(tokenResponse) {
console.log('TOKEN RESPONSE',tokenResponse);
}).catch(function(error) {
console.error(error);
});
}
});
},
Are you calling handleRedirectPromise on your redirectUri (blank page)?
I'm getting the error below. My problem is NOT with the actual error but the fact that it is saying that the error was Uncaught. If you take a look at my auth.service.ts and sign-in.component.ts files I am catching the error.
My question is, why am getting the Error: Uncaught (in promise) error in the console? What am I missing?
I'm using
"#angular/fire": "^7.0.4"
"firebase": "^9.0.2"
"rxjs": "6.6.7"
auth.service.ts
/**
* Sign in
*
* #param credentials
*/
signIn(credentials: { email: string; password: string }): Promise<any>
{
return this.auth.signInWithEmailAndPassword(credentials.email, credentials.password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
//console.log(user);
// Store the access token in the local storage
userCredential.user.getIdToken().then(token => {
this.accessToken = token;
//console.log(token);
})
// Set the authenticated flag to true
this._authenticated = true;
// Store the user on the user service
//this._userService.user = user;
// ...
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
console.log('Show Error', error.code);
throw errorCode;
});
}
sign-in.component.ts
/**
* Sign in
*/
signIn(): void
{
// Return if the form is invalid
if ( this.signInForm.invalid )
{
return;
}
// Disable the form
this.signInForm.disable();
// Hide the alert
this.showAlert = false;
// Sign in
this._authService.signIn(this.signInForm.value)
.then(
() => {
// Set the redirect url.
// The '/signed-in-redirect' is a dummy url to catch the request and redirect the user
// to the correct page after a successful sign in. This way, that url can be set via
// routing file and we don't have to touch here.
const redirectURL = this._activatedRoute.snapshot.queryParamMap.get('redirectURL') || '/signed-in-redirect';
// Navigate to the redirect url
this._router.navigateByUrl(redirectURL);
},
(response) => {
console.log('error from auth.service', response);
// Re-enable the form
this.signInForm.enable();
// Reset the form
this.signInNgForm.resetForm();
// Set the alert
this.alert = {
type : 'error',
message: 'Wrong email or password'
};
// Show the alert
this.showAlert = true;
}
);
}
First of all, my native language is not English, so if I write like a fool you know why.
try this:
_authService.service.ts
import { getAuth, signInWithEmailAndPassword, Auth, inMemoryPersistence, browserLocalPersistence } from '#angular/fire/auth';
constructor(private _fireAuth: Auth,) {
/**
* Sign-in
*
* #param credentials
* #param rememberMe
*/
async signIn(credentials: { email: string; password: string }, rememberMe: boolean): Promise<any> {
// firebase Persistence.LOCAL browserLocalPersistence
// firebase Persistence.SESSION browserSessionPersistence
// firebase Persistence.NONE inMemoryPersistence
return new Promise(async (resolve, reject) => {
//Initialize auth()
const auth = getAuth();
// Extra function
if (rememberMe) {
await getAuth().setPersistence(browserLocalPersistence).catch(error => reject(-1));
} else {
await getAuth().setPersistence(inMemoryPersistence).catch(error => reject(-1));
}
signInWithEmailAndPassword(auth, credentials.email, credentials.password).then(async (userCredential) => {
// Signed in
const user = userCredential.user;
console.log(user);
// Store the access token in the local storage
await userCredential.user.getIdTokenResult().then(token => {
this.accessToken = token.token;
console.log(token);
})
// Set the authenticated flag to true
this._authenticated = true;
}).catch(error => reject(error.code));
});
}
Note: As you can see I have added some extra functions that you can remove if you are not interested (setPersistence), this allows you to take into account the user's choice to stay logged in if he wants to, or to remove his login when he closes the tab.
sign-in.component.ts
alert = {
userNotFound : false,
wrongPassword: false,
unknownError : false,
};
/**
* Sign in
*/
signIn(): void
{
// Return if the form is invalid
if ( this.signInForm.invalid )
{
return;
}
// Disable the form
this.signInForm.disable();
// Hide the alert
this.showAlert = false;
// Sign in
this._authService.signIn(this.signInForm.value)
.then(
() => {
// Set the redirect url.
// The '/signed-in-redirect' is a dummy url to catch the request and redirect the user
// to the correct page after a successful sign in. This way, that url can be set via
// routing file and we don't have to touch here.
const redirectURL = this._activatedRoute.snapshot.queryParamMap.get('redirectURL') || '/signed-in-redirect';
// Navigate to the redirect url
this._router.navigateByUrl(redirectURL);
},
(response) => {
console.log('error from auth.service', response);
// Re-enable the form
this.signInForm.enable();
// Reset the form
this.signInNgForm.resetForm();
// Set the alert
if (error === - 1) {
this.alert.unknownError = true;
} else if (error === 'auth/email-not-found' || error === 'auth/user-not-found') {
this.alert.userNotFound = true;
} else if (error === 'auth/wrong-password') {
this.alert.wrongPassword = true;
}
}
);
}
For me, explicitly catching the error as in the above answers still resulted in the error being sent to the console as Uncaught (in Promise). For whatever reason, in addition to sending the error to be caught via Promise.catch, Angular was routing the error through its default ErrorHandler and claiming that the error was uncaught. I had to override the default ErrorHandler with my own, detect that these were FirebaseErrors and then ignore them (since I already had an explicit Promise.catch defined where I needed it).
Some tips in case they are helpful:
Angular only recognizes error handlers defined on the root module. Definitions on child modules seem to be ignored. You get one global error handler that needs to do everything.
The core FirebaseErrors seem to be stored in a rejection property on the main error object. You can detect them like so:
import { ErrorHandler, Injectable } from "#angular/core";
import { FirebaseError } from "firebase/app";
interface AngularFireError extends Error {
rejection: FirebaseError;
}
function errorIsAngularFireError(err: any): err is AngularFireError {
return err.rejection && err.rejection.name === 'FirebaseError';
}
// Not providedIn 'root': needs special handling in app.module to override default error handler.
#Injectable()
export class YourErrorHandler implements ErrorHandler {
handleError(error: any) {
// AngularFire errors should be catchable and handled in components; no need to further process them.
if (!errorIsAngularFireError(error)) {
console.error(error);
}
}
}
And in your root module:
providers: [
...,
{ provide: ErrorHandler, useClass: YourErrorHandler }
],
as Frank mentionned you are throwing an error without catching it back at a higher lever what I would try would be to do as so:
try {
this._authService.signIn(this.signInForm.value)
.then(
() => {
// Set the redirect url.
// The '/signed-in-redirect' is a dummy url to catch the request and redirect the user
// to the correct page after a successful sign in. This way, that url can be set via
// routing file and we don't have to touch here.
const redirectURL = this._activatedRoute.snapshot.queryParamMap.get('redirectURL') || '/signed-in-redirect';
// Navigate to the redirect url
this._router.navigateByUrl(redirectURL);
},
(response) => {
console.log('error from auth.service', response);
// Re-enable the form
this.signInForm.enable();
// Reset the form
this.signInNgForm.resetForm();
// Set the alert
this.alert = {
type : 'error',
message: 'Wrong email or password'
};
// Show the alert
this.showAlert = true;
}
);
} catch(e) {}
just surround your code with a try catch block so the error is muted. I didn't tested it but maybe calling another catch method after then could do the trick still it would be at the same level (Promise level) so I'm not sure it would work.
I'm using firebase for authentication. Everytime before I do a request to the backend I request for the idToken.
service.interceptors.request.use(async request => {
const token = await firebase.auth().currentUser.getIdToken();
if (!token && request.url !== '/signIn' && request.url !== '/register') {
toast.error('Not authenticated');
return;
}
request.headers.Authorization = 'Bearer ' + token;
return request;
});
Additionally I have a request to the backend that will run in the mounted hook of my vue component.
mounted() {
plantService.getPlants().then(data => (this.suspicionList = data));
}
Usually, this works but the problem is, when I refresh the page, firebase.auth().currentUser is null and the request will fail thus not returning any data.
I already tried creating a computed property and observe that one in a watcher. But not working.
Also, I have an observer for the user in my main.js file like this:
firebase.auth().onAuthStateChanged(user => {
store.dispatch('FETCH_USER', user);
});
Anyone has an idea?
Thanks!
If I correctly understand your problem, the following should do the trick:
mounted() {
firebase.auth().onAuthStateChanged(user => {
if (user) {
plantService.getPlants().then(data => (this.suspicionList = data));
}
});
}
I have an Outlook add-ins using React + TypeScript, debugging locally with Node.js. I'm using the office-js-helpers library to authenticate and the msgraph-sdk-javascript Graph client library. As a POC I'm simply trying to verify that I can successfully call Graph, by retrieving details of the current email by its id. I can successfully use the office-js-helpers Authenticator to authorize the app, and successfully retrieve a token.
However, when I use the Graph client to make a call to v1/me/messages/{ID}, I get:
"401 InvalidAuthenticationToken: Access token validation failure"
I'm not sure whether this is a problem with the way I'm using the Authenticator, or a problem with my add-in or app's manifests. My add-in is using these for AppDomains:
<AppDomains>
<AppDomain>https://localhost:3000</AppDomain>
<AppDomain>https://login.microsoftonline.com</AppDomain>
</AppDomains>
I am using https://localhost:3000 as my app's redirect URI, with implicit auth enabled.
If I use the force option with the authenticate method, I also get this error:
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://login.microsoftonline.com') does not match the recipient window's origin ('https://localhost:3000').
However, I am able to retrieve a token.
What am I doing wrong? I'm not certain about the flow for the Authenticator, in terms of when to use authenticator.tokens.get and authenticator.authenticate. For the first run I assume always authenticate and no need to use tokens.get, and for second run I assume just use tokens.get, but if I try either of those or always both it doesn't seem to change the result of an invalid token.
import * as React from "react";
import { Button, ButtonType, TextField } from "office-ui-fabric-react";
import { Authenticator, Utilities, DefaultEndpoints } from "#microsoft/office-js-helpers";
import * as Graph from "#microsoft/microsoft-graph-client";
export default class GetItemOJSHelpers extends React.Component<any, any> {
constructor(props) {
super(props);
this.getEmail = this.getEmail.bind(this);
this.callGraph = this.callGraph.bind(this);
this.getItemRestId = this.getItemRestId.bind(this);
this.state = { graphResponse: "", accessToken: "" };
console.log("====GetItemOJSHelpers loaded");
}
getEmail() {
console.log("====getEmail(): Entered ");
//debugger;
// Get the access token and create a Microsoft Graph client
let authenticator = new Authenticator();
// register Microsoft (Azure AD 2.0 Converged auth) endpoint
authenticator.endpoints.registerMicrosoftAuth("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", {
redirectUrl: "https://localhost:3000/index.html",
scope: "Mail.ReadWrite User.Read User.ReadBasic.All"
});
console.log("====getEmail(): Getting token");
let authObject = authenticator.tokens.get("Microsoft");
let accessToken = authObject.access_token;
if (accessToken !== null) {
console.log(`====getEmail(): Current cached token: ${accessToken}`);
this.callGraph(accessToken);
return;
} else {
// for the default Microsoft endpoint
//If the user, rejects the grant to the application then you will receive an error in the catch function.
authenticator
.authenticate(DefaultEndpoints.Microsoft)
.then(function(token) {
/* Microsoft Token */
console.log(`====getEmail(): Authenticated; auth token: ${token.access_token}`);
accessToken = token.access_token;
})
.catch(function(error) {
//debugger;
console.log("====getEmail(): authenticate error");
Utilities.log(error);
throw new Error("Failed to login using your Office 365 Account");
});
}
console.log(`====getEmail(): Current token: ${accessToken}`);
this.callGraph(accessToken);
}
callGraph(token) {
// Get the item's REST ID
let itemId = this.getItemRestId();
console.log(`====callGraph(): itemId ${itemId}`);
const client = Graph.Client.init({
authProvider: done => {
done(null, token); //first parameter takes an error if you can't get an access token
},
debugLogging: true
});
client
.api("me/messages/" + itemId)
.version("v1.0")
.get()
.then(function(item) {
//debugger;
console.log("Email " + item.Subject + " retrieved!!!");
})
.then(function() {
console.log("====callGraph(): complete");
//debugger;
})
.catch(err => {
//debugger;
//403 Forbidden! code: "ErrorAccessDenied", message: "Access is denied. Check credentials and try again."
//Also 401 InvalidAuthenticationToken: Access token validation failure.
console.log(`====callGraph(): error! ${err.statusCode}:'${err.code}': ${err.message}`);
});
}
getItemRestId() {
if (Office.context.mailbox.diagnostics.hostName === "OutlookIOS") {
// itemId is already REST-formatted
return Office.context.mailbox.item.itemId;
} else {
// Convert to an item ID for API v2.0
return Office.context.mailbox.convertToRestId(
Office.context.mailbox.item.itemId,
Office.MailboxEnums.RestVersion.v2_0
);
}
}
render() {
return (
<div>
<Button
id="getEmailButton"
className="ms-welcome__action ms-bgColor-red"
buttonType={ButtonType.primary}
onClick={this.getEmail}
>
Call Graph
</Button>
<div>
<h3> Access Token </h3>
<TextField id="accessToken" />
</div>
<div>
<h3>Graph API Call Response</h3>
<TextField id="graphResponse" />
</div>
</div>
);
}
}
I have simple nodejs app with sockets and I've faced an error where I can't find any solution. So I'm emiting from app to client and nothing happens there. Or client can't receive it - I don't know, because I can't check if it was successfully emited to client. This is the error I got when I tried to debug callback of emit:
Error: Callbacks are not supported when broadcasting
This my app code:
http.listen(6060, function () {
console.log("Listening on *: 6060");
});
io.set('authorization', function (handshakeData, accept) {
var domain = handshakeData.headers.referer.replace('http://', '').replace('https://', '').split(/[/?#]/)[0];
if ('***' == domain) {
accept(null, true);
} else {
return accept('You must be logged in to take an action in this site!', false);
}
});
io.use(function (sock, next) {
var handshakeData = sock.request;
var userToken = handshakeData._query.key;
if (typeof userToken !== null && userToken !== 0 && userToken !== '0' && userToken.length > 0) {
connection.query('***',
[xssfilter.filter(validator.escape(userToken))],
function (error, data) {
if (error) {
debug('Cant receive user data from database by token');
next(new Error('Failed to parse user data! Please login!'));
} else {
// load data to this user.
_updateUsers(xssfilter.filter(validator.escape(userToken)), 'add', data[0], sock.id);
_loadPreData();
next(null, true);
}
});
} else {
debug('Cant receive user token');
next(new Error('Failed to parse user data! Please login!'));
}
sock.on("disconnect", function () {
_updateUsers(false, 'remove', false, sock.id);
});
});
// we need to show people online count
io.emit('online-count', {
count: Object.keys(connectedUsers).length
});
And the function used above:
function _updateUsers(userToken, action, userData, sockedID) {
switch (action) {
case 'add':
connectedUsers[sockedID] = {...};
io.emit('online-count', io.emit('online-count', {
count: Object.keys(connectedUsers).length
}););
break;
case 'remove':
delete connectedUsers[sockedID];
io.emit('online-count', io.emit('online-count', {
count: Object.keys(connectedUsers).length
}););
break;
}
}
so after emiting online-count I should accept it on the client side as I'm doing it:
var socket;
socket = io(globalData.socketConn, {query: "key=" + globalData.userData.token});
socket.on('connect', function (data) {
console.log('Client side successfully connected with APP.');
});
socket.on('error', function (err) {
error('danger', 'top', err);
});
socket.on('online-count', function (data) {
console.log('Got online count: ' + data.count);
$('#online_count').html(data.count);
});
but the problem is with this online-count.. Nothing happens and it seems that it's not was even sent from node app. Any suggestions?
The problem was with my logic - I was sending online count only if new user were connecting/disconnecting. Problem were solved by adding function to repeat itself every few seconds and send online count to client side.