Spark Program | [: “JoyID Connect” — Portable Session Auth Across CKB dApps]
1. Project Overview
- Project name: [ “JoyID Connect”]
- One-sentence summary: A lightweight SSO layer that lets a single JoyID passkey authenticate a user across multiple independent CKB dApps, with per-app scoped permissions instead of full wallet access.
- Category: Developer tool / SDK (with a demo dApp trio to prove it out)
2. Team Profile
Core member: Carl — Solo developer, project lead and sole contributor. No collaborators on this application.
Contact:oxdev6 (Oxdev) · GitHub
-
Discord: carlcn6
-
Email: [email protected]
-
Telegram: carl6
-
GitHub: @oxdev6
-
Repo: GitHub - oxdev6/JoyID-Connect · GitHub (public, MIT-licensed, monorepo already scaffolded).
3. Project Background
JoyID already solved passwordless auth on CKB — native WebAuthn/passkey accounts, no seed phrase. But today every dApp that integrates JoyID treats it as its own login: each app runs its own auth flow against the same passkey, and there’s no shared notion of “this is the same identity, already trusted, across apps.”
This mirrors the pre-SSO era of the web: every site making you log in separately even when you’re using “the same” credential underneath. On CKB specifically, this also means every dApp has to reinvent not just login, but authorization scope — there’s no standard way for a dApp to request “read-only” vs “can sign up to X CKB” from a JoyID identity.
As more CKB dApps adopt JoyID, this fragmentation compounds: each integration is bespoke, and users get no continuity of identity or trust between apps they’ve already authenticated with.
4. Solution
A small SDK + reference implementation that lets a dApp:
- Detect if the user already has an active JoyID session (established with another participating dApp)
- Request a scoped session grant from the user — e.g., “read-only,” or “can sign transactions up to N CKB” — co-signed by the user’s own passkey, without forcing a full new passkey registration
- Verify that grant against the user’s public key already registered with JoyID — not against the requesting dApp — so no individual dApp is ever a trusted issuer for anyone else
User’s view: authenticate once with your JoyID passkey; when you visit a second participating dApp, you’re recognized and asked to approve a scoped permission with one lightweight signature — not a full WebAuthn re-registration, but also not a rubber-stamp “click to trust another app’s word for it.”
Differentiation from plain “use JoyID as your login everywhere”: the scoping mechanism, and — after a security review of the original design (see Section 5) — who is allowed to vouch for the user. This isn’t just federated login (OAuth-style “who are you”), it’s federated authorization (“what can you do here”), and critically, no dApp is ever the identity issuer — only the user’s own passkey and JoyID’s registered public key are.
5. Technical Approach
- Stack: TypeScript SDK, React/Vite demo frontends, CKB SDK / Lumos for cell construction (future work), JoyID SDK for passkey/WebAuthn integration
- Repo structure (already scaffolded — see diagram below): a monorepo with
packages/sdk(session grant construction/verification, scope enforcement, expiration, public-key signature checks) andpackages/demo-suite(three functional mock dApps — DEX, marketplace, lending — currently running against aMockJoyIDAdapterthat simulates passkey-backed identity locally, no testnet dependency required to develop against). - Architecture, revised after security review:
- JoyID account = lock script backed by the passkey’s public key (existing JoyID behavior, unchanged)
- A session grant (off-chain) is a small payload — identity reference, requesting dApp (audience), permission scope, expiry — co-signed by the user’s own passkey, not issued or signed by the requesting dApp
- Each dApp verifies a grant directly against the user’s public key registered with JoyID, and checks that the grant’s audience field matches its own origin, so a grant issued for dApp B cannot be replayed at dApp C
- No dApp ever acts as an issuer or identity provider for any other dApp — every dApp is a verifier only
Why this changed from the original design: the first version had the requesting dApp’s server sign session tokens that other dApps would trust — a federation model where compromising one dApp’s backend could forge sessions for every dApp that trusted it. A CKB community reviewer pushed on this directly during the proposal discussion (see forum thread), and the honest answer was that this introduced a new cross-dApp blast radius that doesn’t exist in today’s per-dApp WebAuthn model. Moving the signature to the user’s own passkey removes dApps as trust roots entirely: compromising any single dApp’s backend now leaks nothing that lets it impersonate the user anywhere else, which is no worse than today’s baseline.
Explicit threat model:
- If a dApp’s server-side key leaks: under the revised design, an attacker gains nothing that lets them forge sessions elsewhere — the dApp was never a signer, only a verifier. This is the property the redesign was for.
- If the user’s passkey leaks: an attacker can authenticate as the user everywhere JoyID is used, which is already true today under plain JoyID with no SSO layer — a single shared identity is inherently cross-dApp once compromised. This project does not change that baseline.
- Additional mitigations: per-dApp audience binding (a grant for dApp B is rejected by dApp C), short expiry on grants, and mandatory fresh passkey cosigning for any scope escalation rather than accepting an existing lower-scope grant as a stepping stone to a higher one.
- Key technical decision, already made: the SDK uses an off-chain, user-cosigned grant model rather than a fully on-chain session registry cell or a dApp-issued-token model. This was the right call for a 1-2 month grant — it’s demonstrable, avoids introducing a new trust root, and is honest about what it is now, with the on-chain registry documented as future work rather than overpromised here.
- What’s left before this is “real”: the current demo suite proves the logic works end-to-end, but against a simulated identity and the pre-review token design. The core risk this grant retires is two-fold: (1) integration risk — does the flow hold up once
MockJoyIDAdapteris replaced with actual WebAuthn calls against JoyID’s testnet, and (2) the redesign — implementing user-cosigned, audience-bound grants in place of dApp-issued tokens. - Language: English (repo/docs), Chinese not required per current forum policy.
6. To-Do List (6-week plan)
Starting point: SDK skeleton, an initial (since-revised) session token schema, and all three demo dApps (DEX, marketplace, lending) already exist and run locally against a MockJoyIDAdapter. The plan below covers two things: taking this from “logic proven against a mock” to “working against real JoyID passkeys on testnet,” and implementing the redesigned, user-cosigned grant model that came out of community security review.
- Week 1 (done — pre-grant): SDK skeleton, initial session token schema, three demo dApps scaffolded and functional against
MockJoyIDAdapter. - Week 2: Redesign session grants to be user-passkey-cosigned and audience-bound (dApp-specific), replacing the original dApp-issued-token model; update SDK’s create/verify logic accordingly.
- Week 3: Replace
MockJoyIDAdapterwith real JoyID SDK integration — actual WebAuthn passkey registration and login against JoyID testnet, wired to the redesigned grant model; finish and expand the SDK unit test suite, including tests asserting a grant issued for one dApp is rejected by another. Milestone 1. - Week 4: Deploy dApp A and dApp B to testnet; confirm cross-app session handoff — login on A, recognized grant on B (scoped prompt, one cosigned approval, no re-registration) — works against real infrastructure with the audience-bound grant model. Milestone 2.
- Week 5: Deploy dApp C to testnet requiring a higher scope; confirm this forces a fresh cosigned grant rather than silently accepting the existing lower-scope one (step-up enforcement).
- Week 6: Docs (including a short write-up of the threat model and why the design moved away from dApp-issued tokens), demo video, publish SDK to npm, write-up of on-chain-registry future path. Milestone 3.
7. Required Funding & Breakdown
-
Requested total: $1,000 USD (in CKB, or CKB + USDI ≤ 50%)
-
Structure: 20% upfront on approval, remainder released against three verifiable milestones (below), consistent with the program’s standard payment model.
-
Upfront — $200 (20%): released on grant approval.
-
Milestone 1 — Redesigned grant model + real JoyID integration — $250: due end of Week 3. Trigger: session grants are user-passkey-cosigned and audience-bound (not dApp-issued);
MockJoyIDAdapterfully replaced with the real JoyID SDK; passkey registration/login working against JoyID testnet. Verification: committee can clone the repo and runnpm testto see passing tests, including a test asserting a grant issued for one dApp is rejected when presented to another. -
Milestone 2 — Cross-app SSO live on testnet — $350: due end of Week 4. Trigger: dApp A and dApp B both deployed on testnet (not just local); login on A, recognized grant on B via one cosigned approval, no re-registration. Verification: live testnet URLs — the committee repeats the flow themselves, no code read required.
-
Milestone 3 — Full demo + docs — $200: due end of Week 6. Trigger: dApp C deployed requiring step-up (a fresh cosigned grant for higher scope), SDK published to npm, demo video published, integration docs including the threat-model write-up complete. Verification: demo video plus the three live URLs plus a README walkthrough.
-
Cost basis: roughly $170/week across a 6-week, part-time solo build — front-loaded slightly toward Milestone 2 since the cross-app session flow (Weeks 3-4) is the technical core the rest depends on.
-
Fallback: if Milestone 2 slips past Week 4, I’ll flag it proactively in the weekly check-in rather than let it surface at final review — the $350 tranche stays gated on the working demo, not the calendar date.
8. Deliverables + How to Verify
Deliverables:
- Open-source SDK repo (TypeScript), MIT-licensed, published to npm, implementing user-cosigned, audience-bound session grants
- Three demo dApps (DEX, marketplace, lending mock) deployed on testnet, running against real JoyID passkeys (not the current
MockJoyIDAdapter) - Short demo video showing: login on dApp A → grant recognized on dApp B via one cosigned approval (not re-registration) → step-up to a higher scope required and separately approved on dApp C
- README + integration docs, including a short threat-model write-up, for other CKB devs to plug the SDK into their own dApp
How to verify (no code review required):
- Watch the demo video: one passkey login, one cosigned approval recognized on a second app, a fresh cosigned approval required for the higher-scope third app
- Visit the three live testnet demo URLs directly and repeat the flow themselves
- Check the repo’s commit history against the weekly milestones above
- Run the included test suite (
npm test), including the cross-audience rejection test — pass/fail output is self-evident
9. Current State vs. Funded Work
- Current state: Public repo already live at github.com/oxdev6/JoyID-Connect (MIT-licensed monorepo).
packages/sdkhas an initial session token model implemented (create, verify, scope enforcement, expiration, signature checks).packages/demo-suitehas all three demo dApps (DEX, marketplace, lending) running locally and passing the cross-app session flow — but against aMockJoyIDAdapterrather than real JoyID/WebAuthn, and against the original token design. - A note on that original design: during proposal review, a community member correctly identified that the original token model — where a requesting dApp’s own server signed and vouched for session tokens other dApps would trust — introduced a new cross-dApp blast radius (compromising one dApp’s signing key could forge sessions for every dApp that trusted it) that doesn’t exist in today’s per-dApp WebAuthn model. The design has been revised to user-passkey-cosigned, audience-bound grants, where no dApp ever acts as an issuer. See Section 5 for the full threat-model discussion.
- What this means: the general shape of the SSO/scoped-session flow is already proven end-to-end against a mock. What’s unproven, and what this grant funds, is (1) whether it holds up against real infrastructure — real WebAuthn ceremonies, real signature verification, real network latency — and (2) implementing the revised, security-reviewed grant model in place of the original one.
- Funded scope: implementing user-cosigned, audience-bound session grants; replacing the mock adapter with real JoyID SDK integration; deploying all three demo dApps to testnet; hardening the SDK test suite (including cross-audience rejection tests); publishing the SDK to npm; and producing docs (including a threat-model write-up) + a demo video. See Section 6 for the week-by-week breakdown.
- Explicitly out of scope: an on-chain session registry cell (vs. the off-chain, user-cosigned grant model already built) is documented as future work, not part of this grant.
10. CKB Alignment
- Directly builds on JoyID, a CKB-native primitive, and extends it rather than working around it
- Uses CKB’s cell model conceptually for session state (even in the off-chain-first version, the design anticipates a future on-chain session cell)
- Addresses a real ecosystem gap: every JoyID-integrated dApp today re-implements auth independently; this reduces integration friction for future CKB developers, which is a multiplier for ecosystem growth rather than a single-app feature