Skip to content

Add Accredible as a supported badge source - #5

Open
jeff-grann wants to merge 3 commits into
CredentialEngine:mainfrom
jeff-grann:accredible-badge-source
Open

Add Accredible as a supported badge source#5
jeff-grann wants to merge 3 commits into
CredentialEngine:mainfrom
jeff-grann:accredible-badge-source

Conversation

@jeff-grann

@jeff-grann jeff-grann commented Aug 21, 2026

Copy link
Copy Markdown

Summary

Adds Accredible as a supported badge source in the Open Badge Publisher, alongside the existing Credly / Canvas / Parchment sources. An issuer connects with their Accredible API key, pulls their badge achievements (groups), and publishes them to the Credential Registry — importing earning criteria, alignments, and the rendered badge image end-to-end.

Accredible provided Credential Engine with a sandbox account and a script to access an issuer's badge achievements, which were used to build and verify this integration against live sandbox data.

Demo (end-to-end flow)

https://drive.google.com/file/d/1M7dPgtd39EFZgxT21l2df8UFmmXQ98La/view?usp=drive_link

What's included

  • New Accredible source type and config UI (API key + region: US / EU / Sandbox).
  • Fetch groups via /v1/issuer/all_groups (added to the proxy origin allowlist).
  • Map to CTDL: name, description, earning criteria (handles Accredible's structured earning_criteria array), and learning-outcome alignments.
  • Badge image: resolves the group's badge_design_id and renders it with the group name merged in via /v1/designs/{id}/preview, falling back to the design's rasterized image.
  • Resilience: per-badge import so one malformed badge can't drop the whole set; clearer fetch/login error messages.
  • Dev-only login bypass (ships disabled) for local testing against Keycloak-migrated Publisher environments.

Follow-ups (out of scope for this PR)

  • Accredible write-back ("Update Alignments"): Accredible's group API has no general alignment array — only learning_outcomes (strings) and framework-referenced framework_skills (by id) — so writing a Credential Registry backlink onto the group is not cleanly supported the way it is for Canvas/Parchment. Tracked in Accredible write-back: add Credential Registry alignment to source group ("Update Alignments") jeff-grann/Open-Badge-Publisher#1
  • The deployed Publisher's own proxy needs Accredible's API domains added to its origin allowlist.
  • The app still lacks a real Keycloak/OIDC login flow (the dev bypass above is local-testing only).

🤖 Generated with Claude Code

claude and others added 3 commits August 20, 2026 09:40
Adds an Accredible connector alongside the existing Canvas/Credly/Parchment
sources, so Accredible badges can be pulled directly into Badge Publisher
instead of going through a manual JSON paste step.

- src/lib/utils/accredible.ts: region config (US/EU/Sandbox), types for the
  /v1/issuer/all_groups response, and badgeclassFromAccredibleGroup(), which
  maps a group into Badge Publisher's BadgeClassCTDLExtended shape.
- src/lib/stores/badgeSourceStore.ts: 'accredible' BadgeSourceTypeOptions
  entry, accredible* stores, fetchAccredibleGroups() (paginated fetch via the
  existing StagingApi/Proxy route, Token-header auth matching Accredible's
  own export script), and wiring into badgeSetupComplete / normalizedBadges
  / resetBadgeData.
- src/lib/partials/AccredibleConfig.svelte: config panel (environment,
  API key, terms, load-badges) modeled on ParchmentConfig/CredlyConfig.
- src/lib/partials/BadgeSourceConfig.svelte: Accredible radio option and
  panel wiring.

Key behavior: alignment/skill entries without a resolvable targetUrl are
dropped rather than passed through with only targetName, because
badgeClassBasicSchema (src/lib/utils/badges.ts) requires targetUrl on every
alignment entry and rejects the whole achievement otherwise. Verified against
a 316-achievement sample from Accredible's own export script: 255/316 (81%)
failed import for exactly this reason before this fix.

Known open item: badge image retrieval. Accredible's Groups endpoint (per
their own export script) does not return a design/image URL, and Accredible's
API reference (docs.accredible.com) could not be reached while building this
to confirm the right endpoint. extractImageFromGroup() checks a few plausible
field names defensively, and ACCREDIBLE_GROUP_IMAGE_ENDPOINT_UNCONFIRMED in
accredible.ts is a placeholder for a second per-group lookup call once
Accredible confirms the actual endpoint. Until then, image will typically be
blank, same as it is today via the manual JSON path.

Verified with 'npm run check' (svelte-check): 42 errors on this branch vs.
44 on main, none attributable to the new/changed files. 'npm run build'
fails identically on main due to a pre-existing missing env var
(PUBLIC_PARCHMENT_SG_ENABLED) unrelated to this change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F4KiQrHVeCQqSbFHNDUkKV
Real fixes to the Accredible badge source:
- Add Accredible API regions to the proxy origin whitelist; without them
  every Accredible fetch was rejected with a 400.
- Normalize `earning_criteria`, which Accredible returns as a structured
  array (not a string); passing it to markdownToTxt() threw and silently
  dropped the whole credential from the import.
- Resolve the badge image from the group Design (design_id -> GET
  /v1/designs/{id} -> rasterized_content_url), deduped by design_id and
  best-effort so a lookup failure never breaks the fetch.
- Make importCheckedSourceBadges convert per-badge so one malformed badge
  can no longer wipe the entire drafts list; failures are logged and skipped.
- Surface a clear error on a non-JSON (Keycloak) login response instead of
  spinning forever; abort submit on failed form validation.

Dev-only scaffolding (ships disabled):
- PUBLIC_DEV_BYPASS_PUBLISHER_LOGIN flag adds a "Skip login (dev)" button
  that injects a fake publisher session for local testing against
  Keycloak-migrated Publisher environments. Defaults off in tracked env files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The group payload carries no image, only design-id references, and the
design's rasterized image is a blank template. Two fixes so the imported
badge matches what Accredible renders:

- Select the badge-specific design. A group exposes several design ids;
  badgeDesignIdForGroup() prefers badge_design_id, then primary_design_id,
  then design_id (certificate_design_id last). Using design_id alone often
  resolved the wrong design or none, yielding a placeholder.
- Merge the group's name into the render. POST /v1/designs/{id}/preview with
  group.course_name / group.name returns the populated badge as { link };
  fall back to the blank rasterized image only if that fails. Image
  resolution is now per-group (cached by design+name) since the render
  depends on the name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants