javascript-solid-server 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/test/idp.test.js CHANGED
@@ -256,3 +256,172 @@ describe('Identity Provider - Accounts', () => {
256
256
  assert.ok(!account.password, 'should not store plain password');
257
257
  });
258
258
  });
259
+
260
+ describe('Identity Provider - Credentials Endpoint', () => {
261
+ let server;
262
+ const CREDS_DATA_DIR = './test-data-idp-creds';
263
+ const CREDS_PORT = 3101;
264
+ const CREDS_URL = `http://${TEST_HOST}:${CREDS_PORT}`;
265
+
266
+ before(async () => {
267
+ await fs.remove(CREDS_DATA_DIR);
268
+ await fs.ensureDir(CREDS_DATA_DIR);
269
+
270
+ server = createServer({
271
+ logger: false,
272
+ root: CREDS_DATA_DIR,
273
+ idp: true,
274
+ idpIssuer: CREDS_URL,
275
+ });
276
+
277
+ await server.listen({ port: CREDS_PORT, host: TEST_HOST });
278
+
279
+ // Create a test user
280
+ await fetch(`${CREDS_URL}/.pods`, {
281
+ method: 'POST',
282
+ headers: { 'Content-Type': 'application/json' },
283
+ body: JSON.stringify({
284
+ name: 'credtest',
285
+ email: 'credtest@example.com',
286
+ password: 'testpassword123',
287
+ }),
288
+ });
289
+ });
290
+
291
+ after(async () => {
292
+ await server.close();
293
+ await fs.remove(CREDS_DATA_DIR);
294
+ });
295
+
296
+ describe('GET /idp/credentials', () => {
297
+ it('should return endpoint info', async () => {
298
+ const res = await fetch(`${CREDS_URL}/idp/credentials`);
299
+ assert.strictEqual(res.status, 200);
300
+
301
+ const info = await res.json();
302
+ assert.ok(info.endpoint);
303
+ assert.strictEqual(info.method, 'POST');
304
+ assert.ok(info.parameters.email);
305
+ assert.ok(info.parameters.password);
306
+ });
307
+ });
308
+
309
+ describe('POST /idp/credentials', () => {
310
+ it('should return 400 for missing credentials', async () => {
311
+ const res = await fetch(`${CREDS_URL}/idp/credentials`, {
312
+ method: 'POST',
313
+ headers: { 'Content-Type': 'application/json' },
314
+ body: JSON.stringify({}),
315
+ });
316
+
317
+ assert.strictEqual(res.status, 400);
318
+ const body = await res.json();
319
+ assert.strictEqual(body.error, 'invalid_request');
320
+ });
321
+
322
+ it('should return 401 for wrong password', async () => {
323
+ const res = await fetch(`${CREDS_URL}/idp/credentials`, {
324
+ method: 'POST',
325
+ headers: { 'Content-Type': 'application/json' },
326
+ body: JSON.stringify({
327
+ email: 'credtest@example.com',
328
+ password: 'wrongpassword',
329
+ }),
330
+ });
331
+
332
+ assert.strictEqual(res.status, 401);
333
+ const body = await res.json();
334
+ assert.strictEqual(body.error, 'invalid_grant');
335
+ });
336
+
337
+ it('should return 401 for unknown email', async () => {
338
+ const res = await fetch(`${CREDS_URL}/idp/credentials`, {
339
+ method: 'POST',
340
+ headers: { 'Content-Type': 'application/json' },
341
+ body: JSON.stringify({
342
+ email: 'unknown@example.com',
343
+ password: 'anypassword',
344
+ }),
345
+ });
346
+
347
+ assert.strictEqual(res.status, 401);
348
+ });
349
+
350
+ it('should return access token for valid credentials', async () => {
351
+ const res = await fetch(`${CREDS_URL}/idp/credentials`, {
352
+ method: 'POST',
353
+ headers: { 'Content-Type': 'application/json' },
354
+ body: JSON.stringify({
355
+ email: 'credtest@example.com',
356
+ password: 'testpassword123',
357
+ }),
358
+ });
359
+
360
+ assert.strictEqual(res.status, 200);
361
+ const body = await res.json();
362
+
363
+ assert.ok(body.access_token, 'should have access_token');
364
+ assert.strictEqual(body.token_type, 'Bearer');
365
+ assert.ok(body.expires_in > 0, 'should have expires_in');
366
+ assert.ok(body.webid.includes('credtest'), 'should have webid');
367
+ });
368
+
369
+ it('should return simple token with webid for Bearer auth', async () => {
370
+ const res = await fetch(`${CREDS_URL}/idp/credentials`, {
371
+ method: 'POST',
372
+ headers: { 'Content-Type': 'application/json' },
373
+ body: JSON.stringify({
374
+ email: 'credtest@example.com',
375
+ password: 'testpassword123',
376
+ }),
377
+ });
378
+
379
+ const body = await res.json();
380
+
381
+ // Simple tokens have format: base64payload.signature
382
+ const parts = body.access_token.split('.');
383
+ assert.strictEqual(parts.length, 2, 'simple token has 2 parts');
384
+
385
+ // Decode the payload
386
+ const payload = JSON.parse(Buffer.from(parts[0], 'base64url').toString());
387
+
388
+ assert.ok(payload.webId, 'token should have webId');
389
+ assert.ok(payload.webId.includes('credtest'), 'webId should reference user');
390
+ assert.ok(payload.exp > payload.iat, 'should have valid expiry');
391
+ });
392
+
393
+ it('should work with form-encoded body', async () => {
394
+ const res = await fetch(`${CREDS_URL}/idp/credentials`, {
395
+ method: 'POST',
396
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
397
+ body: 'email=credtest%40example.com&password=testpassword123',
398
+ });
399
+
400
+ assert.strictEqual(res.status, 200);
401
+ const body = await res.json();
402
+ assert.ok(body.access_token);
403
+ });
404
+
405
+ it('should allow using token to access protected resource', async () => {
406
+ // Get access token
407
+ const tokenRes = await fetch(`${CREDS_URL}/idp/credentials`, {
408
+ method: 'POST',
409
+ headers: { 'Content-Type': 'application/json' },
410
+ body: JSON.stringify({
411
+ email: 'credtest@example.com',
412
+ password: 'testpassword123',
413
+ }),
414
+ });
415
+
416
+ const { access_token } = await tokenRes.json();
417
+
418
+ // Try to access private resource
419
+ const res = await fetch(`${CREDS_URL}/credtest/private/`, {
420
+ headers: { 'Authorization': `Bearer ${access_token}` },
421
+ });
422
+
423
+ // Should succeed (not 401/403)
424
+ assert.ok([200, 404].includes(res.status), `expected 200 or 404, got ${res.status}`);
425
+ });
426
+ });
427
+ });