# Consent Page Profile Card Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Show the authorizing user's Bluesky profile (avatar, name, handle) on the OAuth consent page. **Architecture:** Add inline HTML/CSS/JS to the consent page. Profile is fetched client-side from Bluesky's public API using the `login_hint` parameter. Graceful degradation if fetch fails. **Tech Stack:** Vanilla JS, Bluesky public API (`app.bsky.actor.getProfile`) --- ### Task 1: Update renderConsentPage signature **Files:** - Modify: `src/pds.js:5008-5017` (function signature and JSDoc) **Step 1: Add loginHint to JSDoc and parameters** Change the function signature from: ```javascript /** * @param {{ clientName: string, clientId: string, scope: string, requestUri: string, error?: string }} params * @returns {string} HTML page content */ function renderConsentPage({ clientName, clientId, scope, requestUri, error = '', }) { ``` To: ```javascript /** * @param {{ clientName: string, clientId: string, scope: string, requestUri: string, loginHint?: string, error?: string }} params * @returns {string} HTML page content */ function renderConsentPage({ clientName, clientId, scope, requestUri, loginHint = '', error = '', }) { ``` **Step 2: Verify syntax is correct** Run: `node --check src/pds.js` Expected: No output (success) --- ### Task 2: Add profile card CSS **Files:** - Modify: `src/pds.js:5027-5055` (inside the ``: ```css .profile-card{display:flex;align-items:center;gap:12px;padding:16px;background:#2a2a2a;border-radius:8px;margin-bottom:20px} .profile-card.loading .avatar{background:#404040;animation:pulse 1.5s infinite} .profile-card .avatar{width:48px;height:48px;border-radius:50%;background:#404040;flex-shrink:0} .profile-card .avatar img{width:100%;height:100%;border-radius:50%;object-fit:cover} .profile-card .info{min-width:0} .profile-card .name{color:#fff;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} .profile-card .handle{color:#808080;font-size:14px} @keyframes pulse{0%,100%{opacity:1}50%{opacity:0.5}} ``` **Step 2: Verify syntax is correct** Run: `node --check src/pds.js` Expected: No output (success) --- ### Task 3: Add profile card HTML **Files:** - Modify: `src/pds.js:5056-5057` (after `` opening, before `

`) **Step 1: Add profile card HTML conditionally** Replace: ```javascript

Sign in to authorize

``` With: ```javascript ${loginHint ? `
Loading...
${escapeHtml(loginHint.startsWith('did:') ? loginHint : '@' + loginHint)}
` : ''}

Sign in to authorize

``` **Step 2: Verify syntax is correct** Run: `node --check src/pds.js` Expected: No output (success) --- ### Task 4: Add profile fetch script **Files:** - Modify: `src/pds.js:5066` (before ``) **Step 1: Add inline script to fetch profile** Replace: ```javascript `; ``` With: ```javascript ${loginHint ? `` : ''} `; ``` **Step 2: Verify syntax is correct** Run: `node --check src/pds.js` Expected: No output (success) --- ### Task 5: Pass loginHint from PAR flow **Files:** - Modify: `src/pds.js:3954-3959` (PAR flow renderConsentPage call) **Step 1: Add loginHint to renderConsentPage call** Change: ```javascript return new Response( renderConsentPage({ clientName: clientMetadata.client_name || clientId, clientId: clientId || '', scope: parameters.scope || 'atproto', requestUri: requestUri || '', }), ``` To: ```javascript return new Response( renderConsentPage({ clientName: clientMetadata.client_name || clientId, clientId: clientId || '', scope: parameters.scope || 'atproto', requestUri: requestUri || '', loginHint: parameters.login_hint || '', }), ``` **Step 2: Verify syntax is correct** Run: `node --check src/pds.js` Expected: No output (success) --- ### Task 6: Pass loginHint from direct flow **Files:** - Modify: `src/pds.js:4022-4027` (direct flow renderConsentPage call) **Step 1: Add loginHint to renderConsentPage call** Change: ```javascript return new Response( renderConsentPage({ clientName: clientMetadata.client_name || clientId, clientId: clientId, scope: scope || 'atproto', requestUri: newRequestUri, }), ``` To: ```javascript return new Response( renderConsentPage({ clientName: clientMetadata.client_name || clientId, clientId: clientId, scope: scope || 'atproto', requestUri: newRequestUri, loginHint: loginHint || '', }), ``` **Step 2: Verify syntax is correct** Run: `node --check src/pds.js` Expected: No output (success) --- ### Task 7: Run tests and commit **Step 1: Run full test suite** Run: `npm test` Expected: All 126 tests pass **Step 2: Commit changes** ```bash git add src/pds.js docs/plans/2025-01-09-consent-profile-card.md git commit -m "feat: add profile card to OAuth consent page Shows the authorizing user's avatar, display name, and handle on the consent page. Fetches from Bluesky public API using the login_hint parameter. Degrades gracefully if fetch fails." ``` --- ## Manual Testing After implementation, test by: 1. Start local PDS: `npx wrangler dev` 2. Trigger OAuth flow with login_hint parameter 3. Verify profile card shows on consent page 4. Verify it degrades gracefully with invalid login_hint