Skip to content

Commit 46dbfcd

Browse files
Add built-in Solid-OIDC Identity Provider (v0.0.12)
Implements a modular, standalone Identity Provider using oidc-provider: - Enable with --idp flag or { idp: true } config - OIDC discovery at /.well-known/openid-configuration - DPoP token binding for Solid-OIDC compliance - Dynamic client registration - Filesystem-based adapter for all OIDC data - Account management with bcrypt password hashing - Login/consent interaction handlers with HTML views - JWKS key generation and rotation support Pod creation changes when IdP enabled: - Requires email and password - Creates account in .idp/accounts/ - Returns loginUrl instead of simple token New dependencies: oidc-provider, bcrypt, @fastify/middie 174 tests passing (11 new IdP tests)
1 parent 8e3cb71 commit 46dbfcd

15 files changed

Lines changed: 2875 additions & 22 deletions

README.md

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ npm run benchmark
5454

5555
## Features
5656

57-
### Implemented (v0.0.11)
57+
### Implemented (v0.0.12)
5858

5959
- **LDP CRUD Operations** - GET, PUT, POST, DELETE, HEAD
6060
- **N3 Patch** - Solid's native patch format for RDF updates
@@ -67,6 +67,7 @@ npm run benchmark
6767
- **Multi-user Pods** - Create pods at `/<username>/`
6868
- **WebID Profiles** - JSON-LD structured data in HTML at pod root
6969
- **Web Access Control (WAC)** - `.acl` file-based authorization
70+
- **Solid-OIDC Identity Provider** - Built-in IdP with DPoP, dynamic registration
7071
- **Solid-OIDC Resource Server** - Accept DPoP-bound access tokens from external IdPs
7172
- **Simple Auth Tokens** - Built-in token authentication for development
7273
- **Content Negotiation** - Optional Turtle <-> JSON-LD conversion
@@ -132,6 +133,8 @@ jss --help # Show help
132133
| `--ssl-cert <path>` | SSL certificate (PEM) | - |
133134
| `--conneg` | Enable Turtle support | false |
134135
| `--notifications` | Enable WebSocket | false |
136+
| `--idp` | Enable built-in IdP | false |
137+
| `--idp-issuer <url>` | IdP issuer URL | (auto) |
135138
| `-q, --quiet` | Suppress logs | false |
136139

