Skip to content

Commit 81d69a7

Browse files
feat: ship a tiny sign-in app at /signin.html (#15)
Replaces the "Sign in → /idp → 'go find a Solid app'" detour with a real OIDC client baked into jspod. Welcome page's Sign in button now points at /signin.html. What signin.html does: - Imports solid-oidc (zero-dep, AGPL, same JavaScriptSolidServer org as JSS and jspod) from a version-pinned jsdelivr URL - On fresh visit: tries to restore prior session; if none, calls session.login(thisPod, /signin.html). solid-oidc handles dynamic client registration, PKCE, DPoP. Browser is redirected to JSS's /idp/auth with OAuth params — at which point JSS renders its real Passkey / Schnorr / username+password form - On return (?code= present): session.handleRedirectFromLogin() exchanges code for DPoP-bound tokens, stores them in IndexedDB, redirects user back to the pod root Collapses ~6 context switches (welcome → /idp message → Pilot → type pod URL → /idp form → me/me → back) down to 2 (welcome → /idp form → me/me → back). ACL: signin.html ships with a sibling signin.html.acl giving public read (mirrors JSS's index.html.acl pattern), so the page is reachable before any session exists. Implementation: - signin.html (~4 KB) — single-file static page - signin.html.acl — public-read for the page itself - welcome.html — Sign in href changes from ./idp to ./signin.html - index.js — always-overwrite copy alongside welcome.html - package.json — files whitelist + 0.0.17 bump Known follow-up (not in this PR): after sign-in, the welcome page's locked-tile probe still uses plain fetch and so the tiles stay dimmed. Auth-aware probes need solid-oidc on welcome.html too, OR a separate /account.html dashboard. Tracking as next step. Refs #1
1 parent eff3fc9 commit 81d69a7

5 files changed

Lines changed: 137 additions & 2 deletions

File tree

index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,17 @@ ready.then((ok) => {
473473
const indexDst = join(options.root, 'index.html');
474474
if (existsSync(indexSrc)) copyFileSync(indexSrc, indexDst);
475475

476+
// signin.html is a jspod-owned static page; always-overwrite so
477+
// upgrades to the sign-in flow ship immediately. Pair with a .acl
478+
// granting public read so unauthenticated visitors can reach it
479+
// (mirrors JSS's index.html.acl pattern).
480+
const signinSrc = join(__dirname, 'signin.html');
481+
const signinDst = join(options.root, 'signin.html');
482+
if (existsSync(signinSrc)) copyFileSync(signinSrc, signinDst);
483+
const signinAclSrc = join(__dirname, 'signin.html.acl');
484+
const signinAclDst = join(options.root, 'signin.html.acl');
485+
if (existsSync(signinAclSrc)) copyFileSync(signinAclSrc, signinAclDst);
486+
476487
const linksSrc = join(__dirname, 'links.jsonld');
477488
const linksDst = join(options.root, 'public', 'links.jsonld');
478489
if (existsSync(linksSrc) && !existsSync(linksDst)) {

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jspod",
3-
"version": "0.0.16",
3+
"version": "0.0.17",
44
"description": "JavaScript Solid Pod - Just works, batteries included",
55
"type": "module",
66
"main": "index.js",
@@ -12,6 +12,8 @@
1212
"data-browser.js",
1313
"data-browser.css",
1414
"welcome.html",
15+
"signin.html",
16+
"signin.html.acl",
1517
"links.jsonld"
1618
],
1719
"scripts": {

signin.html

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width,initial-scale=1">
6+
<title>Sign in — Solid pod</title>
7+
<style>
8+
body{font:14px/1.6 system-ui,-apple-system,sans-serif;color:#222;background:#f3eee5;max-width:560px;margin:2em auto;padding:0 1em}
9+
.card{background:#fff;border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,.08);padding:1.5em 2em}
10+
h1{margin:0 0 .5em;font-weight:500;font-size:1.6em}
11+
.lede{color:#555;margin:0 0 1em}
12+
.creds{background:#f3eee5;padding:.8em 1em;border-radius:8px;margin:1em 0;font-family:"SFMono-Regular",Consolas,monospace}
13+
.creds .label{font-family:system-ui,sans-serif;color:#555;margin-right:.5em}
14+
.error{background:#fff0f0;color:#a00;padding:.8em 1em;border-radius:8px;margin:1em 0;display:none;font-family:"SFMono-Regular",Consolas,monospace;font-size:.9em;word-break:break-word}
15+
.error.show{display:block}
16+
a{color:#0a66c2;text-decoration:none}
17+
a:hover{text-decoration:underline}
18+
.btn{display:inline-block;padding:.6em 1.2em;border-radius:6px;text-decoration:none;font-size:.95em;background:#0a66c2;color:#fff;border:1px solid transparent;cursor:pointer}
19+
.btn:hover{background:#084e96;text-decoration:none}
20+
.btn-secondary{background:#fff;color:#222;border-color:#ddd}
21+
.btn-secondary:hover{background:#f0efeb}
22+
footer{margin-top:1.5em;padding-top:1em;border-top:1px solid #eee;color:#888;font-size:.85em;text-align:center}
23+
</style>
24+
</head>
25+
<body>
26+
<div class="card">
27+
<h1 id="title">Signing in…</h1>
28+
<p class="lede" id="lede">Starting Solid-OIDC handshake with <strong id="pod-url">this pod</strong>.</p>
29+
<div class="creds">
30+
<span class="label">Default sign-in:</span> me <span class="label">/</span> me
31+
</div>
32+
<div class="error" id="error"></div>
33+
<p id="retry" style="display:none"><a href="./signin.html" class="btn">Try again</a> <a href="./" class="btn btn-secondary">Back to pod</a></p>
34+
<footer>Powered by <a href="https://github.com/JavaScriptSolidServer/jspod">jspod</a> · <a href="https://github.com/JavaScriptSolidServer/solid-oidc">solid-oidc</a></footer>
35+
</div>
36+
<script type="module">
37+
// Tiny sign-in client. Uses solid-oidc (zero-dep, AGPL, same org).
38+
// Flow:
39+
// 1. Fresh visit → try restore; if active, redirect to /. Else call
40+
// session.login(idp, redirectUri) which bounces to /idp with OAuth
41+
// params; JSS renders its real login form there.
42+
// 2. Return visit (?code= present) → handleRedirectFromLogin, then
43+
// redirect home.
44+
import Session from 'https://cdn.jsdelivr.net/npm/solid-oidc@0.0.8/solid-oidc.js';
45+
46+
const idp = new URL('./', window.location.href).href;
47+
const redirectUri = new URL('./signin.html', window.location.href).href;
48+
49+
document.getElementById('pod-url').textContent = idp;
50+
51+
const titleEl = document.getElementById('title');
52+
const ledeEl = document.getElementById('lede');
53+
const errorEl = document.getElementById('error');
54+
const retryEl = document.getElementById('retry');
55+
56+
function fail(msg) {
57+
titleEl.textContent = 'Sign-in failed';
58+
ledeEl.style.display = 'none';
59+
errorEl.textContent = msg;
60+
errorEl.classList.add('show');
61+
retryEl.style.display = 'block';
62+
}
63+
64+
try {
65+
const session = new Session();
66+
const params = new URLSearchParams(window.location.search);
67+
68+
if (params.get('code')) {
69+
titleEl.textContent = 'Completing sign-in…';
70+
ledeEl.textContent = 'Exchanging the authorization code for a token.';
71+
await session.handleRedirectFromLogin();
72+
titleEl.textContent = 'Signed in';
73+
ledeEl.textContent = 'WebID: ' + (session.webId || '(none)') + '. Redirecting…';
74+
setTimeout(() => { window.location.href = './'; }, 800);
75+
} else {
76+
// Try to restore a prior session. solid-oidc throws "Missing
77+
// refresh data" on a clean first visit — that's fine, it just
78+
// means there's no session yet and we should kick off login.
79+
let restored = false;
80+
try {
81+
await session.restore();
82+
restored = session.isActive;
83+
} catch {
84+
restored = false;
85+
}
86+
if (restored) {
87+
titleEl.textContent = 'Already signed in';
88+
ledeEl.textContent = 'WebID: ' + session.webId + '. Redirecting…';
89+
setTimeout(() => { window.location.href = './'; }, 400);
90+
} else {
91+
await session.login(idp, redirectUri);
92+
}
93+
}
94+
} catch (e) {
95+
fail(e && e.message ? e.message : String(e));
96+
}
97+
</script>
98+
</body>
99+
</html>

signin.html.acl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"@context": {
3+
"acl": "http://www.w3.org/ns/auth/acl#",
4+
"foaf": "http://xmlns.com/foaf/0.1/"
5+
},
6+
"@graph": [
7+
{
8+
"@id": "#public",
9+
"@type": "acl:Authorization",
10+
"acl:agentClass": {
11+
"@id": "foaf:Agent"
12+
},
13+
"acl:accessTo": {
14+
"@id": "./signin.html"
15+
},
16+
"acl:mode": [
17+
{
18+
"@id": "acl:Read"
19+
}
20+
]
21+
}
22+
]
23+
}

welcome.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ <h1>Your Solid pod</h1>
3535
<p class="lede">Running at <a id="server-url" href="./">this server</a>.</p>
3636

3737
<div class="actions">
38-
<a href="./idp" class="btn btn-primary" data-cond="login" hidden>Sign in</a>
38+
<a href="./signin.html" class="btn btn-primary" data-cond="login" hidden>Sign in</a>
3939
<a href="./idp/register" class="btn btn-secondary" data-cond="register" hidden>Sign up</a>
4040
</div>
4141

0 commit comments

Comments
 (0)