Skip to content

Conversation

@jonkafton
Copy link
Contributor

@jonkafton jonkafton commented Dec 19, 2025

What are the relevant tickets?

Description (What does it do?)

Adds the "Download Digital Credential" button and dialog to the certificate page.

In Figma:

MVP:

Screenshots (if appropriate):

image image

How can this be tested?

You'll need mitxonline running locally and to be set up to test certificates:

  • created a B2B organization, contract and courses.
  • created a certificate page and signatories in Wagtail.
  • course run certificate created.
  • certificate page revision set.
  • certificate associated to a course run.
  • user added to an organization.
  • user enrolled to a course run.
  • user graded on the course run.

Create a VerifiableCredential and set it on your certificate (replace <certificate_uuid>):

# docker compose exec web python manage.py shell

from courses.models import CourseRunCertificate, VerifiableCredential

# Find certificate - replace with your actual UUID
cert = CourseRunCertificate.all_objects.get(uuid='<certificate_uuid>')

credential_data = {
    "@context": [
        "https://www.w3.org/ns/credentials/v2",
        "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json",
        "https://w3id.org/security/suites/ed25519-2020/v1"
    ],
    "id": "urn:uuid:19281fe8-90d2-4eao-a9da-67b188898a6c",
    "type": [
        "VerifiableCredential",
        "OpenBadgeCredential"
    ],
    "issuer": {
        "id": "did:key:z6MkjoriXdbyWD25YXTed114F8hdJrLXQ567xxPHAUKxpKkS",
        "type": [
            "Profile"
        ],
        "name": "MIT Learn",
        "image": {
            "id": "https://github.com/digitalcredentials/test-files/assets/206059/01eca9f5-a508-40ac-9dd5-c12d11308894",
            "type": "Image",
            "caption": "MIT Learn logo"
        }
    },
    "validFrom": "2025-02-24T00:00:00Z",
    "validUntil": "2030-01-01T00:00:00Z",
    "credentialSubject": {
        "type": [
            "AchievementSubject"
        ],
        "activityStartDate": "2023-03-01T00:00:00Z",
        "activityEndDate": "2025-02-24T00:00:00Z",
        "identifier": [
            {
                "type": "IdentityObject",
                "identityHash": "Lucas Delisle-Doray",
                "identityType": "name",
                "hashed": False,
                "salt": "not-used"
            }
        ],
        "achievement": {
            "id": "https://something.org/theCourse",
            "achievementType": "Course",
            "type": [
                "Achievement"
            ],
            "image": {
                "id": "https://github.com/digitalcredentials/test-files/assets/206059/01eca9f5-a508-40ac-9dd5-c12d11308894",
                "type": "Image",
                "caption": "MIT Learn Certificate logo"
            },
            "criteria": {
                "narrative": "If you wanted to add some kind of criteria, e.g. a list of courses or modules, etc. CAN BE MARKDOWN"
            },
            "description": "Lucas Delisle-Doray has successfully completed all modules and earned a Course Certificate in Foundations of Universal AI.",
            "name": "Foundations of Universal AI"
        }
    },
    "proof": {
        "type": "DataIntegrityProof",
        "created": "2025-12-12T17:52:25Z",
        "verificationMethod": "did:key:z6MkjoriXdbyWD25YXTed114F8hdJrLXQ567xxPHAUKxpKkS#z6MkjoriXdbyWD25YXTed114F8hdJrLXQ567xxPHAUKxpKkS",
        "cryptosuite": "eddsa-rdfc-2022",
        "proofPurpose": "assertionMethod",
        "proofValue": "z5UBb8q7StJLsA839GjyMrFsB6atZRkeXx2MCmgaB6D4mDvQQujcxxzysF3F6d8MZgWQh4ftUCRbWCBWibyRMCwR8"
    }
}

vc = VerifiableCredential.objects.create(credential_data=credential_data)
cert.verifiable_credential = vc
cert.save()

print(f"Created VerifiableCredential {vc.uuid} for certificate {cert.uuid}")

Additional Context

@jonkafton
Copy link
Contributor Author

Setting to draft for now as awaiting mitodl/mitxonline#3156 to be released and the @mitodl/mitxonline-api-axios API client to be published.

@jonkafton jonkafton marked this pull request as draft December 19, 2025 19:32
@jonkafton jonkafton added the Needs Review An open Pull Request that is ready for review label Dec 19, 2025
@gumaerc gumaerc self-assigned this Dec 19, 2025
Copy link
Contributor

@gumaerc gumaerc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to build the API library locally and get this working. The dialog appeared as expected and everything seems to work okay, but I noticed some issues with the styling:

} from "@mitodl/mitxonline-api-axios/v2"
import { PartialFactory } from "ol-test-utilities"

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it odd that there isn't a standard maintained Typescript library we can import for this, but it seems like what most people do is just implement their own type like this.

"0 3px 8px 0 rgba(37, 38, 43, 0.12), 0 2px 4px 0 rgba(37, 38, 43, 0.10)",
}))

const Info = styled.dl({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

I think there's an issue with overflow with this container. I had AI generate my test data for my credentials to randomize it a bit, and it inserted some longer strings for issued to and criteria. You can see in this screenshot how they overflow outside the right bounds of the container. An overflow: "auto" here will add a scrollbar, but we might want to be more explicit about width to have the text properly wrap.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unlikely an awardee name or word in the description/criteria would be long enough to cause the overflow. I've pushed a change to force long words to break, just in case.

verifiableCredential
const { identifier, achievement } = credentialSubject
return (
<Dialog
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Verify Credential and Cancel / Download Digital Credential buttons clash here if the window is too small:

image

I know the dialog uses absolute positioning to set where the buttons are, but we should figure out a way to avoid this happening.

Copy link
Contributor Author

@jonkafton jonkafton Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I set the document body as the dialog scroll container. I actually think this is the better option for all dialogs.

With default scroll "paper" - The dialog shrinks to preserve the bottom margin. The dialog content becomes y-scrollable (or overflow must be otherwise handled). With MacOS default hidden scrollbars, it may not be obvious that there is more content:

image

With scroll: "body" - The dialog does not shrink. When content is taller than the window, the body is scrollable to reach the dialog buttons (full height window aligned scroll). This feels more natural and allows touch scroll anywhere on the page.

Screen.Recording.2025-12-22.at.15.52.34.mov

Dialog props: https://mui.com/material-ui/api/dialog/#props

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually think this is the better option for all dialogs.

After brief chat with @ChristopherChudzicki and @steven-hatch ... above not true for all dialogs. I'm of the view that we should not need to design for overflow behavior inside dialogs that are generally fixed height and not expected to overflow even small window sizes, ie. the user sizing their window very small is an edge case and we can then just defer to the body scroll. I would use dialog paper scroll where we have list content, or content that is expected to overflow the page. Chris makes the valid point that it is beneficial to see the dialog confirm and cancel buttons always.

@jonkafton jonkafton requested a review from gumaerc December 22, 2025 14:53
Copy link
Contributor

@gumaerc gumaerc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes, it looks good now.

I tried a bit to brainstorm a better UX than "download the JSON file and manually upload it to VerifierPlus." They have a JSON API, but it returns a JSON response and you can't just redirect to their results page. We would have to display the result in our own UI, but that's a discussion for another day.

P.S. Remember to update the API version here once the MITx Online release needed is complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs Review An open Pull Request that is ready for review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants