Skip to content

Commit 2310f82

Browse files
Morgan Touverey-QuillingLionC
authored andcommitted
✨ Add TypeScript support
* Add TypeScript types declaration file * Reference declaration file in package.json * Update README.md to give some hints about usage in TypeScript * 📝 Remove semicolons from the documentation examples * 🎨 Remove semicolons from typescript definitions
1 parent 170d5fd commit 2310f82

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[![npm version](https://badge.fury.io/js/express-basic-auth.svg)](https://badge.fury.io/js/express-basic-auth)
44
[![npm](https://img.shields.io/npm/dm/express-basic-auth.svg)]()
55
[![David](https://img.shields.io/david/strongloop/express.svg)]()
6+
![TypeScript compatible](https://img.shields.io/badge/typescript-compatible-brightgreen.svg)
67
[![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://opensource.org/licenses/mit-license.php)
78

89
Simple plug & play HTTP basic auth middleware for Express.
@@ -154,6 +155,47 @@ node example.js
154155
This will start a small express server listening at port 8080. Just look at the file,
155156
try out the requests and play around with the options.
156157

158+
## TypeScript usage
159+
160+
A declaration file is bundled with the library. You don't have to install a `@types/` package.
161+
162+
```typescript
163+
import * as basicAuth from 'express-basic-auth'
164+
```
165+
166+
:bulb: **Using `req.auth`**
167+
168+
express-basic-auth sets `req.auth` to an object containing the authorized credentials like `{ user: 'admin', password: 'supersecret' }`.
169+
170+
In order to use that `req.auth` property in TypeScript without an unknown property error, use covariance to downcast the request type:
171+
172+
```typescript
173+
app.use(basicAuth(options), (req: basicAuth.IBasicAuthedRequest, res, next) => {
174+
res.end(`Welcome ${req.auth.user} (your password is ${req.auth.password})`)
175+
next()
176+
})
177+
```
178+
179+
:bulb: **A note about type inference on synchronous authorizers**
180+
181+
Due to some TypeScript's type-system limitation, the arguments' type of the synchronous authorizers are not inferred.
182+
For example, on an asynchronous authorizer, the three arguments are correctly inferred:
183+
184+
```typescript
185+
basicAuth({
186+
authorizeAsync: true,
187+
authorizer: (user, password, authorize) => authorize(null, password == 'secret')
188+
})
189+
```
190+
191+
However, on a synchronous authorizer, you'll have to type the arguments yourself:
192+
193+
```typescript
194+
basicAuth({
195+
authorizer: (user: string, password: string) => (password == 'secret')
196+
})
197+
```
198+
157199
## Tests
158200

159201
The cases in the `example.js` are also used for automated testing. So if you want

express-basic-auth.d.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "1.0.2",
44
"description": "Plug & play basic auth middleware for express",
55
"main": "index.js",
6+
"types": "express-basic-auth.d.ts",
67
"scripts": {
78
"test": "mocha test.js"
89
},
@@ -25,6 +26,7 @@
2526
},
2627
"homepage": "https://github.com/LionC/express-basic-auth#readme",
2728
"dependencies": {
29+
"@types/express": ">=4.0.0",
2830
"basic-auth": "^1.0.4"
2931
},
3032
"devDependencies": {

0 commit comments

Comments
 (0)