Page 1 of 1
Access token never expires APIv2 1.36.35
Posted: Thu Jan 02, 2025 7:09 am
by alextuning
Hi @iconnor and happy NY!
I recently faced an issue that on Ubuntu 22.04.5 ZM 1.36.35 access token received with v2.0 API is not getting expired.
Using same token I can access a video stream days later. I mean that every time a new connection is initiated with same access token (not a case viewtopic.php?t=30611) I find the token is not expired
Code: Select all
OPT_USE_AUTH enabled
AUTH_HASH_SECRET defined
OPT_USE_AP enabled
OPT_USE_LEGACY_API_AUTH enabled (tried to disable as well - same)
AUTH_HASH_TTL 2
Can you please reproduce the issue on your environment and confirm?
Re: Access token never expires APIv2 1.36.35
Posted: Thu Jan 02, 2025 3:58 pm
by iconnor
This is interesting, because one issue I face is I have a customer who for whatever reason is using an old zmNinja which waits for the token to expire before getting a new one, which spams the logs. So in newer zmNinja I get a new token BEFORE the current one expires.
Re: Access token never expires APIv2 1.36.35
Posted: Sat Jan 04, 2025 5:30 pm
by alextuning
@iconnor, I was able to reproduce the issue on my fresh test environment Ubuntu 22.04.5 with ZM 1.36.35.
Might probably, the issue lays on service level as path /zm/api/host/monitors.json returns 401 Unauthorized "Expired token" as expected but camera stream still accessible. "Revoke all tokens" via web UI works fine - can not get video stream with token. Log file contains not any meaningful information.
Can you give me some steps how to fix this?
Code: Select all
root@zm:~# date
Sun Jan 5 08:09:20 AM UTC 2025
root@zm:~# curl -sk -d 'token=eyJ0eXAiOiJKV1QiLCJhbGci<...>sdwU' http://localhost/zm/api/host/monitors.json | jq .
{
"success": false,
"data": {
"name": "Expired token",
"message": "Expired token",
"url": "/zm/api/host/monitors.json",
"exception": {
"class": "UnauthorizedException",
"code": 401,
"message": "Expired token"
}
}
}
root@zm:~# curl -vk 'http://localhost/zm/cgi-bin/nph-zms?mode=jpeg&monitor=1&token=eyJ0eXAiOiJKV1QiLCJhbGci<...>sdwU'
* Trying 127.0.0.1:80...
* Connected to localhost (127.0.0.1) port 80 (#0)
> GET /zm/cgi-bin/nph-zms?mode=jpeg&monitor=1&token=eyJ0eXAiOiJKV1QiLCJhbGci<...>sdwU HTTP/1.1
> Host: localhost
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: ZoneMinder Video Server/1.36.35
< Last-Modified: Sun, 05 Jan 2025 08:09:23 GMT
< Expires: Mon, 26 Jul 1997 05:00:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate
< Cache-Control: post-check=0, pre-check=0
< Pragma: no-cache
< Content-Type: multipart/x-mixed-replace; boundary=ZoneMinderFrame
<
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.
* Failure writing output to destination
* Closing connection 0
Re: Access token never expires APIv2 1.36.35
Posted: Mon Jan 06, 2025 9:21 pm
by iconnor
I wonder is your system time zone and php/zm timezone synced up?
Otherwise I'll have to dig in see if I can figure it out.
Re: Access token never expires APIv2 1.36.35
Posted: Tue Jan 07, 2025 2:51 pm
by alextuning
Just made sure php/system/mysql timezones are the same:
Code: Select all
root@zm:~ # timedatectl | egrep "Time zone|synchronized"
Time zone: Asia/Novosibirsk (+07, +0700)
System clock synchronized: yes
mysql> SELECT @@system_time_zone;
+--------------------+
| @@system_time_zone |
+--------------------+
| +07 |
+--------------------+
1 row in set (0.00 sec)
mysql> SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);
+--------------------------------+
| TIMEDIFF(NOW(), UTC_TIMESTAMP) |
+--------------------------------+
| 07:00:00 |
+--------------------------------+
1 row in set (0.00 sec)
root@zm:~ # grep "timezone" /etc/php/8.1/apache2/php.ini
date.timezone = 'Asia/Novosibirsk'
Re: Access token never expires APIv2 1.36.35
Posted: Tue Jan 14, 2025 11:17 am
by alextuning
iconnor wrote: ↑Mon Jan 06, 2025 9:21 pm
I wonder is your system time zone and php/zm timezone synced up?
Otherwise I'll have to dig in see if I can figure it out.
Dear @iconnor,
I hope this message finds you well.
I wanted to kindly inquire if there have been any updates regarding this matter?
Re: Access token never expires APIv2 1.36.35
Posted: Tue Jan 21, 2025 1:56 pm
by alextuning
iconnor wrote: ↑Mon Jan 06, 2025 9:21 pm
I wonder is your system time zone and php/zm timezone synced up?
Otherwise I'll have to dig in see if I can figure it out.
Hi @iconnor,
Is there any way to call "Revoke all tokens" (or specific user) periodically as workaround?
Re: Access token never expires APIv2 1.36.35
Posted: Tue Jan 21, 2025 11:42 pm
by iconnor
Yes there is a way.. it's in the user options.. it isn't ideal but if you look in the code you should see how to do it.
A revamp of all this is on the todo list, but has been for like 2 years..
Re: Access token never expires APIv2 1.36.35
Posted: Thu Jan 23, 2025 2:29 pm
by alextuning
iconnor wrote: ↑Tue Jan 21, 2025 11:42 pm
Yes there is a way.. it's in the user options.. it isn't ideal but if you look in the code you should see how to do it.
A revamp of all this is on the todo list, but has been for like 2 years..
Hope someday this is got properly fixed
To whom it may concern as workaround a simple python script to revoke all tokens bellow:
Code: Select all
import mysql.connector
from mysql.connector import Error
import os
import time
class DatabaseManager:
def __init__(self, config_file_path):
self.config = self.load_env(config_file_path)
def load_env(self, file_path):
if not os.path.exists(file_path):
raise FileNotFoundError(f"Configuration file not found: {file_path}")
config = {}
with open(file_path, 'r') as file:
for line in file:
line = line.strip()
if not line or line.startswith('#'):
continue
key, value = line.split('=', 1)
config[key.strip()] = value.strip()
return {
'ZM_DB_HOST': config.get('ZM_DB_HOST', 'localhost'),
'ZM_DB_NAME': config.get('ZM_DB_NAME', ''),
'ZM_DB_USER': config.get('ZM_DB_USER', ''),
'ZM_DB_PASS': config.get('ZM_DB_PASS', '')
}
def db_connect(self):
try:
conn = mysql.connector.connect(
host=self.config['ZM_DB_HOST'],
database=self.config['ZM_DB_NAME'],
user=self.config['ZM_DB_USER'],
password=self.config['ZM_DB_PASS']
)
return conn
except Error as e:
print(f"Error connecting to MySQL database: {str(e)}")
return None
def db_query(self, query, params=None):
conn = self.db_connect()
if conn is None:
return None
cursor = conn.cursor()
try:
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
conn.commit()
return cursor
except Error as e:
print(f"SQL Error: {str(e)}")
return None
finally:
cursor.close()
conn.close()
def revoke_all_tokens(self):
min_token_time = int(time.time())
result = self.db_query('UPDATE `Users` SET `TokenMinExpiry` = %s', (min_token_time,))
if result is not None:
print('All tokens have been revoked.')
else:
print('Failed to revoke tokens.')
if __name__ == '__main__':
config_file_path = '/etc/zm/zm.conf'
db_manager = DatabaseManager(config_file_path)
db_manager.revoke_all_tokens()