Failed to complete negotiation with the server - Angular + SignalR - javascript

I'm unable to establish a connection using Angular + Azure Functions using Nodejs. I know that the backend works because I created it using the following documentation and tested it with the client URL provided in docs. Messaging was working across multiple clients.
So now I'm trying to get Angular working. I've created a service for signalr in Angular to establish the connection, but I'm getting the errors
and on the network tab
I also tried replacing this code in service
private buildConnection = () => {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:7070") //use your api adress here and make sure you use right hub name.
.build();
};
with this code
private buildConnection = () => {
this.hubConnection = new signalR.HubConnectionBuilder()
.configureLogging(signalR.LogLevel.Debug)
.withUrl("http://localhost:7070/api", {
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets
})
.build();
per the recommended answer from this post but then it just returns
I've never used SignalR before so I'm trying to piece this together from looking at several tutorials. I appreciate any help!
signalr service
import { Injectable, EventEmitter } from "#angular/core";
import * as signalR from "#aspnet/signalr";
import { SignalViewModel } from "./signal-view-model";
#Injectable({
providedIn: "root"
})
export class SignalRService {
private hubConnection: signalR.HubConnection;
signalReceived = new EventEmitter<SignalViewModel>();
constructor() {
this.buildConnection();
this.startConnection();
}
private buildConnection = () => {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl("http://localhost:7070/api") //c
.build();
};
private startConnection = () => {
this.hubConnection
.start()
.then(() => {
console.log("Connection Started...");
this.registerSignalEvents();
})
.catch(err => {
console.log("Error while starting connection: " + err);
//if you get error try to start connection again after 3 seconds.
setTimeout(function () {
this.startConnection();
}, 3000);
});
};
private registerSignalEvents() {
this.hubConnection.on("SignalMessageReceived", (data: SignalViewModel) => {
this.signalReceived.emit(data);
});
}
}
just like from Reference Docs link above, this is what my azure functions looks like
messages
module.exports = async function (context, req) {
return {
"target": "newMessage",
"arguments": [ req.body ]
};
};
negotiate
module.exports = async function (context, req, connectionInfo) {
context.res.json(connectionInfo);
};
host.json
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
},
"Host": {
"LocalHttpPort": 7070,
"CORS": "http://localhost:4200",
"CORSCredentials": true
}
}

Ok, you can't connect your Angular with the SignalR function because de CORS polity.
You forgot to configure the CORS on your function.
Try with this configuration.

Related

Error: Node.js module defined by file index.js is expected to export function named xxxx

