Traccar Websocket not working with multi-tenancy - laravel

I have a multi-tenant Laravel web application, using stancl/tenancy for multi-tenancy support in Laravel. I also created a separate traccar server. Each tenant has a different user in the traccar server they have their own subdomains and each tenant have separates sessions. I have a problem about session cookie authentication I use the api POST api/session to create a session of the user and connect to traccar websocket and establish connection but it cannot work with my multitenancy setup.
This is my code for websocket connection:
var ajaxTraccar = function (method, url, callback) {
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open(method, url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
callback(JSON.parse(xhr.responseText));
}
};
if (method == 'POST') {
xhr.setRequestHeader('Content-type', 'application/json');
}
xhr.send()
};
var openWebsocket = function(token){
ajaxTraccar('GET', 'https://traccarwebsite.server/api/server', function(server) {
ajaxTraccar('GET', 'https://traccarwebsite.server/api/session?token=' + token, function(user) {
ajaxTraccar('GET', 'https://traccarwebsite.server/api/devices', function(devices) {
var socket = new WebSocket('wss://traccarwebsite.server/api/socket');
socket.onclose = function (event) {
console.log('socket closed');
};
socket.onmessage = function (event) {
console.log("Socket Messaged);
};
});
});
});
};
function initMap() {
$.ajax({
//url: "http://[IP]:8082/api/session",
url: "https://traccarwebsite.server/api/session",
dataType: "json",
type: "post",
async: false,
data: {
email: "{{ $email }}",
password: "{{ $pass }}",
},
success: function(sessionResponse){
openWebsocket(sessionResponse.token)
}
});
}
Scenario: suppose I have two companies and open them at the same time. When I try to establish a WebSocket connection only one will connect and the other tenant can listen to the messages from the websocket.
How can I separate the WebSockets of tenants? Do I need to create a Proxy server?
Is this diagram viable?
What can I try next?

Related

Traccar Websocket not connecting

I'm new to WebSockets and I want to have real-time data from traccar so I'm trying to use their WebSocket API but I couldn't connect to it. I followed their documentation https://www.traccar.org/traccar-api/.
And this is my code:
$.ajax({
//url: "http://[IP]:8082/api/session",
url: "http://demo.traccar.org/api/session",
dataType: "json",
type: "POST",
data: {
email: "useremail#email.net",
password: "myPassword"
},
success: function(sessionResponse){
console.log(sessionResponse);
openWebsocket();
}
});
var openWebsocket = function(){
var socket;
socket = new WebSocket('ws://demo.traccar.org/api/socket');
socket.onclose = function (event) {
console.log("WebSocket closed");
};
socket.onmessage = function (event) {
var i, j, store, data, array, entity, device, typeKey, alarmKey, text, geofence;
console.log(event.data);
};
};
and I'm encountering this error:
It looks like you're trying to send a request from a different host. It won't work because CORS is not enabled on the demo server.
The easiest workaround is set up a proxy, so both your JS app and the API are on the same host.

Ajax Request to netcore mvc app using IdentityServer redirects to authorization endpoint

I'm developing an netcore MVC application which uses Ajax requests to POST data to the server. I am using IdentityServer4 as my auth middleware. The flow is when the application is launched with a URL of http://localhost:6002 it redirect to IdentityServer (localhost:6000). The user logs in and is redirected to the main application which then works fine.
Ajax GET requests also work correctly. I can observe a list of claims on the Get action in the controller (User.Identity.Claims). However when I try a POST data from the server the request returns a 200 but from the Identity Server with Redirect=true
My call from the Javascript
applyUpdate(modelData) {
let that = this;
fetch("http://localhost:6002/Client/Update/", {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(modelData)
}
).then(function (response) {
return response;
}).then(function (outData) {
alert("saved");
});
}
The response I receive is
{type: "cors",
url: "http://localhost:6000/account/login?returnUrl=%2Fc…%26x-client..,
redirected: true,
status: 200,
ok: true}
I have enabled CORS on the applications as previously I was getting 405 issues. What appears to be happening is when I call my controller action from Javascript a redirect is being performed to IdentityServer which is then returning to the client without ever actually executing my action.
My controller action looks like
[Authorize]
[HttpPost]
public async Task<JsonResult> Update([FromBody] MyVM myVM)
{
}
If I remove the [Authorize] attribute the method is reached however the value of User.Identity.Claims is always empty where in a HTTP Get it contains a list of all my claims.
Below is the relevant section for configuring IdentityServer from the Startup.cs file
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role",
};
options.ClientId = "my client";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.UseTokenLifetime = false;
});
I am absolutely stumped at this behavior, any help would be greatly appreciated
UPDATE
Bizarrely I am using the Javascript Fetch API to do the POST, when I swap it out of use Jquery Ajax it works perfectly so many it's the Fetch API that isn't managing the Redirect. This call works fine
$.ajax({
type: "POST",
contentType: "application/json",
dataType: "json",
url: 'http://localhost:6002/Client/Update/',
data: JSON.stringify(modelData),
success: function success(msg) {
alert("saved");
},
error: function error(xhr, data, err) {
console.log(xhr);
}
});
I didn't want to have to include a dependency Jquery

