Skip to content

Commit 3815aa7

Browse files
nesitorAndres D. Molins
andauthored
Move domain payload field to Operation token instead PubKey one (#647)
* Problem: If a user wants to manage different operations for a different CRNs, they have to sign a new pubkey token for every CRN, and this is so bad for the user experience. Solution: Move the `domain` field to the operation token payload instead the pubkey one, just to improve the user experience and maintain the security integrity. * Fix: Solved test error message failing. --------- Co-authored-by: Andres D. Molins <[email protected]>
1 parent 97f343a commit 3815aa7

File tree

2 files changed

+11
-18
lines changed

2 files changed

+11
-18
lines changed

src/aleph/vm/orchestrator/views/authentication.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ class SignedPubKeyPayload(BaseModel):
4646
# {'pubkey': {'alg': 'ES256', 'crv': 'P-256', 'ext': True, 'key_ops': ['verify'], 'kty': 'EC',
4747
# 'x': '4blJBYpltvQLFgRvLE-2H7dsMr5O0ImHkgOnjUbG2AU', 'y': '5VHnq_hUSogZBbVgsXMs0CjrVfMy4Pa3Uv2BEBqfrN4'}
4848
# alg: Literal["ECDSA"]
49-
domain: str
5049
address: str
5150
expires: str
5251

@@ -100,6 +99,7 @@ def content(self) -> SignedPubKeyPayload:
10099
class SignedOperationPayload(BaseModel):
101100
time: datetime.datetime
102101
method: Union[Literal["POST"], Literal["GET"]]
102+
domain: str
103103
path: str
104104
# body_sha256: str # disabled since there is no body
105105

@@ -201,8 +201,8 @@ async def authenticate_jwk(request: web.Request) -> str:
201201
"""Authenticate a request using the X-SignedPubKey and X-SignedOperation headers."""
202202
signed_pubkey = get_signed_pubkey(request)
203203
signed_operation = get_signed_operation(request)
204-
if signed_pubkey.content.domain != settings.DOMAIN_NAME:
205-
logger.debug(f"Invalid domain '{signed_pubkey.content.domain}' != '{settings.DOMAIN_NAME}'")
204+
if signed_operation.content.domain != settings.DOMAIN_NAME:
205+
logger.debug(f"Invalid domain '{signed_operation.content.domain}' != '{settings.DOMAIN_NAME}'")
206206
raise web.HTTPUnauthorized(reason="Invalid domain")
207207
if signed_operation.content.path != request.path:
208208
logger.debug(f"Invalid path '{signed_operation.content.path}' != '{request.path}'")
@@ -217,8 +217,8 @@ async def authenticate_websocket_message(message) -> str:
217217
"""Authenticate a websocket message since JS cannot configure headers on WebSockets."""
218218
signed_pubkey = SignedPubKeyHeader.parse_obj(message["X-SignedPubKey"])
219219
signed_operation = SignedOperation.parse_obj(message["X-SignedOperation"])
220-
if signed_pubkey.content.domain != settings.DOMAIN_NAME:
221-
logger.debug(f"Invalid domain '{signed_pubkey.content.domain}' != '{settings.DOMAIN_NAME}'")
220+
if signed_operation.content.domain != settings.DOMAIN_NAME:
221+
logger.debug(f"Invalid domain '{signed_operation.content.domain}' != '{settings.DOMAIN_NAME}'")
222222
raise web.HTTPUnauthorized(reason="Invalid domain")
223223
return verify_signed_operation(signed_operation, signed_pubkey)
224224

tests/supervisor/test_authentication.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ async def test_require_jwk_authentication_expired(aiohttp_client):
105105
pubkey = {
106106
"pubkey": json.loads(key.export_public()),
107107
"alg": "ECDSA",
108-
"domain": "localhost",
109108
"address": signer_account.address,
110109
"expires": "2023-05-02T10:44:42.754994Z",
111110
}
@@ -155,7 +154,7 @@ async def view(request, authenticated_sender):
155154
)
156155
)
157156
}
158-
payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/"}
157+
payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/", "domain": "localhost"}
159158
headers["X-SignedOperation"] = json.dumps(
160159
{
161160
"payload": bytes.hex(json.dumps(payload).encode("utf-8")),
@@ -189,7 +188,6 @@ async def view(request, authenticated_sender):
189188
pubkey = {
190189
"pubkey": json.loads(key.export_public()),
191190
"alg": "ECDSA",
192-
"domain": "localhost",
193191
"address": signer_account.address,
194192
"expires": "2023-05-02T10:44:42.754994Z",
195193
}
@@ -198,26 +196,22 @@ async def view(request, authenticated_sender):
198196
signed_message: SignedMessage = signer_account.sign_message(signable_message)
199197
pubkey_signature = to_0x_hex(signed_message.signature)
200198

201-
# Modify the payload to render the signature invalid
202-
pubkey["domain"] = "baddomain"
203-
invalid_pubkey_payload = json.dumps(pubkey).encode("utf-8").hex()
204-
205199
app.router.add_get("", view)
206200
client = await aiohttp_client(app)
207201
headers = {
208202
"X-SignedPubKey": (
209203
json.dumps(
210204
{
211-
"payload": invalid_pubkey_payload,
205+
"payload": pubkey_payload,
212206
"signature": pubkey_signature,
213207
}
214208
)
215209
)
216210
}
217-
payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/"}
211+
invalid_operation_payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/", "domain": "baddomain"}
218212
headers["X-SignedOperation"] = json.dumps(
219213
{
220-
"payload": bytes.hex(json.dumps(payload).encode("utf-8")),
214+
"payload": bytes.hex(json.dumps(invalid_operation_payload).encode("utf-8")),
221215
"signature": "96ffdbbd1704d5f6bfe4698235a0de0d2f58668deaa4371422bee26664f313f51fd483c78c34c6b317fc209779f9ddd9c45accf558e3bf881b49ad970ebf0ade",
222216
}
223217
)
@@ -226,7 +220,7 @@ async def view(request, authenticated_sender):
226220
assert resp.status == 401, await resp.text()
227221

228222
r = await resp.json()
229-
assert {"error": "Invalid signature"} == r
223+
assert {"error": "Invalid domain"} == r
230224

231225

232226
@pytest.mark.asyncio
@@ -269,7 +263,6 @@ async def test_require_jwk_authentication_good_key(aiohttp_client, patch_datetim
269263
pubkey = {
270264
"pubkey": json.loads(key.export_public()),
271265
"alg": "ECDSA",
272-
"domain": "localhost",
273266
"address": signer_account.address,
274267
"expires": (patch_datetime_now.FAKE_TIME + datetime.timedelta(days=1)).isoformat() + "Z",
275268
}
@@ -292,7 +285,7 @@ async def view(request, authenticated_sender):
292285
app.router.add_get("", view)
293286
client = await aiohttp_client(app)
294287

295-
payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/"}
288+
payload = {"time": "2010-12-25T17:05:55Z", "method": "GET", "path": "/", "domain": "localhost"}
296289

297290
payload_as_bytes = json.dumps(payload).encode("utf-8")
298291
headers = {"X-SignedPubKey": pubkey_signature_header}

0 commit comments

Comments
 (0)