diff --git a/src/aleph/vm/orchestrator/views/authentication.py b/src/aleph/vm/orchestrator/views/authentication.py index 2d43c07d3..1043ce994 100644 --- a/src/aleph/vm/orchestrator/views/authentication.py +++ b/src/aleph/vm/orchestrator/views/authentication.py @@ -46,7 +46,6 @@ class SignedPubKeyPayload(BaseModel): # {'pubkey': {'alg': 'ES256', 'crv': 'P-256', 'ext': True, 'key_ops': ['verify'], 'kty': 'EC', # 'x': '4blJBYpltvQLFgRvLE-2H7dsMr5O0ImHkgOnjUbG2AU', 'y': '5VHnq_hUSogZBbVgsXMs0CjrVfMy4Pa3Uv2BEBqfrN4'} # alg: Literal["ECDSA"] - domain: str address: str expires: str @@ -100,6 +99,7 @@ def content(self) -> SignedPubKeyPayload: class SignedOperationPayload(BaseModel): time: datetime.datetime method: Union[Literal["POST"], Literal["GET"]] + domain: str path: str # body_sha256: str # disabled since there is no body @@ -201,8 +201,8 @@ async def authenticate_jwk(request: web.Request) -> str: """Authenticate a request using the X-SignedPubKey and X-SignedOperation headers.""" signed_pubkey = get_signed_pubkey(request) signed_operation = get_signed_operation(request) - if signed_pubkey.content.domain != settings.DOMAIN_NAME: - logger.debug(f"Invalid domain '{signed_pubkey.content.domain}' != '{settings.DOMAIN_NAME}'") + if signed_operation.content.domain != settings.DOMAIN_NAME: + logger.debug(f"Invalid domain '{signed_operation.content.domain}' != '{settings.DOMAIN_NAME}'") raise web.HTTPUnauthorized(reason="Invalid domain") if signed_operation.content.path != request.path: logger.debug(f"Invalid path '{signed_operation.content.path}' != '{request.path}'") @@ -217,8 +217,8 @@ async def authenticate_websocket_message(message) -> str: """Authenticate a websocket message since JS cannot configure headers on WebSockets.""" signed_pubkey = SignedPubKeyHeader.parse_obj(message["X-SignedPubKey"]) signed_operation = SignedOperation.parse_obj(message["X-SignedOperation"]) - if signed_pubkey.content.domain != settings.DOMAIN_NAME: - logger.debug(f"Invalid domain '{signed_pubkey.content.domain}' != '{settings.DOMAIN_NAME}'") + if signed_operation.content.domain != settings.DOMAIN_NAME: + logger.debug(f"Invalid domain '{signed_operation.content.domain}' != '{settings.DOMAIN_NAME}'") raise web.HTTPUnauthorized(reason="Invalid domain") return verify_signed_operation(signed_operation, signed_pubkey) diff --git a/tests/supervisor/test_authentication.py b/tests/supervisor/test_authentication.py index 249806f01..4d881806a 100644 --- a/tests/supervisor/test_authentication.py +++ b/tests/supervisor/test_authentication.py @@ -105,7 +105,6 @@ async def test_require_jwk_authentication_expired(aiohttp_client): pubkey = { "pubkey": json.loads(key.export_public()), "alg": "ECDSA", - "domain": "localhost", "address": signer_account.address, "expires": "2023-05-02T10:44:42.754994Z", } @@ -155,7 +154,7 @@ async def view(request, authenticated_sender): ) ) } - payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/"} + payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/", "domain": "localhost"} headers["X-SignedOperation"] = json.dumps( { "payload": bytes.hex(json.dumps(payload).encode("utf-8")), @@ -189,7 +188,6 @@ async def view(request, authenticated_sender): pubkey = { "pubkey": json.loads(key.export_public()), "alg": "ECDSA", - "domain": "localhost", "address": signer_account.address, "expires": "2023-05-02T10:44:42.754994Z", } @@ -198,26 +196,22 @@ async def view(request, authenticated_sender): signed_message: SignedMessage = signer_account.sign_message(signable_message) pubkey_signature = to_0x_hex(signed_message.signature) - # Modify the payload to render the signature invalid - pubkey["domain"] = "baddomain" - invalid_pubkey_payload = json.dumps(pubkey).encode("utf-8").hex() - app.router.add_get("", view) client = await aiohttp_client(app) headers = { "X-SignedPubKey": ( json.dumps( { - "payload": invalid_pubkey_payload, + "payload": pubkey_payload, "signature": pubkey_signature, } ) ) } - payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/"} + invalid_operation_payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/", "domain": "baddomain"} headers["X-SignedOperation"] = json.dumps( { - "payload": bytes.hex(json.dumps(payload).encode("utf-8")), + "payload": bytes.hex(json.dumps(invalid_operation_payload).encode("utf-8")), "signature": "96ffdbbd1704d5f6bfe4698235a0de0d2f58668deaa4371422bee26664f313f51fd483c78c34c6b317fc209779f9ddd9c45accf558e3bf881b49ad970ebf0ade", } ) @@ -226,7 +220,7 @@ async def view(request, authenticated_sender): assert resp.status == 401, await resp.text() r = await resp.json() - assert {"error": "Invalid signature"} == r + assert {"error": "Invalid domain"} == r @pytest.mark.asyncio @@ -269,7 +263,6 @@ async def test_require_jwk_authentication_good_key(aiohttp_client, patch_datetim pubkey = { "pubkey": json.loads(key.export_public()), "alg": "ECDSA", - "domain": "localhost", "address": signer_account.address, "expires": (patch_datetime_now.FAKE_TIME + datetime.timedelta(days=1)).isoformat() + "Z", } @@ -292,7 +285,7 @@ async def view(request, authenticated_sender): app.router.add_get("", view) client = await aiohttp_client(app) - payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/"} + payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/", "domain": "localhost"} payload_as_bytes = json.dumps(payload).encode("utf-8") headers = {"X-SignedPubKey": pubkey_signature_header}