Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 70 additions & 2 deletions app/plugins/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,37 @@ async def handle_payload(self, payload):
current_app.logger.info("Out of band invitation")
invitation = payload.split('?oob=')[-1]
decoded_invitation = json.loads(base64.urlsafe_b64decode(invitation+'===').decode())
# BUG #21: connectionless present-proof request inside OOB (redirect form)
pres_request = self._get_connectionless_presentation_request(decoded_invitation)
await self.didcomm_handler(decoded_invitation)
if pres_request:
exchange_id = self._find_presentation_exchange_id(pres_request.get("@id"))
return {
"type": "oob_presentation_request",
"action": "presentation_request",
"exchange_id": exchange_id,
}
return {"type": "oob_invitation", "label": decoded_invitation.get("label", "Unknown")}

elif payload.split('?')[-1].startswith('_oobid='):
try:
r = requests.get(payload)
invitation = r.json()
# BUG #21: connectionless present-proof request inside OOB (redirect form)
pres_request = self._get_connectionless_presentation_request(invitation)
await self.didcomm_handler(invitation)
except:
current_app.logger.info(r.text)
if pres_request:
exchange_id = self._find_presentation_exchange_id(pres_request.get("@id"))
return {
"type": "oob_presentation_request",
"action": "presentation_request",
"exchange_id": exchange_id,
}
except Exception:
try:
current_app.logger.info(r.text)
except Exception:
current_app.logger.info("Failed to fetch/parse _oobid invitation")

current_app.logger.info("No matching URL scheme found")
return {"type": "unknown", "message": "No matching URL scheme found"}
Expand Down Expand Up @@ -80,3 +101,50 @@ async def iuv_handler(self, payload):
elif exchange.get("redirectUrl", None):
redirect_url = exchange.get("redirectUrl")
redirect_url


# BUG #21: connectionless present-proof request inside OOB (redirect form)
def _get_connectionless_presentation_request(self, invitation):
"""
Identify connectionless present-proof OOB invites:
- handshake_protocols is [] or missing
- requests~attach contains a present-proof/2.0 message
"""
handshake_protocols = invitation.get("handshake_protocols")
if handshake_protocols not in (None, []):
return None

for attachment in invitation.get("requests~attach", []):
message = attachment.get("data", {}).get("json", {})
msg_type = message.get("@type", "")
if msg_type.startswith("https://didcomm.org/present-proof/2.0/"):
return message

return None

# BUG #21: connectionless present-proof request inside OOB (redirect form)
def _find_presentation_exchange_id(self, thread_id):
"""
Best-effort: after ACA-Py receives the invitation, poll present-proof records
to find pres_ex_id by thread_id.
"""
if not thread_id:
return None

for _ in range(3):
try:
response = requests.get(
f"{agent.admin_endpoint}/present-proof-2.0/records",
params={"thread_id": thread_id},
headers=agent.tenant_headers,
)
data = response.json() if response is not None else {}
records = data.get("results") or data.get("pres_ex_records") or []
if records:
return records[0].get("pres_ex_id")
except Exception:
pass

time.sleep(0.2)

return None
10 changes: 10 additions & 0 deletions app/static/qr-scanner.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ function detect(source) {
if (scannerModal) {
scannerModal.hide();
}
// BUG #21: connectionless present-proof request inside OOB (redirect form)
if (data && data.result && data.result.action === "presentation_request") {
if (data.result.exchange_id) {
window.location.href = `/presentations/${data.result.exchange_id}`;
} else {
// fallback: reload so notification / inbox shows
window.location.reload();
}
return;
}

// Show connection processing modal with auto-refresh
showConnectionProcessing();
Expand Down