diff --git a/types/hapi__hawk/OTHER_FILES.txt b/types/hapi__hawk/OTHER_FILES.txt new file mode 100644 index 0000000000..fb0da6b4d9 --- /dev/null +++ b/types/hapi__hawk/OTHER_FILES.txt @@ -0,0 +1 @@ +lib/browser.d.ts diff --git a/types/hapi__hawk/hapi__hawk-tests.ts b/types/hapi__hawk/hapi__hawk-tests.ts new file mode 100644 index 0000000000..59b22017d5 --- /dev/null +++ b/types/hapi__hawk/hapi__hawk-tests.ts @@ -0,0 +1,48 @@ +import * as Http from 'http'; +import * as Hawk from '@hapi/hawk'; +import * as request from 'request'; + +const credentialsFunc = (): Hawk.server.Credentials => { + const credentials: Hawk.server.Credentials = { + key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', + algorithm: 'sha256', + user: 'Steve', + }; + + return credentials; +}; + +Http.createServer(async (req, res) => { + const { credentials, artifacts } = await Hawk.server.authenticate(req, credentialsFunc); + const payload = `Hello ${credentials.user} ${artifacts.ext}`; + const status = 200; + + const headers: Record = { 'Content-Type': 'text/plain' }; + const header = Hawk.server.header(credentials, artifacts, { payload, contentType: headers['Content-Type'] }); + headers['Server-Authorization'] = header; + + res.writeHead(status, headers); + res.end(payload); +}); + +const credentials: Hawk.client.Credentials = { + id: 'dh37fgj492je', + key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', + algorithm: 'sha256', +}; + +const requestOptions: request.OptionsWithUri = { + uri: 'http://example.com:8000/resource/1?b=1&a=2', + method: 'GET', + headers: {}, +}; + +const header = Hawk.client.header('http://example.com:8000/resource/1?b=1&a=2', 'GET', { + credentials, + ext: 'some-app-data', +}); +requestOptions.headers!.Authorization = header; + +request(requestOptions, (_error, response, body) => { + Hawk.client.authenticate(response, credentials, header.artifacts, { payload: body }); +}); diff --git a/types/hapi__hawk/index.d.ts b/types/hapi__hawk/index.d.ts new file mode 100755 index 0000000000..80ea4133c4 --- /dev/null +++ b/types/hapi__hawk/index.d.ts @@ -0,0 +1,22 @@ +// Type definitions for @hapi/hawk 8.0 +// Project: https://github.com/hapijs/hawk/ +// Definitions by: Florian Keller +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/// + +import * as sntp from '@hapi/sntp'; +import * as server from './lib/server'; +import * as client from './lib/client'; +import * as crypto from './lib/crypto'; +import * as utils from './lib/utils'; +import * as plugin from './lib/plugin'; + +export namespace uri { + const authenticate: typeof server.authenticateBewit; + const getBewit: typeof client.getBewit; +} + +export { sntp, server, client, crypto, utils, plugin }; + +export as namespace hawk; diff --git a/types/hapi__hawk/lib/browser.d.ts b/types/hapi__hawk/lib/browser.d.ts new file mode 100755 index 0000000000..b397d5722d --- /dev/null +++ b/types/hapi__hawk/lib/browser.d.ts @@ -0,0 +1,62 @@ +import CryptoJS = require('crypto-js'); +import * as Crypto from './crypto'; +import * as Client from './client'; + +declare namespace hawk { + namespace client { + function authenticate( + request: XMLHttpRequest | Response, + credentials: Client.Credentials, + artifacts: Crypto.Artifacts, + options?: Client.AuthenticateOptions, + ): boolean; + function authenticateTimestamp( + message: string, + credentials: Client.Credentials, + updateClock?: boolean, + ): boolean; + function bewit(uri: string, options?: Client.BewitOptions): string; + function header(uri: string | utils.ParsedUri, method: string, options?: Client.HeaderOptions): Client.Header; + function message(host: string, port: number, message: string, options: Client.MessageOptions): Client.Message; + } + + namespace crypto { + const headerVersion: string; + const algorithms: string[]; + + function calculateMac(type: string, credentials: Client.Credentials, options: Crypto.Artifacts): string; + function calculatePayloadHash(payload: string, algorithm: string, contentType: string): string; + function calculateTsMac(ts: string, credentials: Client.Credentials): string; + function generateNormalizedString(type: string, options: Crypto.Artifacts): string; + } + + namespace utils { + interface ParsedUri { + host: string; + port: string; + resource: string; + } + + const storage: Storage; + /** `scheme://credentials@host:port/resource#fragment` */ + const uriRegex: RegExp; + + function base64urlEncode(value: string | Buffer, encoding?: BufferEncoding): string; + function escapeHeaderAttribute(attribute: string): string; + function getNtpSecOffset(): number; + function now(localtimeOffsetMsec?: number): number; + function nowSec(localtimeOffsetMsec?: number): number; + function parseAuthorizationHeader(header: string, keys?: string[]): Record; + function parseContentType(header?: string): string; + function parseUri(input: string): ParsedUri; + function randomString(size: number): string; + function setNtpSecOffset(offset: number): void; + function setStorage(storage: Storage): void; + } + + namespace crypto { + const utils: typeof CryptoJS; + } +} + +export = hawk; diff --git a/types/hapi__hawk/lib/client.d.ts b/types/hapi__hawk/lib/client.d.ts new file mode 100755 index 0000000000..943dd975ff --- /dev/null +++ b/types/hapi__hawk/lib/client.d.ts @@ -0,0 +1,88 @@ +import * as Crypto from './crypto'; +import * as http from 'http'; +import * as request from 'request'; + +export interface Credentials { + algorithm: 'sha1' | 'sha256'; + id: string; + key: string; +} + +export interface HeaderOptions { + /** Oz application id */ + app?: string; + /** Payload content-type (ignored if hash provided) */ + contentType?: string; + credentials: Credentials; + /** Oz delegated-by application id */ + dlg?: string; + /** Application specific data sent via the ext attribute */ + ext?: string; + /** Pre-calculated payload hash */ + hash?: string; + /** Time offset to sync with server time (ignored if timestamp provided) */ + localtimeOffsetMsec?: number; + /** A pre-generated nonce */ + nonce?: string; + /** UTF-8 encoded string for body hash generation (ignored if hash provided) */ + payload?: string; + /** A pre-calculated timestamp in seconds */ + timestamp?: number; +} + +export interface Header { + header: string; + artifacts: Crypto.Artifacts; +} + +export interface AuthenticateOptions { + /** optional payload received */ + payload?: string; + /** specifies if a Server-Authorization header is required. Defaults to 'false' */ + required?: boolean; +} + +export interface Authentication { + headers: Record; +} + +export interface BewitOptions { + credentials: Credentials; + /** Application specific data sent via the ext attribute */ + ext?: string; + /** Time offset to sync with server time */ + localtimeOffsetMsec: number; + /** TTL in seconds */ + ttlSec: number; +} + +export interface MessageOptions { + credentials: Credentials; + /** Time offset to sync with server time (ignored if timestamp provided) */ + localtimeOffsetMsec: number; + /** A pre-generated nonce */ + nonce: string; + /** A pre-calculated timestamp in seconds */ + timestamp: number; +} + +export interface Message { + hash: string; + id: string; + mac: string; + nonce: string; + ts: string; +} + +export function authenticate( + res: http.IncomingMessage | request.Response, + credentials: Credentials, + artifacts: Crypto.Artifacts, + options?: AuthenticateOptions, +): Authentication; + +export function getBewit(uri: string, options: BewitOptions): string; + +export function header(uri: string, method: string, options?: HeaderOptions): Header; + +export function message(host: string, port: number, message: string, options?: MessageOptions): Message; diff --git a/types/hapi__hawk/lib/crypto.d.ts b/types/hapi__hawk/lib/crypto.d.ts new file mode 100755 index 0000000000..c01405bb9b --- /dev/null +++ b/types/hapi__hawk/lib/crypto.d.ts @@ -0,0 +1,38 @@ +import * as Crypto from 'crypto'; +import * as Client from './client'; + +export interface Artifacts { + app?: string; + dlg?: string; + ext?: string; + hash?: string; + host: string; + method: string; + nonce: string; + port: number; + resource: string; + ts: string; +} + +export interface TimestampMessage { + ts: number; + tsm: string; +} + +export const headerVersion: string; + +export const algorithms: string[]; + +export function calculateMac(type: string, credentials: Client.Credentials, options: Artifacts): string; + +export function generateNormalizedString(type: string, options: Artifacts): string; + +export function calculatePayloadHash(payload: string, algorithm: string, contentType: string): string; + +export function initializePayloadHash(algorithm: string, contentType: string): string; + +export function finalizePayloadHash(hash: Crypto.Hash): string; + +export function calculateTsMac(ts: string, credentials: Client.Credentials): string; + +export function timestampMessage(credentials: Client.Credentials, localtimeOffsetMsec: number): TimestampMessage; diff --git a/types/hapi__hawk/lib/plugin.d.ts b/types/hapi__hawk/lib/plugin.d.ts new file mode 100755 index 0000000000..6a03460d1f --- /dev/null +++ b/types/hapi__hawk/lib/plugin.d.ts @@ -0,0 +1,7 @@ +import * as Hapi from '@hapi/hapi'; + +export namespace plugin { + const pkg: Record; + const requirements: Record; + function register(server: Hapi.Server): void; +} diff --git a/types/hapi__hawk/lib/server.d.ts b/types/hapi__hawk/lib/server.d.ts new file mode 100755 index 0000000000..afe46d6158 --- /dev/null +++ b/types/hapi__hawk/lib/server.d.ts @@ -0,0 +1,131 @@ +import * as http from 'http'; +import * as request from 'request'; + +import * as Crypto from './crypto'; +import * as Utils from './utils'; +import { Message } from './client'; + +export type CredentialsFunc = (id: string) => Promise | Credentials; +export type NonceFunc = (key: string, nonce: string, ts: string) => Promise | void; + +export interface AuthenticateOptions { + /** + * optional header field name, used to override the default 'Host' header when used + * behind a cache of a proxy. Apache2 changes the value of the 'Host' header while preserving + * the original (which is what the module must verify) in the 'x-forwarded-host' header field. + * Only used when passed a node `http.ServerRequest` object. + */ + hostHeaderName?: string; + /** + * optional nonce validation function. The function signature is `async function(key, nonce, ts)` + * and it must return no value for success or throw an error for invalid state. + */ + nonceFunc?: NonceFunc; + /** + * optional number of seconds of permitted clock skew for incoming timestamps. Defaults to 60 seconds. + * Provides a +/- skew which means actual allowed window is double the number of seconds. + */ + timestampSkewSec?: number; + /** + * Optional local clock time offset express in a number of milliseconds (positive or negative). + * Defaults to 0. + */ + localtimeOffsetMsec?: number; + /** + * optional payload for validation. The client calculates the hash value and includes it via the 'hash' + * header attribute. The server always ensures the value provided has been included in the request + * MAC. When this option is provided, it validates the hash value itself. Validation is done by calculating + * a hash value over the entire payload (assuming it has already be normalized to the same format and + * encoding used by the client to calculate the hash on request). If the payload is not available at the time + * of authentication, the `authenticatePayload()` method can be used by passing it the credentials and + * `attributes.hash` returned from `authenticate()`. + */ + payload?: string; + /** + * optional host name override. Only used when passed a node request object. + */ + host?: string; + /** + * optional port override. Only used when passed a node request object. + */ + port?: number; +} + +export interface Credentials { + algorithm: 'sha1' | 'sha256'; + key: string; + user: string; +} + +export interface Authentication { + artifacts: Crypto.Artifacts; + credentials: Credentials; +} + +export interface HeaderOptions { + /** Payload content-type (ignored if hash provided) */ + contentType?: string; + /** Application specific data sent via the ext attribute */ + ext?: string; + /** Pre-calculated payload hash */ + hash?: string; + /** UTF-8 encoded string for body hash generation (ignored if hash provided) */ + payload?: string; +} + +export type AuthenticateBewitOptions = Pick< + AuthenticateOptions, + 'hostHeaderName' | 'localtimeOffsetMsec' | 'host' | 'port' +>; + +export interface Bewit { + id: string; + exp: string; + mac: string; + ext: string; +} + +export interface AuthenticatedBewit extends AuthenticatedMessage { + bewit: Bewit; +} + +export interface AuthenticatedMessage { + credentials: Credentials; +} + +export type AuthenticateMessageOptions = Pick< + AuthenticateOptions, + 'nonceFunc' | 'timestampSkewSec' | 'localtimeOffsetMsec' +>; + +export function authenticate( + req: http.IncomingMessage, + credentialsFunc: CredentialsFunc, + options?: AuthenticateOptions, +): Promise; + +export function authenticateBewit( + req: http.IncomingMessage, + credentialsFunc: CredentialsFunc, + options?: AuthenticateBewitOptions, +): AuthenticatedBewit; + +export function authenticateMessage( + host: string, + port: number, + message: string, + authorization: Message, + credentialsFunc: CredentialsFunc, + options: AuthenticateMessageOptions, +): Promise; + +export function authenticatePayload( + payload: string, + credentials: Credentials, + artifacts: Crypto.Artifacts, + contentType: string, +): void; + +export function authenticatePayloadHash(calculatedHash: string, artifacts: Crypto.Artifacts): void; + +export function header(credentials: Credentials, artifacts: Crypto.Artifacts, options?: HeaderOptions): string; diff --git a/types/hapi__hawk/lib/utils.d.ts b/types/hapi__hawk/lib/utils.d.ts new file mode 100755 index 0000000000..345d7463de --- /dev/null +++ b/types/hapi__hawk/lib/utils.d.ts @@ -0,0 +1,51 @@ +import * as Boom from '@hapi/boom'; +import * as http from 'http'; +import * as https from 'https'; + +export interface Host { + name: string; + port: number; +} + +export interface CustomRequest { + authorization: string; + contentType: string; + host: string; + method: string; + port: number; + url: string; +} + +export interface ParseRequestOptions { + host?: string; + hostHeaderName?: string; + name?: string; + port?: number; +} + +export const version: string; + +export const limits: { + /** Limit the length of uris and headers to avoid a DoS attack on string matching */ + maxMatchLength: number; +}; + +export function now(localtimeOffsetMsec: number): number; + +export function nowSecs(localtimeOffsetMsec: number): number; + +export function parseAuthorizationHeader(header: string, keys?: string[]): Record; + +export function parseContentType(header?: string): string; + +export function parseHost(req: http.RequestOptions | https.RequestOptions, hostHeaderName?: string): Host | null; + +export function parseRequest( + req: http.RequestOptions | https.RequestOptions, + options?: ParseRequestOptions, +): CustomRequest; + +export function unauthorized( + message?: string, + attributes?: string | Boom.unauthorized.Attributes, +): Boom.Boom & Boom.unauthorized.MissingAuth; diff --git a/types/hapi__hawk/package.json b/types/hapi__hawk/package.json new file mode 100644 index 0000000000..8859777600 --- /dev/null +++ b/types/hapi__hawk/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "dependencies": { + "@hapi/boom": "^9.0.0" + } +} diff --git a/types/hapi__hawk/tsconfig.json b/types/hapi__hawk/tsconfig.json new file mode 100644 index 0000000000..3dd314a82e --- /dev/null +++ b/types/hapi__hawk/tsconfig.json @@ -0,0 +1,59 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "dom", + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "baseUrl": "../", + "paths": { + "@hapi/basic": [ + "hapi__basic" + ], + "@hapi/boom": [ + "hapi__boom" + ], + "@hapi/catbox": [ + "hapi__catbox" + ], + "@hapi/hapi": [ + "hapi__hapi" + ], + "@hapi/hawk": [ + "hapi__hawk" + ], + "@hapi/iron": [ + "hapi__iron" + ], + "@hapi/joi": [ + "hapi__joi" + ], + "@hapi/mimos": [ + "hapi__mimos" + ], + "@hapi/podium": [ + "hapi__podium" + ], + "@hapi/shot": [ + "hapi__shot" + ], + "@hapi/sntp": [ + "hapi__sntp" + ] + }, + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "hapi__hawk-tests.ts" + ] +} diff --git a/types/hapi__hawk/tslint.json b/types/hapi__hawk/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/hapi__hawk/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" }