Skip to content

Commit a5a6b6d

Browse files
committed
feat: Move to an Authorization Code Flow issuer
This allows us to move away from the "poc9" issuer. And to test with an Authorization Code Flow. Most wallets, by now *only* support this, so having that makes sense. We'll move back to an impierce ssi-agent issuer once we have stable one running on a public SDP tier (playground?) and once that supports the authorization code flow fully.
1 parent d536db3 commit a5a6b6d

3 files changed

Lines changed: 45 additions & 59 deletions

File tree

_config.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ github_username: educredentials
99
fenced_code_blocks: true
1010
show_dir_listing: false
1111

12+
# This "secret" is used in the frontend to create offers for credentials,
13+
# so it is published in a public site anyway. Fetching it from an ENV
14+
# var is very difficult with jekyll/github-pages, so we just hardcode it.
15+
agent_create_offer_bearer_token: "lrpAVYvgQyRUl2qlyKZVrcKyOw5lb7mP"
16+
agent_create_offer_url: "https://demo.dev.edubadges.nl/offer"
17+
1218
defaults:
1319
- scope:
1420
path: "examples.md"

_layouts/credential.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
1313

1414
<!-- Offers functionality -->
15+
<script type="text/javascript">
16+
const AGENT_CREATE_OFFER_BEARER_TOKEN = "{{ site.agent_create_offer_bearer_token }}";
17+
const AGENT_CREATE_OFFER_URL = "{{ site.agent_create_offer_url }}";
18+
</script>
1519
<script src="{{ '/assets/offers.js' | relative_url }}"></script>
1620

1721

assets/offers.js

Lines changed: 35 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,6 @@
22
* Offers module for credential offer generation and display
33
*/
44

5-
// API endpoint constants
6-
const AGENT_CREATE_CREDENTIALS_URL =
7-
"https://agent.poc9.eduwallet.nl/v0/credentials";
8-
const AGENT_CREATE_OFFER_URL = "https://agent.poc9.eduwallet.nl/v0/offers";
9-
10-
/**
11-
* Generate SHA-256 hash of a JSON object
12-
* @param {Object} obj - The object to hash
13-
* @returns {Promise<string>} The hash as a hex string
14-
*/
15-
async function generateHash(obj) {
16-
const jsonString = JSON.stringify(obj);
17-
const msgBuffer = new TextEncoder().encode(jsonString);
18-
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
19-
const hashArray = Array.from(new Uint8Array(hashBuffer));
20-
const hashHex = hashArray
21-
.map((b) => b.toString(16).padStart(2, "0"))
22-
.join("");
23-
return hashHex;
24-
}
25-
265
/**
276
* Request an offer for a credential
287
* @param {Object} credentialJson - The full JSON of the credential
@@ -31,56 +10,53 @@ async function generateHash(obj) {
3110
async function requestOffer(credentialJson) {
3211
console.log("Creating an offer for credential:", credentialJson);
3312

34-
// Generate hash for offerId (same as sha256sum in the shell script)
35-
const hash = await generateHash(credentialJson);
13+
// {
14+
// offerId: hash,
15+
// credentialConfigurationId: "openbadge_credential",
16+
// expiresAt: "never",
17+
// isSigned: false,
18+
// credential: credentialJson,
19+
// };
3620

3721
// Step 1: Create the credential on the agent
3822
const credentialPayload = {
39-
offerId: hash,
40-
credentialConfigurationId: "openbadge_credential",
41-
expiresAt: "never",
42-
isSigned: false,
43-
credential: credentialJson,
44-
};
45-
46-
const createCredentialResponse = await fetch(AGENT_CREATE_CREDENTIALS_URL, {
47-
method: "POST",
48-
headers: {
49-
"Content-Type": "application/json",
23+
credentials: [],
24+
grants: {
25+
authorization_code: {
26+
issuer_state: "generate",
27+
},
28+
// "urn:ietf:params:oauth:grant-type:pre-authorized_code": {
29+
// "pre-authorized_code": "string",
30+
// "tx_code": boolean|object, optional,
31+
// }
5032
},
51-
body: JSON.stringify(credentialPayload),
52-
});
53-
54-
if (!createCredentialResponse.ok) {
55-
throw new Error(
56-
`Failed to create credential: ${createCredentialResponse.status} ${createCredentialResponse.statusText}`,
57-
);
58-
}
59-
60-
// Step 2: Create the offer on the agent
61-
const offerPayload = {
62-
offerId: hash,
63-
preAuthorizedCode: hash,
33+
credentialDataSupplierInput: {},
34+
credentialMetadata: {
35+
enableStatusLists: false,
36+
// TODO add expires from the credentialJson.validUntil
37+
},
38+
credential: credentialJson,
6439
};
6540

6641
const createOfferResponse = await fetch(AGENT_CREATE_OFFER_URL, {
6742
method: "POST",
6843
headers: {
6944
"Content-Type": "application/json",
45+
Authorization: `Bearer ${AGENT_CREATE_OFFER_BEARER_TOKEN}`,
7046
},
71-
body: JSON.stringify(offerPayload),
47+
body: JSON.stringify({ badge: credentialJson }),
7248
});
7349

7450
if (!createOfferResponse.ok) {
7551
throw new Error(
76-
`Failed to create offer: ${createOfferResponse.status} ${createOfferResponse.statusText}`,
52+
`Failed to create credential: ${createOfferResponse.status} ${createOfferResponse.statusText}`,
7753
);
7854
}
7955

8056
// The response is the offer URI
81-
const offerUri = await createOfferResponse.text();
82-
83-
return offerUri;
57+
const offer = await createOfferResponse.json();
58+
console.log(offer);
59+
return offer.url;
8460
}
8561

8662
/**
@@ -173,9 +149,9 @@ function displayOffer(offerString, container) {
173149
correctLevel: QRCode.CorrectLevel.H,
174150
});
175151
} else {
176-
const errorPara = document.createElement('p');
177-
errorPara.className = 'offer-error';
178-
errorPara.textContent = 'QR code library not loaded';
152+
const errorPara = document.createElement("p");
153+
errorPara.className = "offer-error";
154+
errorPara.textContent = "QR code library not loaded";
179155
qrContainer.appendChild(errorPara);
180156
console.error(
181157
"QRCode library not found. Make sure to include it from CDN.",
@@ -215,10 +191,10 @@ function initializeOfferButton(credentialJson) {
215191
displayOffer(offerString, offerContainer);
216192
} catch (err) {
217193
console.error("Failed to request offer", err);
218-
offerContainer.innerHTML = '';
219-
const errorPara = document.createElement('p');
220-
errorPara.className = 'offer-error';
221-
errorPara.textContent = 'Failed to request offer. Please try again.';
194+
offerContainer.innerHTML = "";
195+
const errorPara = document.createElement("p");
196+
errorPara.className = "offer-error";
197+
errorPara.textContent = "Failed to request offer. Please try again.";
222198
offerContainer.appendChild(errorPara);
223199
} finally {
224200
// Re-enable button

0 commit comments

Comments
 (0)