Is your code hosted on a different host? Have you configured "SameSite"?
Yes my code in different host
exp
my config file :
<entry key='server.statistics'>http://172.100.10.10</entry>
<entry key='web.enable'>true</entry>
<entry key='web.address'>172.100.10.10</entry>
<entry key='web.port'>8082</entry>
<entry key='web.console'>true</entry>
<entry key='geocoder.enable'>true</entry>
<entry key='geocoder.type'>nominatim</entry>
<entry key='geocoder.url'>https://nominatim.openstreetmap.org/reverse</entry>
<entry key='geocoder.onRequest'>true</entry>
<entry key='geocoder.ignorePositions'>true</entry>
<entry key='geocoder.reuseDistance'>20</entry>
<entry key='filter.enable'>true</entry>
<entry key='filter.zero'>true</entry>
<entry key='filter.duplicate'>true</entry>
<entry key='filter.distance'>3</entry>
<entry key='processing.remoteAddress.enable'>true</entry>
<entry key='database.registerUnknown'>true</entry>
<entry key='database.ignoreUnknown'>false</entry>
<entry key='web.sameSiteCookie'>None</entry>
<entry key='web.origin'>*</entry>
Looks good. There must be some other issue with the session cookie.
i tried with Postman at first
next
next
Hi
i tried many type of method but every time the same point so i tried to execute the modern web-ui in separate server and the problem raised again so the problem of session still exist every time.
the only way that i found to add authentication with token like this in AsyncSocketServlet.java
package org.traccar.api;
import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
import org.traccar.Context;
import org.traccar.api.resource.SessionResource;
import org.traccar.config.Keys;
import org.traccar.model.User;
import javax.servlet.http.HttpSession;
import java.time.Duration;
import java.util.*;
public class AsyncSocketServlet extends JettyWebSocketServlet {
private static final String KEY_TOKEN = "token";
@Override
public void configure(JettyWebSocketServletFactory factory) {
factory.setIdleTimeout(Duration.ofMillis(Context.getConfig().getLong(Keys.WEB_TIMEOUT)));
factory.setCreator((req, resp) -> {
if (req.getSession() != null) {
long userId = (Long) ((HttpSession) req.getSession()).getAttribute(SessionResource.USER_ID_KEY);
return new AsyncSocket(userId);
} else {
Map params = getQueryMap(req.getQueryString());
String token = (String) params.get(KEY_TOKEN);
if (token != null) {
User user = Context.getUsersManager().getUserByToken(token);
if (user != null) {
Context.getPermissionsManager().checkUserEnabled(user.getId());
return new AsyncSocket(user.getId());
}
}
return null;
}
});
}
public static Map<String, String> getQueryMap(String query)
{
String[] params = query.split("&");
Map<String, String> map = new HashMap<String, String>();
for (String param : params)
{ String [] p=param.split("=");
String name = p[0];
if(p.length>1) {String value = p[1];
map.put(name, value);
}
}
return map;
}
}
After a successful compilation I tried the websocket with token in URL like this :
http://xx.xx.xx.xx:8082/api/soket?token=xxxxxxxxxxxxxxxxxxxxxxxxx
and connection successful, but i don't know if this will affect the rest of app or not ?
If you lose session, it will affect the rest of the app.
Perfect. I managed to connect here successfully using your script..
however I am using SSL on both ends.
Good, the only thing is that this change will only be used for the websocket not for managing devices and users or anything else, but I'm satisfied with this because I only need a real-time websocket for map integration.
but in this case it is possible to use the API to manage, although it is possible to send commands via socket... look at the console... enter traccar and send a command to a device..
yes but i mean like in modern interface you can do everything not just websocket modern interface uses session authentication but that's ok
Traccar-web simple/app.js working in firefox not in chrome sign in popup while call api/device. JSESSIONID Cookie value changing each url server and session. Session value not storing and not passing in subsequent request
package org.traccar.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
import org.traccar.api.resource.SessionResource;
import org.traccar.api.security.LoginResult;
import org.traccar.api.security.LoginService;
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.model.User;
import org.traccar.session.ConnectionManager;
import org.traccar.storage.Storage;
import org.traccar.storage.StorageException;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import jakarta.servlet.http.HttpSession;
import jakarta.ws.rs.core.Context;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.time.Duration;
import java.util.*;
@Singleton
public class AsyncSocketServlet extends JettyWebSocketServlet {
private final Config config;
private final ObjectMapper objectMapper;
private final ConnectionManager connectionManager;
private final Storage storage;
private static final String KEY_TOKEN = "token";
@Inject
private LoginService loginService;
@Inject
public AsyncSocketServlet(
Config config, ObjectMapper objectMapper, ConnectionManager connectionManager, Storage storage) {
this.config = config;
this.objectMapper = objectMapper;
this.connectionManager = connectionManager;
this.storage = storage;
}
@Override
public void configure(JettyWebSocketServletFactory factory) {
factory.setIdleTimeout(Duration.ofMillis(config.getLong(Keys.WEB_TIMEOUT)));
factory.setCreator((req, resp) -> {
if (req.getSession() != null) {
Long userId = (Long) ((HttpSession) req.getSession()).getAttribute(SessionResource.USER_ID_KEY);
if (userId != null) {
return new AsyncSocket(objectMapper, connectionManager, storage, userId);
} else {
Map<String, String> params = getQueryMap(req.getQueryString());
String token = (String) params.get(KEY_TOKEN);
if (token != null) {
LoginResult loginResult;
try {
loginResult = loginService.login(token);
} catch (StorageException | GeneralSecurityException | IOException e) {
return null;
}
User user = loginResult.getUser();
if (user != null) {
return new AsyncSocket(objectMapper, connectionManager, storage, user.getId());
}
}
return null;
}
}
return null;
});
}
public static Map<String, String> getQueryMap(String query)
{
String[] params = query.split("&");
Map<String, String> map = new HashMap<String, String>();
for (String param : params)
{ String [] p=param.split("=");
String name = p[0];
if(p.length>1) {String value = p[1];
map.put(name, value);
}
}
return map;
}
}
What I don't understand is how do you get the token? (/api/session/token
)?
The token is generated from the Web interface in parameter of user, you can check this custom version for websocket external access:
https://github.com/mr-wolf-gb/traccar-custom
Hi,
I'm so grateful for this biggest project.
The Traccar server works fine, and I'm working to consume the api in a laravel project.
Like in API-Reference everything fine. But I'm trying to show devices positions in real-time on leaflet map, in websocket documentation the only thing required is a session, I consulted the simple in GitHub traccar-web repository i tried to execute the app.js in simple but the ajax request not saving the session and in deviecs request a popup shown for credential.
I tried other javascript example and I tried using axios library, but every time stuck in the same point session not saved and if I enter credentials in the popup the websocket fail for connection.
var url = "http://xx.xx.xx.xx:8082"; var token = "eSQB2ee4PbQ7K3XNRimDmFIAUjFg7GrZ"; var ajax = 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() }; ajax('GET', url + '/api/server', function(server) { ajax('GET', url + '/api/session?token=' + token, function(user) { console.log('session', user); ajax('GET', url + '/api/devices', function(devices) { console.log('devices', devices); var socket = new WebSocket('ws' + url.substring(4) + '/api/socket'); socket.onopen = () => { console.log('socket Open') } socket.onerror = (error) => { console.log('socket error: ', error) } socket.onclose = function (event) { console.log('socket closed', event); }; socket.onmessage = function (event) { var data = JSON.parse(event.data); console.log('socket message : ', data) } }); }); });
This popup show when devices get fired in the ajax sequence
This is the details of session request works fine and session json received with user details and token.
This is the request details of devices after I decline the credential popup without entering credentials
I guess here that the session not saved by the ajax session request. i tried every thing in forum and i tried using axios library but the problem is the same.
I tried entering the credential in the popup and after submit the request works and i receive the devices list but the websocket fail because of session.
I hope to receive an answer for this problem, i feel like the solution is simple but i'm not watching it
Best regards.