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
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.
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
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', '...');
}
});
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?
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) {
}
});