This commit is contained in:
		| @@ -1,11 +1,10 @@ | ||||
| import http from 'http'; | ||||
|  | ||||
| class Response extends http.IncomingMessage { | ||||
| export class Response extends http.IncomingMessage { | ||||
|   body?: string; | ||||
| } | ||||
|  | ||||
| export | ||||
| function get ( | ||||
| export function get ( | ||||
|   headers: http.OutgoingHttpHeaders = {} | ||||
| ): Promise<Response> { | ||||
|   return new Promise ((resolve) => { | ||||
| @@ -21,3 +20,9 @@ function get ( | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| export function modify_signature (signature: string): string { | ||||
|   const dec = signature.split ('.'); | ||||
|   dec[1] = ''; | ||||
|   return dec.join ('.'); | ||||
| } | ||||
|   | ||||
| @@ -1,17 +1,72 @@ | ||||
| import http from 'http'; | ||||
| import http, { IncomingMessage, ServerResponse } from 'http'; | ||||
| import { to_b64 } from '@sapphirecode/encoding-helper'; | ||||
| import auth from '../../lib/Authority'; | ||||
| import { get } from '../Helper'; | ||||
| import { get, modify_signature, Response } from '../Helper'; | ||||
| import create_auth_handler from '../../lib/AuthHandler'; | ||||
|  | ||||
|  | ||||
| const expires_seconds = 600; | ||||
| const refresh_expires_seconds = 3600; | ||||
| const part_expires_seconds = 60; | ||||
|  | ||||
| interface CheckHeaderResult { | ||||
|   at: string; | ||||
|   rt?: string; | ||||
|   data: Record<string, unknown>; | ||||
| } | ||||
|  | ||||
| function check_headers (resp: Response): CheckHeaderResult { | ||||
|   const data = JSON.parse (resp.body as string); | ||||
|   const at = data.access_token; | ||||
|   const rt = data.refresh_token; | ||||
|   expect (resp.headers['cache-control']) | ||||
|     .toEqual ('no-store'); | ||||
|   expect (resp.headers.pragma) | ||||
|     .toEqual ('no-cache'); | ||||
|   return { data, at, rt }; | ||||
| } | ||||
|  | ||||
| function check_token (token: string, type: string):void { | ||||
|   const v = auth.verify (token); | ||||
|   expect (v.valid) | ||||
|     .toEqual (true); | ||||
|   expect (v.authorized) | ||||
|     .toEqual (type === 'access_token'); | ||||
|   expect (v.type) | ||||
|     .toEqual (type); | ||||
|   expect (token) | ||||
|     .toMatch (/^[0-9a-z-._~+/]+$/ui); | ||||
| } | ||||
|  | ||||
| // eslint-disable-next-line max-lines-per-function | ||||
| xdescribe ('auth handler', () => { | ||||
| describe ('auth handler', () => { | ||||
|   let server: http.Server|null = null; | ||||
|   beforeAll (() => { | ||||
|     server = http.createServer ((req, res) => { | ||||
|       res.writeHead (404); | ||||
|       res.end (); | ||||
|     const ah = create_auth_handler ((req) => { | ||||
|       if (!req.is_basic) { | ||||
|         req.invalid ('unknown autorization type'); | ||||
|       } | ||||
|       else if (req.user !== 'foo' || req.password !== 'bar') { | ||||
|         req.deny (); | ||||
|       } | ||||
|       else { | ||||
|         req.allow_access ({ | ||||
|           access_token_expires_in:  expires_seconds, | ||||
|           include_refresh_token:    true, | ||||
|           refresh_token_expires_in: refresh_expires_seconds | ||||
|         }); | ||||
|       } | ||||
|     }, { | ||||
|       cookie_name: 'cookie_jar', | ||||
|       refresh:     { | ||||
|         access_token_expires_in:  expires_seconds, | ||||
|         refresh_token_expires_in: refresh_expires_seconds, | ||||
|         include_refresh_token:    true | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     server = http.createServer ((req: IncomingMessage, res: ServerResponse) => { | ||||
|       ah (req, res); | ||||
|     }); | ||||
|     server.listen (3000); | ||||
|  | ||||
| @@ -21,46 +76,144 @@ xdescribe ('auth handler', () => { | ||||
|       .mockDate (new Date); | ||||
|   }); | ||||
|  | ||||
|   it ('should return a valid access and refresh token', async () => { | ||||
|     const resp = await get ({ authorization: 'Basic foo:bar' }); | ||||
|     expect (resp.statusCode) | ||||
|   it ('auth test sequence', async () => { | ||||
|     // get initial access and refresh tokens | ||||
|     const resp1 = await get ({ authorization: 'Basic foo:bar' }); | ||||
|     expect (resp1.statusCode) | ||||
|       .toEqual (200); | ||||
|     const data = JSON.parse (resp.body as string); | ||||
|     const at = data.access_token; | ||||
|     const rt = data.refresh_token; | ||||
|     expect (resp.headers['set-cookie']) | ||||
|       .toContain (`cookie_jar=${at}`); | ||||
|     expect (resp.headers['cache-control']) | ||||
|       .toEqual ('no-store'); | ||||
|     expect (resp.headers.pragma) | ||||
|       .toEqual ('no-cache'); | ||||
|     expect (data.token_type) | ||||
|     const res1 = check_headers (resp1); | ||||
|     expect (res1.data.token_type) | ||||
|       .toEqual ('bearer'); | ||||
|     expect (data.expires_in) | ||||
|     expect (resp1.headers['set-cookie']) | ||||
|       .toContain (`cookie_jar=${res1.at}`); | ||||
|  | ||||
|     check_token (res1.at as string, 'access_token'); | ||||
|     expect (res1.data.expires_in) | ||||
|       .toEqual (expires_seconds); | ||||
|     expect (data.refresh_expires_in) | ||||
|  | ||||
|     check_token (res1.rt as string, 'refresh_token'); | ||||
|     expect (res1.data.refresh_expires_in) | ||||
|       .toEqual (refresh_expires_seconds); | ||||
|  | ||||
|     expect (at as string) | ||||
|       .toMatch (/^[0-9a-z-._~+/]+$/ui); | ||||
|     expect (rt as string) | ||||
|       .toMatch (/^[0-9a-z-._~+/]+$/ui); | ||||
|  | ||||
|     const atv = auth.verify (at as string); | ||||
|     expect (atv.valid) | ||||
|       .toEqual (true); | ||||
|     expect (atv.authorized) | ||||
|       .toEqual (true); | ||||
|     expect (atv.type) | ||||
|       .toEqual ('access_token'); | ||||
|     // get refresh token | ||||
|     const resp2 = await get ({ authorization: `Bearer ${res1.rt}` }); | ||||
|     expect (resp2.statusCode) | ||||
|       .toEqual (200); | ||||
|     const res2 = check_headers (resp2); | ||||
|     expect (res2.data.token_type) | ||||
|       .toEqual ('bearer'); | ||||
|     expect (resp2.headers['set-cookie']) | ||||
|       .toContain (`cookie_jar=${res2.at}`); | ||||
|  | ||||
|     const rtv = auth.verify (rt as string); | ||||
|     expect (rtv.valid) | ||||
|       .toEqual (true); | ||||
|     expect (rtv.authorized) | ||||
|       .toEqual (false); | ||||
|     expect (rtv.type) | ||||
|       .toEqual ('refresh_token'); | ||||
|     check_token (res2.at as string, 'access_token'); | ||||
|     expect (res2.data.expires_in) | ||||
|       .toEqual (expires_seconds); | ||||
|     expect (res2.at).not.toEqual (res1.at); | ||||
|  | ||||
|     check_token (res2.rt as string, 'refresh_token'); | ||||
|     expect (res2.data.refresh_expires_in) | ||||
|       .toEqual (refresh_expires_seconds); | ||||
|     expect (res2.rt).not.toEqual (res1.rt); | ||||
|   }); | ||||
|  | ||||
|   it ('should return the correct denial message', async () => { | ||||
|     const resp = await get ({ authorization: 'Basic bar:baz' }); | ||||
|     expect (resp.statusCode) | ||||
|       .toEqual (401); | ||||
|     const res = check_headers (resp); | ||||
|     expect (res.data) | ||||
|       .toEqual ({ error: 'invalid_client' }); | ||||
|   }); | ||||
|  | ||||
|   it ('should allow base64 login', async () => { | ||||
|     // get initial access and refresh tokens | ||||
|     const resp1 = await get ({ authorization: `Basic ${to_b64 ('foo:bar')}` }); | ||||
|     expect (resp1.statusCode) | ||||
|       .toEqual (200); | ||||
|     const res1 = check_headers (resp1); | ||||
|     expect (res1.data.token_type) | ||||
|       .toEqual ('bearer'); | ||||
|     expect (resp1.headers['set-cookie']) | ||||
|       .toContain (`cookie_jar=${res1.at}`); | ||||
|  | ||||
|     check_token (res1.at as string, 'access_token'); | ||||
|     expect (res1.data.expires_in) | ||||
|       .toEqual (expires_seconds); | ||||
|  | ||||
|     check_token (res1.rt as string, 'refresh_token'); | ||||
|     expect (res1.data.refresh_expires_in) | ||||
|       .toEqual (refresh_expires_seconds); | ||||
|   }); | ||||
|  | ||||
|   it ('should reject invalid requests', async () => { | ||||
|     const resp1 = await get (); | ||||
|     expect (resp1.statusCode) | ||||
|       .toEqual (401); | ||||
|     const res1 = check_headers (resp1); | ||||
|     expect (res1.data) | ||||
|       .toEqual ({ error: 'invalid_client' }); | ||||
|  | ||||
|     const resp2a = await get ({ authorization: 'Basic foo:bar' }); | ||||
|     const res2a = check_headers (resp2a); | ||||
|     const resp2b = await get ( | ||||
|       { authorization: `Bearer ${res2a.at}` } | ||||
|     ); | ||||
|     expect (resp2b.statusCode) | ||||
|       .toEqual (400); | ||||
|     const res2 = check_headers (resp2b); | ||||
|     expect (res2.data) | ||||
|       .toEqual ({ | ||||
|         error:             'invalid_request', | ||||
|         error_description: 'invalid bearer type' | ||||
|       }); | ||||
|   }); | ||||
|  | ||||
|   it ('should reject an invalid token', async () => { | ||||
|     const resp1 = await get ({ authorization: 'Basic foo:bar' }); | ||||
|     const res1 = check_headers (resp1); | ||||
|     const resp2 = await get ( | ||||
|       { authorization: `Bearer ${modify_signature (res1.at)}` } | ||||
|     ); | ||||
|     expect (resp2.statusCode) | ||||
|       .toEqual (401); | ||||
|     const res2 = check_headers (resp2); | ||||
|     expect (res2.data) | ||||
|       .toEqual ({ error: 'invalid_client' }); | ||||
|   }); | ||||
|  | ||||
|  | ||||
|   xit ('should process part token', async () => { | ||||
|     const resp1 = await get ({ authorization: 'Basic part:bar' }); | ||||
|     expect (resp1.statusCode) | ||||
|       .toEqual (200); | ||||
|     const res1 = check_headers (resp1); | ||||
|     expect (res1.data.token_type) | ||||
|       .toEqual ('bearer'); | ||||
|     expect (res1.data.expires_in) | ||||
|       .toEqual (part_expires_seconds); | ||||
|     check_token (res1.data.part_token as string, 'part_token'); | ||||
|  | ||||
|     const resp2 = await get ( | ||||
|       { authorization: `Bearer ${res1.data.part_token}` } | ||||
|     ); | ||||
|     expect (resp2.statusCode) | ||||
|       .toEqual (200); | ||||
|     const res2 = check_headers (resp2); | ||||
|     expect (res2.data.token_type) | ||||
|       .toEqual ('bearer'); | ||||
|     expect (resp2.headers['set-cookie']) | ||||
|       .toContain (`cookie_jar=${res2.at}`); | ||||
|  | ||||
|     check_token (res2.at as string, 'access_token'); | ||||
|     expect (res2.data.expires_in) | ||||
|       .toEqual (expires_seconds); | ||||
|     expect (res2.at).not.toEqual (res1.at); | ||||
|  | ||||
|     check_token (res2.rt as string, 'refresh_token'); | ||||
|     expect (res2.data.refresh_expires_in) | ||||
|       .toEqual (refresh_expires_seconds); | ||||
|     expect (res2.rt).not.toEqual (res1.rt); | ||||
|   }); | ||||
|  | ||||
|   afterAll (() => { | ||||
|   | ||||
| @@ -7,12 +7,7 @@ | ||||
|  | ||||
| import auth from '../../lib/Authority'; | ||||
| import bl from '../../lib/Blacklist'; | ||||
|  | ||||
| function modify_signature (signature: string): string { | ||||
|   const dec = signature.split ('.'); | ||||
|   dec[1] = ''; | ||||
|   return dec.join ('.'); | ||||
| } | ||||
| import { modify_signature } from '../Helper'; | ||||
|  | ||||
| // eslint-disable-next-line max-lines-per-function | ||||
| describe ('authority', () => { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user