Add new type definition for http-auth (#47878)

* add http-auth type

* fix for dtslint
This commit is contained in:
nokazn 2020-09-23 04:22:02 +09:00 committed by GitHub
parent 5d9cf12b75
commit 5d2eb256ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 359 additions and 0 deletions

153
types/http-auth/index.d.ts vendored Normal file
View File

@ -0,0 +1,153 @@
// Type definitions for http-auth 4.1
// Project: https://github.com/http-auth/http-auth
// Definitions by: nokazn <https://github.com/nokazn>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 4.0
/// <reference types="node" />
import * as http from 'http';
import { EventEmitter } from 'events';
type Qop = 'auth' | 'none';
type Algorithm = 'MD5' | 'MD5-sess';
type CheckedRequestListener = (req: http.IncomingMessage & { user?: string }, res: http.ServerResponse) => void;
type BasicChecker = (username: string, password: string, callback: (isAuthorized: boolean) => void) => void;
type DigestChecker = (username: string, callback: (hash?: string) => void) => void;
interface BasicResult<T extends boolean = boolean> {
user?: string;
pass?: T;
}
type DigestResult<T extends boolean = boolean> = BasicResult<T> & {
stale?: true;
};
type ResultEmitter = (result: BasicResult | DigestResult | Error) => void;
type Nonce = [string, number, number];
interface ClientOptions {
username: string;
realm: string;
nonce: string;
uri: string;
algorithm: Algorithm;
response: string;
qop?: 'auth';
nc?: string;
cnonce?: string;
}
interface BasicOptions {
/**
* Authentication realm, by default it is 'users'.
* @default 'users'
*/
realm?: string;
/**
* File where user details are stored.
* - Line format is {user:pass} or {user:passHash} for basic access.
* - Line format is {user:realm:passHash} for digest access.
*/
file?: string;
/**
* Message for failed authentication 401 page.
* @default '401 Unauthorized'
*/
msg401?: string;
/**
* Message for failed authentication 407 page.
* @default '407 Proxy authentication required
*/
msg407?: string;
/**
* Content type for failed authentication page.
* @default 'text/plain'
*/
contentType?: string;
/**
* Set this to true, if you want to use it with http-proxy (https://github.com/http-party/node-http-proxy).
* @default false
*/
proxy?: boolean;
/**
* Set this to true, if you don't want req.user to be filled with authentication info.
*/
skipUser?: boolean;
}
type DigestOptions = BasicOptions & {
/**
* Quality of protection that is used only for digest access authentication
* - 'auth' is set by default.
* - 'none' this option is disabling protection.
* @default 'auth
*/
qop?: Qop;
/**
* Algorithm that will be used only for digest access authentication.
* 'MD5' or 'MD5-sess' can be set.
* @default 'MD5'
*/
algorithm?: Algorithm;
};
declare abstract class Base extends EventEmitter {
constructor(options: BasicOptions, checker?: BasicChecker | DigestChecker);
on(event: 'success', callback: (result: BasicResult<true> | DigestResult<true>) => void): this;
on(event: 'fail', callback: (result: BasicResult<false> | DigestResult<false>) => void): this;
on(event: 'error', callback: (err: Error) => void): this;
check(callback?: CheckedRequestListener): CheckedRequestListener;
abstract processLine(userLine: string): void;
abstract parseAuthorization(header: string): string | ClientOptions | undefined;
abstract findUser(
req: http.IncomingMessage,
hashOrClientOptions: string | ClientOptions,
callback: ResultEmitter,
): void;
abstract generateHeader(result?: DigestResult): string;
private ask(res: http.ServerResponse, result: BasicResult | DigestResult): void;
private isAuthenticated(req: http.IncomingMessage, callback: ResultEmitter): void;
private loadUsers(): void;
}
declare class Basic extends Base {
constructor(options: BasicOptions, checker?: BasicChecker);
private validate(hash: string, password: string): boolean;
processLine(userLine: string): void;
generateHeader(): string;
parseAuthorization(header: string): string | undefined;
findUser(req: http.IncomingMessage, hash: string, callback: ResultEmitter): void;
}
declare class Digest extends Base {
constructor(options: DigestOptions, checker?: DigestChecker);
private nonces: Nonce[];
private validate(ha2: string, clientOptions: ClientOptions, hash: string): boolean;
private removeNonces(nonces: Nonce[]): void;
private validateNonce(nonce: string, qop: Qop, nc: string): boolean;
private askNonce(): string;
processLine(userLine: string): void;
generateHeader(result: DigestResult): string;
parseAuthorization(header: string): string | undefined;
findUser(req: http.IncomingMessage, clientOptions: ClientOptions, callback: ResultEmitter): void;
}
declare function basic(options: BasicOptions, checker?: BasicChecker): Basic;
declare function digest(options: DigestOptions, checker?: DigestChecker): Digest;
export { basic, digest, BasicOptions, DigestOptions, BasicChecker, DigestChecker };

View File

@ -0,0 +1,20 @@
import * as http from 'http';
import * as auth from 'http-auth';
const basic = auth.basic({
realm: 'Simon Area.',
file: __dirname + '/../data/users.htpasswd', // gevorg:gpass, Sarah:testpass
});
const server = http.createServer();
const a = auth.digest({});
a.check();
// Creating new HTTP server.
http.createServer(
basic.check((req, res) => {
res.end(`Welcome to private area - ${req.user}!`);
}),
).listen(1337, () => {
// Log URL.
console.log('Server running at http://127.0.0.1:1337/');
});

View File

@ -0,0 +1,22 @@
import * as http from 'http';
import * as auth from 'http-auth';
const basic = auth.basic(
{
realm: 'Simon Area.',
},
(username, password, callback) => {
// Custom authentication method.
callback(username === 'Tina' && password === 'Bullock');
},
);
// Creating new HTTP server.
http.createServer(
basic.check((req, res) => {
res.end(`Welcome to private area - ${req.user}!`);
}),
).listen(1337, () => {
// Log URL.
console.log('Server running at http://127.0.0.1:1337/');
});

View File

@ -0,0 +1,35 @@
import * as http from 'http';
import * as auth from 'http-auth';
import * as crypto from 'crypto';
const md5 = (input: string) => {
const hash = crypto.createHash('MD5');
hash.update(input);
return hash.digest('hex');
};
const digest = auth.digest(
{
realm: 'Simon Area.',
},
(username, callback) => {
// Expecting md5(username:realm:password) in callback.
if (username === 'simon') {
callback(md5('simon:Simon Area.:smart'));
} else if (username === 'tigran') {
callback(md5('tigran:Simon Area.:great'));
} else {
callback();
}
},
);
// Creating new HTTP server.
http.createServer(
digest.check((req, res) => {
res.end(`Welcome to private area - ${req.user}!`);
}),
).listen(1337, () => {
// Log URL.
console.log('Server running at http://127.0.0.1:1337/');
});

View File

@ -0,0 +1,17 @@
import * as http from 'http';
import * as auth from 'http-auth';
const digest = auth.digest({
realm: 'Simon Area.',
file: __dirname + '/../data/users.htdigest', // vivi:anna, sona:testpass
});
// Creating new HTTP server.
http.createServer(
digest.check((req, res) => {
res.end(`Welcome to private area - ${req.user}!`);
}),
).listen(1337, () => {
// Log URL.
console.log('Server running at http://127.0.0.1:1337/');
});

View File

@ -0,0 +1,30 @@
import * as http from 'http';
import * as auth from 'http-auth';
const basic = auth.basic({
realm: 'Simon Area.',
file: __dirname + '/../data/users.htpasswd', // gevorg:gpass, Sarah:testpass
});
// Adding event listeners.
basic.on('success', result => {
console.log(`User authenticated: ${result.user}`);
});
basic.on('fail', result => {
console.log(`User authentication failed: ${result.user}`);
});
basic.on('error', error => {
console.log(`Authentication error: ${error.message}`);
});
// Creating new HTTP server.
http.createServer(
basic.check((req, res) => {
res.end(`Welcome to private area - ${req.user}!`);
}),
).listen(1337, () => {
// Log URL.
console.log('Server running at http://127.0.0.1:1337/');
});

View File

@ -0,0 +1,27 @@
import * as https from 'https';
import * as fs from 'fs';
import * as auth from 'http-auth';
const basic = auth.basic({
realm: 'Simon Area.',
file: __dirname + '/../data/users.htpasswd', // gevorg:gpass, Sarah:testpass
});
// HTTPS server options.
const options = {
key: fs.readFileSync(__dirname + '/../data/server.key'),
cert: fs.readFileSync(__dirname + '/../data/server.crt'),
};
// Starting server.
https
.createServer(
options,
basic.check((req, res) => {
res.end(`Welcome to private area - ${req.user}!`);
}),
)
.listen(1337, () => {
// Log URL.
console.log('Server running at https://127.0.0.1:1337/');
});

View File

@ -0,0 +1,29 @@
import * as http from 'http';
import * as fs from 'fs';
import * as auth from 'http-auth';
import * as httpProxy from 'http-proxy';
const basic = auth.basic({
realm: 'Simon Area.',
file: __dirname + '/../data/users.htpasswd', // gevorg:gpass, Sarah:testpass
proxy: true,
});
// Create your proxy server.
const proxy = httpProxy.createProxyServer({});
http.createServer(
basic.check((req, res) => {
proxy.web(req, res, { target: 'http://127.0.0.1:1338' });
}),
).listen(1337);
// Create your target server.
http.createServer((req, res) => {
res.end('Request successfully proxied!');
}).listen(1338, () => {
// Log URL.
console.log('Server running at http://127.0.0.1:1338/');
});
// You can test proxy authentication using curl.
// $ curl -x 127.0.0.1:1337 127.0.0.1:1337 -U gevorg

View File

@ -0,0 +1,25 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": ["es6"],
"noImplicitAny": true,
"noImplicitThis": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"baseUrl": "../",
"typeRoots": ["../"],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"test/http-auth-tests.basic.ts",
"test/http-auth-tests.basic_nofile.ts",
"test/http-auth-tests.digest.ts",
"test/http-auth-tests.digest_nofile.ts",
"test/http-auth-tests.events.ts",
"test/http-auth-tests.https.ts",
"test/http-auth-tests.proxy.ts"
]
}

View File

@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }