Skip to content

Commit ef89ba3

Browse files
committed
Merge branch '2.x'
2 parents 0231da5 + dced996 commit ef89ba3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+3505
-3869
lines changed

.github/workflows/node.js.yml

Lines changed: 0 additions & 28 deletions
This file was deleted.

.github/workflows/npm-publish.yml

Lines changed: 0 additions & 33 deletions
This file was deleted.

README.md

Lines changed: 6 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -1,219 +1,17 @@
1-
# SafePlaces Authentication Service
1+
# SafePlaces Information Security
22

33
[![Build Status](https://github.com/Path-Check/safeplaces-auth/workflows/Node.js%20CI/badge.svg)](https://github.com/Path-Check/safeplaces-auth/actions?query=workflow%3A%22Node.js+CI%22)
4-
[![Coverage Status](https://coveralls.io/repos/github/Path-Check/safeplaces-auth/badge.svg?branch=master)](https://coveralls.io/github/Path-Check/safeplaces-auth?branch=master)
54

6-
The modular authentication service for the SafePlaces backend.
5+
The modular information security service for the SafePlaces backend.
76

87
```shell script
98
# Install using NPM
10-
npm install @pathcheck/safeplaces-auth@^1.0.0-alpha.5
9+
npm install @pathcheck/safeplaces-auth@^2.0.0-alpha.1
1110

1211
# Install using Yarn
13-
yarn add @pathcheck/safeplaces-auth@^1.0.0-alpha.5
12+
yarn add @pathcheck/safeplaces-auth@^2.0.0-alpha.1
1413
```
1514

16-
# Table of Contents
17-
* [Examples](#examples)
18-
* [Securing API endpoints](#securing-api-endpoints)
19-
* [Handling login requests](#handling-login-requests)
20-
* [Handling logout requests](#handling-logout-requests)
21-
* [Strategies](#strategies)
22-
* [Auth0](#auth0)
23-
* [Symmetric JWT](#symmetric-jwt)
24-
* [Dynamic strategy selection](#dynamic-strategy-selection)
25-
* [Debugging](#debugging)
15+
## API Specification
2616

27-
# Examples
28-
29-
## Securing API endpoints
30-
31-
The authentication service can be instantiated and attached to an
32-
existing Express application. The point or order at which you attach
33-
the service relative to other middleware affects the point at which
34-
it is invoked.
35-
36-
```javascript
37-
const auth = require('@pathcheck/safeplaces-auth');
38-
39-
// Instantiate a public key retrieval client.
40-
const pkClient = new auth.JWKSClient(
41-
// Pass the URL for the JWKS.
42-
`${process.env.AUTH0_BASE_URL}/.well-known/jwks.json`,
43-
);
44-
45-
// Instantiate a request verification strategy.
46-
const auth0Strategy = new auth.strategies.Auth0({
47-
jwksClient: pkClient,
48-
// The API audience is checked against the user's access token.
49-
apiAudience: process.env.AUTH0_API_AUDIENCE,
50-
});
51-
52-
// Instantiate a strategy enforcer.
53-
const enforcer = new auth.Enforcer({
54-
strategy: auth0Strategy,
55-
// Pass in an asynchronous database lookup function
56-
// that takes the user ID as the only argument.
57-
userGetter: id => User.findOne(id),
58-
});
59-
60-
// `app` is your Express application.
61-
// A middleware is attached to the app, and it intercepts every
62-
// request for verification. The argument to `enforcer.secure`
63-
// affects which routes are secured.
64-
enforcer.secure(app);
65-
```
66-
67-
For greater flexibility, you access the built-in `enforcer.handleRequest(req, res)`
68-
function to selectively handle requests under your own logic. Note that `enforcer`
69-
will continue to end the request with `403 Forbidden` if the request is unauthorized.
70-
71-
```javascript
72-
app.use((req, res, next) => {
73-
if (req.headers['X-Bypass-Login']) {
74-
return next();
75-
} else {
76-
return enforcer
77-
// Enforcer ends the request with a `403 Forbidden` if it is unauthorized,
78-
// meaning `next` will not be called unless the request is authorized.
79-
.handleRequest(req, res, next)
80-
.catch(err => next(err));
81-
}
82-
});
83-
```
84-
85-
For the most flexibility, you can use `enforcer.processRequest(req)` to only validate a request,
86-
allowing you to decide how to handle the validation result yourself, whether that be ending
87-
the request or ignoring the error.
88-
89-
```javascript
90-
app.use((req, res, next) => {
91-
enforcer
92-
.processRequest(req)
93-
// `.then` is called only if `processRequest` has
94-
// determined the request to be authorized.
95-
.then(note => {
96-
// `note` is an error encountered by `processRequest` that may be indicated in server logs
97-
// or in a request header. It was not enough to halt the request, but the server
98-
// may encounter an unexpected error if logic continues.
99-
console.log(note);
100-
next();
101-
})
102-
// Otherwise, an error describing the validation error
103-
// will be thrown, and you can decide what to do with it.
104-
.catch(err => res.status(403).send('Forbidden'));
105-
});
106-
```
107-
108-
## Handling login requests
109-
110-
```javascript
111-
// Instantiate a login handler.
112-
const loginHandler = new auth.handlers.Login({
113-
auth0: {
114-
baseUrl: process.env.AUTH0_BASE_URL,
115-
apiAudience: process.env.AUTH0_API_AUDIENCE,
116-
clientId: process.env.AUTH0_CLIENT_ID,
117-
clientSecret: process.env.AUTH0_CLIENT_SECRET,
118-
realm: process.env.AUTH0_REALM,
119-
},
120-
cookie: {
121-
// Enable/disable cookie attributes depending on environment.
122-
secure: process.env.NODE_ENV !== 'development',
123-
sameSite: process.env.BYPASS_SAME_SITE !== 'true',
124-
},
125-
});
126-
127-
// Handle all requests to the login endpoint.
128-
// Since we are passing around the `handle` function, make sure
129-
// to bind the handle function to its object.
130-
app.post('/auth/login', loginHandler.handle.bind(loginHandler));
131-
```
132-
133-
## Handling logout requests
134-
135-
```javascript
136-
// Instantiate a logout handler.
137-
const logoutHandler = new auth.handlers.Logout({
138-
redirect: 'https://example.com/logout-success/',
139-
cookie: {
140-
// Enable/disable cookie attributes depending on environment.
141-
secure: process.env.NODE_ENV !== 'development',
142-
sameSite: process.env.BYPASS_SAME_SITE !== 'true',
143-
},
144-
});
145-
146-
// Handle all requests to the logout endpoint.
147-
// Since we are passing around the `handle` function, make sure
148-
// to bind the handle function to its object.
149-
app.get('/auth/logout', logoutHandler.handle.bind(logoutHandler));
150-
```
151-
152-
# Strategies
153-
154-
**Supported strategies:**
155-
- Auth0 asymmetric JWT
156-
- Symmetric JWT
157-
158-
## Auth0
159-
160-
Validate the JSON Web Token by checking the signature with
161-
the retrieved public key, and validate the API audience.
162-
163-
```javascript
164-
const auth = require('@pathcheck/safeplaces-auth');
165-
166-
const pkClient = new auth.JWKSClient(
167-
`${process.env.AUTH0_BASE_URL}/.well-known/jwks.json`,
168-
);
169-
const auth0Strategy = new auth.strategies.Auth0({
170-
jwksClient: pkClient,
171-
apiAudience: process.env.AUTH0_API_AUDIENCE,
172-
});
173-
```
174-
175-
## Symmetric JWT
176-
177-
Validate the JSON Web Token by checking the signature with
178-
a fixed private key.
179-
180-
```javascript
181-
const auth = require('@pathcheck/safeplaces-auth');
182-
183-
const symJWTStrategy = new auth.strategies.SymJWT({
184-
algorithm: 'HS256',
185-
privateKey: process.env.JWT_SECRET,
186-
});
187-
```
188-
189-
## Dynamic strategy selection
190-
191-
You can also pass a function into the strategy parameter
192-
to dynamically select the strategy based on the request
193-
or some other variables.
194-
195-
The function should accept the request as the only argument
196-
and return the desired strategy or a promise resolving the
197-
desired strategy.
198-
199-
```javascript
200-
const enforcer = new auth.Enforcer({
201-
strategy: req => {
202-
console.log(req);
203-
// Check something in the request.
204-
// ...
205-
// Example conditional:
206-
if (process.env.NODE_ENV === 'development') {
207-
return symJWTStrategy;
208-
} else {
209-
return auth0Strategy;
210-
}
211-
},
212-
});
213-
```
214-
215-
# Debugging
216-
217-
To debug why the `enforcer` is rejecting a request, set the
218-
`AUTH_LOGGING` environment variable to `verbose`, and the request
219-
validation error will be logged every time.
17+
See [SafePlaces Information Security API](docs/api/README.md) for more information.

docs/api/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# SafePlaces Information Security API
2+
3+
The SafePlaces Information Security API serves 3 primary goals:
4+
5+
1. Determining and verifying the identity of a user
6+
2. Granting users access to resources based on their authorization level
7+
3. Managing the user pool and allowing users to perform self-service tasks
8+
9+
## Table of Contents
10+
11+
- [API Reference](reference.md)
12+
- [Multi-factor Authentication API](mfa/README.md)
13+
- [User Management API](users/README.md)
14+
- [Multi-factor Authentication Flow](mfa-flow.md)

docs/api/login.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Log in
2+
3+
Logs in and obtains an access token for a registered user.
4+
5+
```http request
6+
POST /auth/login
7+
```
8+
9+
### Examples
10+
11+
```json
12+
{
13+
"username": "[email protected]",
14+
"password": "xyz2020"
15+
}
16+
```
17+
18+
## Default Response
19+
20+
The access token was sent in the `Set-Cookie` response header. The cookie cannot be accessed by JavaScript.
21+
22+
The user ID was returned in the response body, and it can be used to call other endpoints
23+
to act on the created user.
24+
25+
```http request
26+
Status: 200 OK
27+
```
28+
29+
```json
30+
{
31+
"id": "a0f853d6-4c28-4384-9853-bec18293bfa9"
32+
}
33+
```
34+
35+
## Error Response
36+
37+
The `username` and `password` combination is wrong.
38+
39+
```http request
40+
Status: 401 Unauthorized
41+
```
42+
43+
```json
44+
{
45+
"error": "InvalidCredentials",
46+
"message": "Wrong username or password"
47+
}
48+
```
49+
50+
<br/>
51+
52+
Multi-factor authentication is required.
53+
54+
```http request
55+
Status: 401 Unauthorized
56+
```
57+
58+
```json
59+
{
60+
"error": "MFARequired",
61+
"message": "Multifactor authentication required",
62+
"mfa_token": "Fe26.2*82dcca*be8149..."
63+
}
64+
```
65+
66+
<br/>
67+
68+
The user was found not in the database.
69+
70+
```http request
71+
Status: 500 Internal Server Error
72+
```
73+
74+
```json
75+
{
76+
"error": "DBError",
77+
"message": "Unable to find user in DB"
78+
}
79+
```
80+
81+
<br/>
82+
83+
The access token returned by the identity provider is malformed.
84+
85+
```http request
86+
Status: 500 Internal Server Error
87+
```
88+
89+
```json
90+
{
91+
"error": "InternalServerError",
92+
"message": "Unable to decode access token"
93+
}
94+
```

0 commit comments

Comments
 (0)