Hello there dev community. I´m trying to debug a firebase function and being trying using several tutorials, but with no success...
I´ve tried
(https://medium.com/#mwebler/debugging-firebase-functions-with-vs-code-3afab528bb36)
(https://medium.com/#david_mccoy/build-and-debug-firebase-functions-in-vscode-73efb76166cf)
My purpose is to get google contacts.
functions/index.js
const { google } = require('googleapis');
const oauthUserCredential = require('./oauthUserCredential.json')
const OAuth2 = google.auth.OAuth2
const key = require('./serviceAccountKey.json')
const jwt = new google.auth.JWT(key.client_email, null, key.private_key, 'https://www.googleapis.com/auth/contacts')
exports.getGoogleContacts = functions.https.onCall(async (data, context) => {
const requestingUser = data.requestingUser
console.log('getGoogleContacts-requestingUser', requestingUser)
const oauth2Client = new google.auth.OAuth2(
'client_id',
'client_secret',
'http://localhost:5000/xxx-xxx/us-central1/OAuthCallbackUrl'
);
const contacts = google.people({
version: 'v1',
auth: oauth2Client,
});
console.log('contacts ?', contacts)
(async () => {
const { data: groups } = await contacts.people.get({
resourceName: 'contactGroups',
});
console.log('Contact Groups:\n', groups);
})()
jwt.authorize((err, response) => {
console.log('inside authorize')
if (err) {
console.error(err);
response.end();
return;
}
// Make an authorized request to list contacts.
contacts.people.connections.list({
auth: authClient,
resourceName: 'people/me'
}, function (err, resp) {
if (err) {
console.error(err);
response.end();
return;
}
console.log("Success");
console.log(resp);
response.send(resp);
});
});
// this is another approach I´ve tried, but it´s also not working
const oAuth2Client = new OAuth2(
oauthUserCredential.web.client_id,
oauthUserCredential.web.client_secret,
oauthUserCredential.web.redirect_uris,
)
oAuth2Client.setCredentials({
refresh_token: oauthUserCredential.refresh_token
})
return new Promise((resolve, reject) => {
console.log('[INSIDE PEOPLE CONNECTIONS]')
contacts.people.connections.list({
auth: oauth2Client //authetication object generated in step-3
}, function (err, response) {
if (err) {
console.log('contacts.people.connections error')
console.log(err)
reject(new Error(err))
} else if (response) {
console.log('contacts.people.connections response')
console.log(response)
resolve(response)
}
});
})
.then(result => { return { found: result } })
.catch(err => { return { error: err } })
})
I´ve tried several different approachs and followed different tutorials
(Using Google People API with Cloud Functions for Firebase)
(https://flaviocopes.com/google-api-authentication/)
(https://medium.com/#smccartney09/integrating-firebase-cloud-functions-with-google-calendar-api-9a5ac042e869)
(https://cloud.google.com/community/tutorials/cloud-functions-oauth-gmail)
but none of them show clearly how could I get my contacts list.
I was able to use a client side code by following this tutorial (https://labs.magnet.me/nerds/2015/05/11/importing-google-contacts-with-javascript.html)
but I thought that living the client_id, client_secret and apiKey exposed in the client side would be a security problem...
I´m submitting also a tutorial request to make it very clear how to get contacts list from google account using firebase functions.
The Error you are receiving is because the cloud function cannot find the function named xxxx to execute, as you have not defined any function named xxxx in the index.js file.
Your Cloud Function to execute name, according to the error message is xxxx but the function that you are calling in index.js is getGoogleContacts. Please make sure that these names are the same, for example change getGoogleContacts to xxxx or change function to execute to getGoogleContacts

angular user-idle-service issue

I am using the user-idle-service (https://www.npmjs.com/package/angular-user-idle) for angular. I am using it for my angular 6 front end application, but am running into issues where my users are getting kicked out prematurely when they are active.
I fire the library as soon as a user logs in to the application. It works most of the time, but sometimes users are getting prematurely kicked out when they are active in their browser. I am using this only for browser based usage.
In my service class I fire the below, as well as configuration below that. Is there anything in the library I am using obviously wrong? :
constructor(private http: Http,
private router: Router,
private cookieService: CookieService,
private userIdle: UserIdleService) {
this.userIdle.setCustomActivityEvents(merge(
fromEvent(window, 'mousemove'),
fromEvent(window, 'resize'),
fromEvent(document, 'keydown'),
fromEvent(document, 'touchstart'),
fromEvent(document, 'touchend'),
fromEvent(window, 'scroll')
));
}
login(credentials: Credentials): Observable<any> {
return this.http.post<any>(this.loginUrl, JSON.stringify(credentials), {
headers: new HttpHeaders({'Content-Type': 'application/json'})
})
.pipe(map(userDetails => {
this.setUserDetailsAndExtendExpirationInLocalStorage(userDetails);
this.startUserIdle();
this.goToHome();
}));
}
startUserIdle() {
this.userIdle.startWatching();
this.subscriptions.push((this.userIdle.onTimerStart().subscribe()));
this.subscriptions.push((this.userIdle.onTimeout().subscribe(() => {
this.logout();
})));
this.subscriptions.push(this.userIdle.ping$.subscribe(() => {
this.refreshToken();
}));
}
refreshToken() {
return this.http.get<any>(this.refreshUrl, {
headers: new HttpHeaders({'Content-Type': 'application/json'})
}).subscribe(() => {
// do stuff
}, () => {
this.logout();
});
}
logout() {
this.goToLogin();
this.removeUserDetails();
this.removeSessionToken();
this.userIdle.resetTimer();
this.userIdle.stopTimer();
this.userIdle.stopWatching();
this.subscriptions.forEach(subscription => subscription.unsubscribe());
this.setIsLoggedIn(false);
}
Also in my app.module.ts file I configure it like below:
UserIdleModule.forRoot({idle: 900, timeout: 1, ping: 840})

Swallowed message : Error: Uncaught (in promise): [object Undefined]

My login component briefly displays before being removed by an error message about an undefined object in a promise.
Here is the promise definition:
static init(): Promise<any> {
KeycloakClientService.auth.loggedIn = false;
return new Promise((resolve, reject) => {
const keycloakConfig = {
url: environment.KEYCLOAK_URL,
realm: environment.KEYCLOAK_REALM,
clientId: environment.KEYCLOAK_CLIENTID,
'ssl-required': 'external',
'public-client': true
};
const keycloakAuth: any = new Keycloak(keycloakConfig);
keycloakAuth.init({onLoad: 'check-sso'})
.success(() => {
KeycloakClientService.auth.loggedIn = true;
KeycloakClientService.auth.authz = keycloakAuth;
KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
+ '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
+ document.baseURI;
console.log('=======>> The keycloak client has been initiated successfully');
resolve('Succeeded in initiating the keycloak client');
})
.error(() => {
reject('Failed to initiate the keycloak client');
});
});
}
It is called by:
KeycloakClientService.init()
.then(
() => {
console.log('The keycloak client has been initialized');
}
)
.catch(
(error) => {
console.log(error);
window.location.reload();
}
);
The console shows both messages:
The keycloak client has been initiated successfully
The keycloak client has been initialized
I'm using Angular 6.0.4 and tried following this blog
Any way to work around this error so as to keep my login form displayed ?
UPDATE: I tried using an observable instead of a promise but the issue remained the same:
public init(): Observable<any> {
KeycloakClientService.auth.loggedIn = false;
return new Observable((observer) => {
const keycloakConfig = {
'url': environment.KEYCLOAK_URL,
'realm': environment.KEYCLOAK_REALM,
'clientId': environment.KEYCLOAK_CLIENTID,
'ssl-required': 'external',
'public-client': true
};
const keycloakAuth: any = new Keycloak(keycloakConfig);
keycloakAuth.init({ 'onLoad': 'check-sso' })
.success(() => {
KeycloakClientService.auth.loggedIn = true;
KeycloakClientService.auth.authz = keycloakAuth;
KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
+ '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
+ document.baseURI;
console.log('The keycloak auth has been initialized');
observer.next('Succeeded in initiating the keycloak client');
observer.complete();
})
.error(() => {
console.log('The keycloak client could not be initiated');
observer.error('Failed to initiate the keycloak client');
});
});
}
The whole source code is available on GitHub
UPDATE: Following an answer below, I also tried to use a then() and a catch() keywords but the error remained the exact same:
keycloakAuth.init({ 'onLoad': 'check-sso' })
.then(() => {
KeycloakClientService.auth.loggedIn = true;
KeycloakClientService.auth.authz = keycloakAuth;
KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
+ '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
+ document.baseURI;
console.log('The keycloak auth has been initialized');
observer.next('Succeeded in initiating the keycloak client');
observer.complete();
})
.catch(() => {
console.log('The keycloak client could not be initiated');
observer.error('Failed to initiate the keycloak client');
});
This is a wild guess, but maybe it's a conflict with Angular's zones. Since this is a security library it might not like that Angular has replaced core functions with proxies. For example, NgZone modifies window.setTimeout and the HTTP methods.
So you could try running this code outside of zones. The only problem here is that you're using a static function, and will have to make this an injectable service so that you can access NgZone
#Injectable()
export class KeycloakClientService {
public constructor(private zone: NgZone) {
}
public init(): Promise<any> {
KeycloakClientService.auth.loggedIn = false;
return new Promise((resolve, reject) => {
this.zone.runOutsideAngular(() => {
const keycloakConfig = {
url: environment.KEYCLOAK_URL,
realm: environment.KEYCLOAK_REALM,
clientId: environment.KEYCLOAK_CLIENTID,
'ssl-required': 'external',
'public-client': true
};
const keycloakAuth: any = new Keycloak(keycloakConfig);
keycloakAuth.init({onLoad: 'check-sso'})
.success(() => {
KeycloakClientService.auth.loggedIn = true;
KeycloakClientService.auth.authz = keycloakAuth;
KeycloakClientService.auth.logoutUrl = environment.KEYCLOAK_URL
+ '/realms/' + environment.KEYCLOAK_REALM + '/protocol/openid-connect/logout?redirect_uri='
+ document.baseURI;
console.log('=======>> The keycloak client has been initiated successfully');
resolve('Succeeded in initiating the keycloak client');
})
.error(() => {
reject('Failed to initiate the keycloak client');
});
});
}
}
}
The change here is to use zone.runOutsideAngular
If you remove the success block, where do you run your logic within success?
I read some of their source code, I think this is why success causes the problem:
Within keycloak.js, there is a function createNativePromise():
function createNativePromise() {
var p = {
setSuccess: function(result) {
p.success = true;
p.resolve(result);
},
setError: function(result) {
p.success = false;
p.reject(result);
}
};
p.promise = new Promise(function(resolve, reject) {
p.resolve = resolve;
p.reject = reject;
});
p.promise.success = function(callback) {
p.promise.then(callback);
return p.promise;
}
p.promise.error = function(callback) {
p.promise.catch(callback);
return p.promise;
}
return p;
}
And it's used this way(simplified code):
function refreshToken() {
var promise = createNativePromise();
...
if (refreshTokenFailed) {
promise.setError(true);
}
...
return promise.promise;
}
The problem is, promise.setError() calls promise.reject(result), so the promise is rejected, it's expecting a catch.
But within promise.success, there is a promise.promise.then(callback);, and nobody is catching this promise.
This is why you get the Uncaught (in promise): [object Undefined], and in my case, i always get Uncaught (in promise): true.
Solution:
Notice that promise.promise is a real Promise, so we can use then and catch instead of success and error.
The drawback is, the typescript type will be wrong.
We have observed a similar error about the promise object undefined. The situation was our local application was working fine with the local keycloak standalone server but faced this error when the local application trying to connect with a keycloak server hosted on the ABC server (ABC is used as a reference here to give any arbitrary name).
This issue was resolved when we hosted the application and the keycloak server both on the ABC server.
It looks like there are some time sync issues in different machines due to which the promise object is not returned.

Error 204 when trying to connect to SignalR

I'm not able to connect to my SignalR Hub in a ASP.NET Core 2.0.3 application running under Windows 7.
I'm using SignalR 1.0.0-alpha1-final from NuGet as server and the signalr-client-1.0.0-alpha2-final.min.js as JavaScript client.
Here is my hub:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
namespace MyProject
{
public class MyHub: Hub
{
public override async Task OnConnectedAsync()
{
await Clients.All.InvokeAsync("Send", $"{Context.ConnectionId} joined");
}
public Task Send(string message)
{
return Clients.All.InvokeAsync("Send", $"{Context.ConnectionId}: {message}");
}
}
}
Configure in startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseHangfireDashboard();
app.UseHangfireServer();
app.UseSignalR(routes =>
{
routes.MapHub<MyHub>("hubs");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
}
and in a test page:
let transportType = signalR.TransportType[getParameterByName('transport')] || signalR.TransportType.WebSockets;
let http = new signalR.HttpConnection(`http://${document.location.host}/hubs`, { transport: transportType });
var connection = new signalR.HubConnection(http);
but when this code executes I get an error 204 from the server.
UPDATE
Based upon the answer of #Gabriel Luci, here the working code:
let transportType = signalR.TransportType.LongPolling;
let http = new signalR.HttpConnection(`http://${document.location.host}/hubs`, { transport: transportType });
let connection = new signalR.HubConnection(http);
connection.start();
connection.on('Send', (message) => {
console.log(message);
});
...
connection.invoke('Echo', "Hello ");
There was an issue raised in GitHub for that: https://github.com/aspnet/SignalR/issues/1028
Apparently WebSockets doesn't work in IIS and IIS Express. You need to use long-polling. There's a snippet of sample code in that issue:
let connection = new HubConnection("someurl", { transport: signalR.TransportType.LongPolling });
connection.start().then(() => {});
In my case i have to remove allow any origin and replace it with WithOrigins and AllowCredentials
options.AddPolicy("policy",
builder =>
{
builder.WithOrigins("http://localhost:8081");
builder.AllowAnyMethod();
builder.AllowAnyHeader();
builder.AllowCredentials();
});

not getting updates on model. socket.io with sails.js

I have a sails app created using the options --no-linker --no-front end. The front end of the application is written in angular2. Making request get and post, seems to work fine.
When i send a get request to the route (to subscribe), i don't get any updates on model being created, updated or destroyed.
I also created a special action, so i could do thing myself, but still with no luck.
The updates performed on the route are made using socket. I don't know where am wrong here. Find my code below
import { Injectable, OnInit, EventEmitter } from '#angular/core'
import { Subject } from 'rxjs/Subject';
import { Donor } from './donor.interface';
import * as socketIO from 'socket.io-client'
import * as sailsIO from 'sails.io'
const url = 'http://localhost:1337'
const io = sailsIO(socketIO)
io.sails.reconnection = true;
io.sails.url = url;
io.socket.on('connect', function () {
console.log("connected to server")
io.socket.get('/donor', function (data, jwres) {
console.log("i subscribed", data, jwres)
})
io.socket.get('/donor/hello', function (data, jwres) {
console.log("i subscribed with hello", data, jwres)
})
io.socket.on('donor', function (data) {
console.log("new donor was created", data)
});
});
io.socket.on('disconnect', function () {
console.log('Lost connection to server');
});
DonorController.js
module.exports = {
hello: function (req, res) {
if (req.isSocket) {
Donor.watch(req.socket)
console.log("new subscriber found")
} else {
console.log("not a socket req")
}
return res.ok();
}
};
So i figured out the problem. When you make an update (CRUD), the socket performing such operation does not receive an update.
I spent hours before figuring this out. So what i do is act on the data if the CRUD operation is successful like what i would get listing on a model with on

Categories

Resources