OAuth Callback Route Implementation Plan#
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Add a dedicated /oauth/callback route so users can log in from any page and return to where they were.
Architecture: Store the current path in sessionStorage before OAuth redirect, then read it back after the callback completes and navigate there.
Tech Stack: Lit web components, quickslice-client-js OAuth
Task 1: Create OAuth Callback Page Component#
Files:
- Create:
src/components/pages/grain-oauth-callback.js
Step 1: Create the callback component
import { LitElement, html, css } from 'lit';
import '../atoms/grain-spinner.js';
export class GrainOAuthCallback extends LitElement {
static styles = css`
:host {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100%;
gap: var(--space-md);
}
p {
color: var(--color-text-secondary);
font-size: var(--font-size-sm);
}
`;
render() {
return html`
<grain-spinner size="32"></grain-spinner>
<p>Signing in...</p>
`;
}
}
customElements.define('grain-oauth-callback', GrainOAuthCallback);
Step 2: Verify file exists
Run: cat src/components/pages/grain-oauth-callback.js
Expected: File contents match above
Step 3: Commit
git add src/components/pages/grain-oauth-callback.js
git commit -m "feat: add OAuth callback page component"
Task 2: Register the OAuth Callback Route#
Files:
- Modify:
src/components/pages/grain-app.js
Step 1: Add import for callback component
Add after line 17 (after grain-copyright import):
import './grain-oauth-callback.js';
Step 2: Register the route
Add after line 67 (before the * wildcard route):
.register('/oauth/callback', 'grain-oauth-callback')
Step 3: Verify build passes
Run: npm run build
Expected: Build succeeds
Step 4: Commit
git add src/components/pages/grain-app.js
git commit -m "feat: register /oauth/callback route"
Task 3: Store Return URL Before OAuth Redirect#
Files:
- Modify:
src/services/auth.js
Step 1: Update login method to store return URL
Replace the login method (lines 58-60):
async login(handle) {
sessionStorage.setItem('oauth_return_url', window.location.pathname);
await this.#client.loginWithRedirect({ handle });
}
Step 2: Verify build passes
Run: npm run build
Expected: Build succeeds
Step 3: Commit
git add src/services/auth.js
git commit -m "feat: store return URL before OAuth redirect"
Task 4: Navigate to Return URL After OAuth Callback#
Files:
- Modify:
src/services/auth.js
Step 1: Add router import at top of file
Add after line 1:
import { router } from '../router.js';
Step 2: Update callback handling to navigate to return URL
Replace lines 18-22 (the callback handling block):
// Handle OAuth callback if present
if (window.location.search.includes('code=')) {
await this.#client.handleRedirectCallback();
const returnUrl = sessionStorage.getItem('oauth_return_url') || '/';
sessionStorage.removeItem('oauth_return_url');
router.replace(returnUrl);
}
Step 3: Verify build passes
Run: npm run build
Expected: Build succeeds
Step 4: Commit
git add src/services/auth.js
git commit -m "feat: navigate to return URL after OAuth callback"
Task 5: Manual Testing Checklist#
Step 1: Start dev server
Run: npm run dev
Step 2: Test login from timeline
- Navigate to
http://localhost:5173/ - Click login, enter handle
- Complete OAuth flow
- Verify you return to
/
Step 3: Test login from profile page
- Navigate to
http://localhost:5173/profile/grain.social - Click login, enter handle
- Complete OAuth flow
- Verify you return to
/profile/grain.social
Step 4: Test login from explore page
- Navigate to
http://localhost:5173/explore - Click login, enter handle
- Complete OAuth flow
- Verify you return to
/explore
Step 5: Test direct visit to callback
- Navigate directly to
http://localhost:5173/oauth/callback - Verify it shows "Signing in..." briefly then redirects to
/
Task 6: Final Build Verification#
Step 1: Run production build
Run: npm run build
Expected: Build succeeds with no errors
Step 2: Commit any remaining changes
If all tests pass and no changes needed, this task is complete.