From f3012e652edef614d4f0ed169b320106a85d83b3 Mon Sep 17 00:00:00 2001 From: omnom62 Date: Wed, 5 Aug 2026 09:14:32 +1000 Subject: http-api: T8989: add REST Bearer token authentication Add JWT Bearer token support to the REST API, as an additional authentication method alongside the existing form-field key and X-API-Key header. - New POST /token endpoint mints a JWT for a valid API key - auth_required() accepts Authorization: Bearer alongside existing key/X-API-Key auth - New config nodes: service https api rest authentication {expiration, secret-length} (defaults: 3600s / 32 bytes) - REST tokens use an independent signing secret from GraphQL's, since GraphQL may not be enabled on all deployments and the two subsystems have different expiry requirements - nginx location regex updated to allow /token - service_https.py default-value merge generalized to also apply to the rest node, not just graphql, so REST authentication defaults populate correctly on commit --- src/services/api/rest/libs/__init__.py | 0 src/services/api/rest/libs/token_auth.py | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/services/api/rest/libs/__init__.py create mode 100644 src/services/api/rest/libs/token_auth.py (limited to 'src/services/api/rest/libs') diff --git a/src/services/api/rest/libs/__init__.py b/src/services/api/rest/libs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/services/api/rest/libs/token_auth.py b/src/services/api/rest/libs/token_auth.py new file mode 100644 index 000000000..24c491fde --- /dev/null +++ b/src/services/api/rest/libs/token_auth.py @@ -0,0 +1,54 @@ +# Copyright VyOS maintainers and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see . + +import datetime +from secrets import token_hex + +import jwt + +from ...session import SessionState + + +def init_secret(): + state = SessionState() + if state.rest_secret is not None: + return + length = state.rest_secret_len or 32 + state.rest_secret = token_hex(length) + + +def generate_token(key_id: str) -> dict: + state = SessionState() + init_secret() + exp_interval = state.rest_token_exp or 3600 + expiration = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta( + seconds=exp_interval + ) + payload = {'iss': 'vyos-rest-api', 'sub': key_id, 'exp': expiration} + token = jwt.encode(payload=payload, key=state.rest_secret, algorithm='HS256') + return {'token': token, 'expires_in': exp_interval} + + +def verify_token(token: str): + state = SessionState() + if state.rest_secret is None: + return None + try: + payload = jwt.decode(token, state.rest_secret, algorithms=['HS256']) + except jwt.ExpiredSignatureError: + return None + except jwt.PyJWTError: + return None + return payload.get('sub') -- cgit v1.2.3