Send an Authenticated Request

I have an MVC project secured with a asp.net identity:
This is my Login function:
self.login = function () {
event.preventDefault();
if (!$('#formLogin').valid()) {
return false;
}
var loginData = {
grant_type: 'password',
username: self.userName(),
password: self.password()
};
$.ajax({
type: 'POST',
url: '/API/Token',
data: loginData
}).done(function (data) {
// Cache the access token in session storage.
sessionStorage.setItem(tokenKey, data.access_token);
self.authenticate();
//change status of Login button to Logout
self.redirect('Users');
}).fail(showError);
}
self.authenticate = function () {
self.token = sessionStorage.getItem(tokenKey);
var headers = {};
console.log(self.token);
if (self.token) {
headers.Authorization = 'Bearer ' + self.token;
}
$.ajaxSetup({
headers: headers
});
}
That works fine, I get the token successfully and the headers are set up correctly.
The problem is that when I try to send a request- for example:
self.getUsers = function () {
$.get("/API/Users/GetUsers/");
}
I get a 401 error from the server:
"message": "Authorization has been denied for this request."
What am I doing wrong?
According to the official documentation of the jQuery.ajax, use this to set custom headers of each request:
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', '...');
}
});

Api sending data from backend to frontend

I have made an web app where the users can question and answer. By fetching the data from database, i send the required data to frontend using JSON. Here is the controller.
public function addquestion(Request $request)
{
if($request->Ajax())
{
if (Auth::check())
{
$question = new Question();
$question->question=$request->question;
$question->save();
$addq_id=new QuestionUser();
$addq_id->q_id=$question->id;
$addq_id->user_id = Auth::user()->id;
$addq_id->save();
echo json_encode(array('status'=>TRUE,'question'=>$request->question));die;
}
else
{
echo json_encode('notloggedin');die;
}
}
}
and here is jquery and ajax:
$(document).on('click','.addquestion',function(e)
{
e.preventDefault();
var question = $(this).prev().val();
$.ajax({
type:"POST",
url: "{{url('/music/addquestion')}}",
data: {
"_token": "{{ csrf_token() }}",
"question": question
},
success: function (data) {
var res = $.parseJSON(data);
if(res.status == true)
{
window.location.reload();
}
else if(res == 'notloggedin')
{
alert('You must login first!');
}
}
});
});
I also used the facebook api and google api for authentication. But, here i am confused on what is building your own api mean. Is my this app an api? I have read more of the articles that it is the interface between backend and frontend?

Crossdomain AJAX Call Internet explorer HTTP to HTTPS

I've figured out that to be able to send data cross-domain in Internet Explorer I should use the XDomainRequest.
By doing so I stumbled upon the next issue. I'm sending data from HTTP to HTTPS which gives the error SCRIPT5: Access is denied.. I tried adding header("Access-Control-Allow-Origin: *"); to the designated PHP file with no result.
Is there any way around this problem wherein I can send data from my HTTP domain to my HTTPS domain in Internet Explorer 9+?
The code I'm using right now(Which gives the script5 error):
if ('XDomainRequest' in window && window.XDomainRequest !== null) {
var xdr = new XDomainRequest(); // Use Microsoft XDR
xdr.open('get', url);
xdr.onload = function () {
var dom = new ActiveXObject('Microsoft.XMLDOM'),
JSON = $.parseJSON(xdr.responseText);
dom.async = false;
if (JSON == null || typeof (JSON) == 'undefined') {
JSON = $.parseJSON(data.firstChild.textContent);
console.log(JSON);
}
successCallback(JSON); // internal function
};
xdr.onerror = function() {
_result = false;
};
xdr.send();
}
I also tried adding $.support.cors = true; with no result.
Answering my own question:
I've fixed it by using JSONP:
$.ajax({
url: url,
data: thedata,
dataType: 'jsonp',
jsonp: 'callback',
jsonpCallback: 'jsonpCallbackFunc',
success: function (response) {
}
});

Resources