|
| 1 | +/// <reference types="express" /> |
| 2 | + |
| 3 | +import { Request, RequestHandler } from 'express' |
| 4 | + |
| 5 | +/** |
| 6 | + * This is the middleware builder. |
| 7 | + * |
| 8 | + * Example: |
| 9 | + * const users = { alice: '1234', bob: 'correcthorsebatterystaple' } |
| 10 | + * app.use(basicAuth({ users, challenge: true }), myHandler) |
| 11 | + * |
| 12 | + * @param options The middleware's options (at least 'users' or 'authorizer' are mandatory). |
| 13 | + */ |
| 14 | +function expressBasicAuth(options: expressBasicAuth.BasicAuthMiddlewareOptions): RequestHandler |
| 15 | + |
| 16 | +namespace expressBasicAuth { |
| 17 | + /** |
| 18 | + * The configuration you pass to the middleware can take three forms, either: |
| 19 | + * - A map of static users ({ bob: 'pa$$w0rd', ... }) ; |
| 20 | + * - An authorizer function |
| 21 | + * - An asynchronous authorizer function |
| 22 | + */ |
| 23 | + export type BasicAuthMiddlewareOptions = IUsersOptions | (IAuthorizerOptions | IAsyncAuthorizerOptions) |
| 24 | + |
| 25 | + /** |
| 26 | + * express-basic-auth patches the request object to set an `auth` property that lets you retrieve the authed user. |
| 27 | + * |
| 28 | + * Example (TypeScript): |
| 29 | + * app.use(basicAuth({ ... }), (req: basicAuth.IBasicAuthedRequest, res, next) => { |
| 30 | + * res.end(`Welcome ${req.auth.user} (your password is ${req.auth.password})`) |
| 31 | + * next() |
| 32 | + * }) |
| 33 | + */ |
| 34 | + export interface IBasicAuthedRequest extends Request { |
| 35 | + auth: { user: string, password: string } |
| 36 | + } |
| 37 | + |
| 38 | + type Authorizer = (username: string, password: string) => boolean |
| 39 | + |
| 40 | + type AsyncAuthorizerCallback = (err: any, authed?: boolean) => void |
| 41 | + |
| 42 | + type AsyncAuthorizer = (username: string, password: string, callback: AsyncAuthorizerCallback) => void |
| 43 | + |
| 44 | + type ValueOrFunction<T> = T | ((req: IBasicAuthedRequest) => T) |
| 45 | + |
| 46 | + interface IBaseOptions { |
| 47 | + /** |
| 48 | + * Per default the middleware will not add a WWW-Authenticate challenge header to responses of unauthorized requests. |
| 49 | + * You can enable that by setting this to true, causing most browsers to show a popup to enter credentials |
| 50 | + * on unauthorized responses. |
| 51 | + * |
| 52 | + * @default false |
| 53 | + */ |
| 54 | + challenge?: boolean |
| 55 | + |
| 56 | + /** |
| 57 | + * You can set the realm (the realm identifies the system to authenticate against and can be used by clients to |
| 58 | + * save credentials) of the challenge by passing a string or a function that gets passed the request and is |
| 59 | + * expected to return the realm. |
| 60 | + * |
| 61 | + * @default undefined |
| 62 | + */ |
| 63 | + realm?: ValueOrFunction<string> |
| 64 | + |
| 65 | + /** |
| 66 | + * Per default, the response body for unauthorized responses will be empty. |
| 67 | + * It can be configured using the unauthorizedResponse option. You can either pass a static response or a |
| 68 | + * function that gets passed the express request object and is expected to return the response body. |
| 69 | + * If the response body is a string, it will be used as-is, otherwise it will be sent as JSON. |
| 70 | + * |
| 71 | + * @default '' |
| 72 | + */ |
| 73 | + unauthorizedResponse?: ValueOrFunction<any> |
| 74 | + } |
| 75 | + |
| 76 | + interface IUsersOptions extends IBaseOptions { |
| 77 | + /** |
| 78 | + * If you simply want to check basic auth against one or multiple static credentials, you can pass those |
| 79 | + * credentials in the users option. |
| 80 | + * |
| 81 | + * Example: |
| 82 | + * const users = { alice: '1234', bob: 'correcthorsebatterystaple' } |
| 83 | + * app.use(basicAuth({ users, challenge: true }), myHandler) |
| 84 | + */ |
| 85 | + users: { [username: string]: string } |
| 86 | + } |
| 87 | + |
| 88 | + interface IAuthorizerOptions extends IBaseOptions { |
| 89 | + /** |
| 90 | + * Set to true if your authorizer is asynchronous. |
| 91 | + */ |
| 92 | + authorizeAsync?: false |
| 93 | + |
| 94 | + /** |
| 95 | + * You can pass your own authorizer function, to check the credentials however you want. |
| 96 | + * It will be called with a username and password and is expected to return true or false to indicate that the |
| 97 | + * credentials were approved or not: |
| 98 | + * |
| 99 | + * Example: |
| 100 | + * app.use(basicAuth({ authorizer })) |
| 101 | + * |
| 102 | + * function myAuthorizer(username: string, password: string) { |
| 103 | + * return username.startsWith('A') && password.startsWith('secret'); |
| 104 | + * } |
| 105 | + * |
| 106 | + * This will authorize all requests with credentials where the username begins with 'A' and the password begins |
| 107 | + * with 'secret'. In an actual application you would likely look up some data instead ;-) |
| 108 | + */ |
| 109 | + authorizer: Authorizer |
| 110 | + } |
| 111 | + |
| 112 | + interface IAsyncAuthorizerOptions extends IBaseOptions { |
| 113 | + /** |
| 114 | + * Set it to true to use a asynchronous authorizer. |
| 115 | + */ |
| 116 | + authorizeAsync: true |
| 117 | + |
| 118 | + /** |
| 119 | + * You can pass an asynchronous authorizer. It will be passed a callback as the third parameter, which is |
| 120 | + * expected to be called by standard node convention with an error and a boolean to indicate if the credentials |
| 121 | + * have been approved or not. |
| 122 | + * |
| 123 | + * Example: |
| 124 | + * app.use(basicAuth({ authorizer, authorizeAsync: true })); |
| 125 | + * |
| 126 | + * function authorizer(username, password, authorize) { |
| 127 | + * if(username.startsWith('A') && password.startsWith('secret')) |
| 128 | + * return authorize(null, true) |
| 129 | + * |
| 130 | + * return authorize(null, false) |
| 131 | + * } |
| 132 | + */ |
| 133 | + authorizer: AsyncAuthorizer |
| 134 | + } |
| 135 | +} |
| 136 | + |
| 137 | +export = expressBasicAuth |
0 commit comments