137140
### Environment Variables
@@ -274,9 +277,38 @@ Use the token returned from pod creation:
274277
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:3000/alice/private/
275278
```
276279

277-
### Solid-OIDC (Production)
280+
### Built-in Identity Provider (v0.0.12+)
278281

279-
The server accepts DPoP-bound access tokens from external Solid identity providers:
282+
Enable the built-in Solid-OIDC Identity Provider:
283+
284+
```bash
285+
jss start --idp
286+
```
287+
288+
With IdP enabled, pod creation requires email and password:
289+
290+
```bash
291+
curl -X POST http://localhost:3000/.pods \
292+
-H "Content-Type: application/json" \
293+
-d '{"name": "alice", "email": "alice@example.com", "password": "secret123"}'
294+
```
295+
296+
Response:
297+
```json
298+
{
299+
"name": "alice",
300+
"webId": "http://localhost:3000/alice/#me",
301+
"podUri": "http://localhost:3000/alice/",
302+
"idpIssuer": "http://localhost:3000",
303+
"loginUrl": "http://localhost:3000/idp/auth"
304+
}
305+
```
306+
307+
OIDC Discovery: `/.well-known/openid-configuration`
308+
309+
### Solid-OIDC (External IdP)
310+
311+
The server also accepts DPoP-bound access tokens from external Solid identity providers:
280312

281313
```bash
282314
curl -H "Authorization: DPoP ACCESS_TOKEN" \
@@ -323,7 +355,7 @@ Server: pub http://localhost:3000/alice/public/data.json (on change)
323355
npm test
324356
```
325357

326-
Currently passing: **163 tests** (including 27 conformance tests)
358+
Currently passing: **174 tests** (including 27 conformance tests)
327359

328360
## Project Structure
329361

@@ -355,6 +387,14 @@ src/
355387
│ ├── index.js # WebSocket plugin
356388
│ ├── events.js # Event emitter
357389
│ └── websocket.js # solid-0.1 protocol
390+
├── idp/
391+
│ ├── index.js # Identity Provider plugin
392+
│ ├── provider.js # oidc-provider config
393+
│ ├── adapter.js # Filesystem adapter
394+
│ ├── accounts.js # User account management
395+
│ ├── keys.js # JWKS key management
396+
│ ├── interactions.js # Login/consent handlers
397+
│ └── views.js # HTML templates
358398
├── rdf/
359399
│ ├── turtle.js # Turtle <-> JSON-LD
360400
│ └── conneg.js # Content negotiation
@@ -372,6 +412,8 @@ Minimal dependencies for a fast, secure server:
372412
- **fs-extra** - Enhanced file operations
373413
- **jose** - JWT/JWK handling for Solid-OIDC
374414
- **n3** - Turtle parsing (only used when conneg enabled)
415+
- **oidc-provider** - OpenID Connect Identity Provider (only when IdP enabled)
416+
- **bcrypt** - Password hashing (only when IdP enabled)
375417

376418
## License
377419

bin/jss.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ program
4444
.option('--no-conneg', 'Disable content negotiation')
4545
.option('--notifications', 'Enable WebSocket notifications')
4646
.option('--no-notifications', 'Disable WebSocket notifications')
47+
.option('--idp', 'Enable built-in Identity Provider')
48+
.option('--no-idp', 'Disable built-in Identity Provider')
49+
.option('--idp-issuer <url>', 'IdP issuer URL (defaults to server URL)')
4750
.option('-q, --quiet', 'Suppress log output')
4851
.option('--print-config', 'Print configuration and exit')
4952
.action(async (options) => {
@@ -55,11 +58,19 @@ program
5558
process.exit(0);
5659
}
5760

61+
// Determine IdP issuer URL
62+
const protocol = config.ssl ? 'https' : 'http';
63+
const serverHost = config.host === '0.0.0.0' ? 'localhost' : config.host;
64+
const baseUrl = `${protocol}://${serverHost}:${config.port}`;
65+
const idpIssuer = config.idpIssuer || baseUrl;
66+
5867
// Create and start server
5968
const server = createServer({
6069
logger: config.logger,
6170
conneg: config.conneg,
6271
notifications: config.notifications,
72+
idp: config.idp,
73+
idpIssuer: idpIssuer,
6374
ssl: config.ssl ? {
6475
key: await fs.readFile(config.sslKey),
6576
cert: await fs.readFile(config.sslCert),
@@ -69,16 +80,14 @@ program
6980

7081
await server.listen({ port: config.port, host: config.host });
7182

72-
const protocol = config.ssl ? 'https' : 'http';
73-
const address = config.host === '0.0.0.0' ? 'localhost' : config.host;
74-
7583
if (!config.quiet) {
7684
console.log(`\n JavaScript Solid Server v${pkg.version}`);
77-
console.log(` ${protocol}://${address}:${config.port}/`);
85+
console.log(` ${baseUrl}/`);
7886
console.log(`\n Data: ${path.resolve(config.root)}`);
7987
if (config.ssl) console.log(' SSL: enabled');
8088
if (config.conneg) console.log(' Conneg: enabled');
8189
if (config.notifications) console.log(' WebSocket: enabled');
90+
if (config.idp) console.log(` IdP: ${idpIssuer}`);
8291
console.log('\n Press Ctrl+C to stop\n');
8392
}
8493

@@ -142,6 +151,15 @@ program
142151
config.sslCert = await prompt('SSL certificate path', './ssl/cert.pem');
143152
}
144153

154+
// Ask about IdP
155+
config.idp = await confirm('Enable built-in Identity Provider?', false);
156+
if (config.idp) {
157+
const customIssuer = await confirm('Use custom issuer URL?', false);
158+
if (customIssuer) {
159+
config.idpIssuer = await prompt('IdP issuer URL', 'https://example.com');
160+
}
161+
}
162+
145163
console.log('');
146164
}
147165

0 commit comments

Comments
 (0)