Skip to content

Commit 6d624b9

Browse files
committed
Valid
1 parent 20d28bc commit 6d624b9

4 files changed

Lines changed: 88 additions & 7 deletions

File tree

app/app.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import hmac
66
import hashlib
77
import gzip
8+
from urllib.parse import urlencode, urlsplit, urlunsplit, parse_qsl
89

910
# Optionally load environment variables from a .env file
1011
try:
@@ -187,9 +188,30 @@ def _build_verify_url(credential_id):
187188
"""Build the canonical verify URL used by all QR generation paths."""
188189
qr_data = _generate_qr_hidden_payload(credential_id)
189190
qr_token = _generate_qr_secret_token(credential_id, _hash_qr_hidden_payload(qr_data))
190-
verify_url = url_for('public_verify', _external=True) + f'?id={credential_id}&qk={qr_token}'
191+
verifier_base_url = (
192+
os.environ.get("QR_VERIFIER_BASE_URL")
193+
or "https://udaycodespace.github.io/credify-verify/"
194+
).strip()
195+
196+
if not verifier_base_url:
197+
verifier_base_url = url_for('public_verify', _external=True)
198+
199+
parsed = urlsplit(verifier_base_url)
200+
existing_query = dict(parse_qsl(parsed.query, keep_blank_values=True))
201+
existing_query.update({
202+
"id": credential_id,
203+
"qk": qr_token,
204+
})
191205
if qr_data:
192-
verify_url += f'&qd={qr_data}'
206+
existing_query["qd"] = qr_data
207+
208+
verify_url = urlunsplit((
209+
parsed.scheme,
210+
parsed.netloc,
211+
parsed.path or "/",
212+
urlencode(existing_query),
213+
parsed.fragment,
214+
))
193215
return {
194216
'verify_url': verify_url,
195217
'qr_token': qr_token,
@@ -1437,7 +1459,7 @@ def api_credential_pdf(credential_id):
14371459
qr.save(qr_buffer, format='PNG')
14381460
qr_buffer.seek(0)
14391461
from reportlab.lib.utils import ImageReader
1440-
p.drawImage(ImageReader(qr_buffer), width-145, y_box-82, width=65, height=65)
1462+
p.drawImage(ImageReader(qr_buffer), width-176, y_box-86, width=96, height=96)
14411463

14421464
# Mini Badge (15% Smaller)
14431465
p.setFillColor(gold)

qr-web-app/app.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,48 @@ async function processPayload(payload, sourceTag) {
302302
}
303303
}
304304

305+
// Keep repeat scans valid on the same device; local storage is only a warning, not proof of fraud.
306+
processPayload = async function processPayloadOverride(payload, sourceTag) {
307+
const parsed = parseQrPayload(payload);
308+
if (!parsed.ok) {
309+
setStatus('fake', 'INVALID QR');
310+
renderCardHtml(`<div class="fail">Credential Invalid ❌</div><div>${escapeHtml(parsed.reason)}</div>`);
311+
return;
312+
}
313+
314+
try {
315+
const alreadyConsumed = await isQrAlreadyConsumed(parsed.qk);
316+
const decodedQd = await decodeQdPayload(parsed.qd);
317+
const decoded = decodedQd.parsed;
318+
const offlineCheck = await verifyJwsToken(parsed.qk, decodedQd.payloadText, parsed.credentialId);
319+
320+
if (offlineCheck && offlineCheck.ok && !alreadyConsumed) {
321+
await markQrConsumed(parsed.qk, parsed.credentialId);
322+
}
323+
324+
const offlineReason = String(offlineCheck?.reason || '');
325+
const repeatedScanReason = alreadyConsumed
326+
? `${offlineReason}${offlineReason ? '. ' : ''}This QR was already scanned on this device earlier.`
327+
: offlineReason;
328+
329+
renderDecodedSecret(decoded, sourceTag, {
330+
...offlineCheck,
331+
reason: repeatedScanReason
332+
});
333+
} catch (e) {
334+
setStatus('fake', 'INVALID');
335+
renderCardHtml(`<div class="fail">Credential Invalid ❌</div><div>${escapeHtml(String(e))}</div>`);
336+
}
337+
}
338+
339+
function getLandingPayloadFromPageUrl() {
340+
const params = new URLSearchParams(window.location.search);
341+
if (!params.get('id') || !params.get('qk') || !params.get('qd')) {
342+
return null;
343+
}
344+
return window.location.href;
345+
}
346+
305347
async function startCameraScan() {
306348
if (activeVideoTrack) return;
307349

@@ -402,3 +444,20 @@ qrFile.addEventListener('change', async (e) => {
402444
}
403445
});
404446
})();
447+
448+
(async function autoVerifyLandingUrl() {
449+
const landingPayload = getLandingPayloadFromPageUrl();
450+
if (!landingPayload) {
451+
return;
452+
}
453+
454+
const ok = await loadIssuerRegistry();
455+
if (!ok) {
456+
return;
457+
}
458+
459+
qrLinkInput.value = landingPayload;
460+
setStatus('pending', 'VERIFYING');
461+
renderCardHtml('<div>QR payload detected in page URL. Verifying now...</div>');
462+
processPayload(landingPayload, 'landing-url');
463+
})();

qr-web-app/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<header class="hero">
1515
<h1>Credify Verify</h1>
1616
<p>Independent Credential Verifier</p>
17-
<small>Scan QR or upload QR image</small>
17+
<small>Open from a certificate QR, scan live, or upload a QR image</small>
1818
</header>
1919

2020
<section class="panel">
@@ -36,7 +36,7 @@ <h2>Upload QR Image</h2>
3636
<section class="panel">
3737
<h2>QR Link Verification</h2>
3838
<div class="row">
39-
<input id="qrLinkInput" type="text" placeholder="Paste verify link (credify://... or https://...)">
39+
<input id="qrLinkInput" type="text" placeholder="Paste a verification URL if you are not scanning directly">
4040
<button id="verifyLinkBtn" type="button">Verify Link</button>
4141
</div>
4242
</section>

templates/certificate_view.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ <h3 class="section-title" style="color: #b91c1c;">Outstanding Backlog Subjects</
558558
document.getElementById('certVerifyUrl').value = certVerifyURL;
559559

560560
QRCode.toCanvas(document.createElement('canvas'), certVerifyURL, {
561-
width: 140,
561+
width: 180,
562562
margin: 2
563563
}, function (error, canvas) {
564564
if (error) console.error(error);
@@ -600,4 +600,4 @@ <h3 class="section-title" style="color: #b91c1c;">Outstanding Backlog Subjects</
600600
document.body.removeChild(a);
601601
}
602602
</script>
603-
{% endblock %}
603+
{% endblock %}

0 commit comments

Comments
 (0)