@boxyhq/saml-jackson 0.1.5-beta.112 → 0.1.5-beta.117

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +91 -60
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,33 +1,36 @@
1
1
  # SAML Jackson (not fiction anymore)
2
-
2
+
3
3
  SAML service [SAML in a box from BoxyHQ]
4
-
4
+
5
5
  You need someone like Jules Winnfield to save you from the vagaries of SAML login.
6
-
6
+
7
7
  # Source code visualizer
8
+
8
9
  [CodeSee codebase visualizer](https://app.codesee.io/maps/public/53e91640-23b5-11ec-a724-79d7dd589517)
9
-
10
+
10
11
  # Getting Started
11
-
12
+
12
13
  There are two ways to use this repo.
14
+
13
15
  - As an npm library (for Express compatible frameworks)
14
16
  - As a separate service
15
-
17
+
16
18
  ## Install as an npm library
19
+
17
20
  Jackson is available as an [npm package](https://www.npmjs.com/package/@boxyhq/saml-jackson) that can be integrated into Express.js routes. The library should be usable with other node.js web application frameworks but is currently untested. Please file an issue or submit a PR if you encounter any issues.
18
-
21
+
19
22
  ```
20
23
  npm i @boxyhq/saml-jackson
21
24
  ```
22
-
25
+
23
26
  ### Add Express Routes
24
-
27
+
25
28
  ```
26
29
  // express
27
30
  const express = require('express');
28
31
  const router = express.Router();
29
32
  const cors = require('cors'); // needed if you are calling the token userinfo endpoints from the frontend
30
-
33
+
31
34
  // Set the required options. Refer to https://github.com/boxyhq/jackson#configuration for the full list
32
35
  const opts = {
33
36
  externalUrl: 'https://my-cool-app.com',
@@ -36,27 +39,30 @@ const opts = {
36
39
  db: {
37
40
  engine: 'mongo',
38
41
  url: 'mongodb://localhost:27017/my-cool-app',
39
- }
42
+ }
40
43
  };
41
-
44
+
45
+
46
+ let apiController;
47
+ let oauthController;
42
48
  // Please note that the initialization of @boxyhq/saml-jackson is async, you cannot run it at the top level
43
49
  // Run this in a function where you initialise the express server.
44
50
  async function init() {
45
51
  const ret = await require('@boxyhq/saml-jackson')(opts);
46
- const apiController = ret.apiController;
47
- const oauthController = ret.oauthController;
52
+ apiController = ret.apiController;
53
+ oauthController = ret.oauthController;
48
54
  }
49
-
55
+
50
56
  // express.js middlewares needed to parse json and x-www-form-urlencoded
51
57
  router.use(express.json());
52
58
  router.use(express.urlencoded({ extended: true }));
53
-
59
+
54
60
  // SAML config API. You should pass this route through your authentication checks, do not expose this on the public interface without proper authentication in place.
55
61
  router.post('/api/v1/saml/config', async (req, res) => {
56
62
  try {
57
63
  // apply your authentication flow (or ensure this route has passed through your auth middleware)
58
64
  ...
59
-
65
+
60
66
  // only when properly authenticated, call the config function
61
67
  res.json(await apiController.config(req.body));
62
68
  } catch (err) {
@@ -65,7 +71,7 @@ router.post('/api/v1/saml/config', async (req, res) => {
65
71
  });
66
72
  }
67
73
  });
68
-
74
+
69
75
  // OAuth 2.0 flow
70
76
  router.get('/oauth/authorize', async (req, res) => {
71
77
  try {
@@ -74,7 +80,7 @@ router.get('/oauth/authorize', async (req, res) => {
74
80
  res.status(500).send(err.message);
75
81
  }
76
82
  });
77
-
83
+
78
84
  router.post('/oauth/saml', async (req, res) => {
79
85
  try {
80
86
  await oauthController.samlResponse(req, res);
@@ -82,7 +88,7 @@ router.post('/oauth/saml', async (req, res) => {
82
88
  res.status(500).send(err.message);
83
89
  }
84
90
  });
85
-
91
+
86
92
  router.post('/oauth/token', cors(), async (req, res) => {
87
93
  try {
88
94
  await oauthController.token(req, res);
@@ -90,7 +96,7 @@ router.post('/oauth/token', cors(), async (req, res) => {
90
96
  res.status(500).send(err.message);
91
97
  }
92
98
  });
93
-
99
+
94
100
  router.get('/oauth/userinfo', cors(), async (req, res) => {
95
101
  try {
96
102
  await oauthController.userInfo(req, res);
@@ -98,15 +104,16 @@ router.get('/oauth/userinfo', cors(), async (req, res) => {
98
104
  res.status(500).send(err.message);
99
105
  }
100
106
  });
101
-
107
+
102
108
  // set the router
103
- app.user('/sso', router);
104
-
109
+ app.use('/sso', router);
110
+
105
111
  ```
106
-
112
+
107
113
  ## Deployment as a service: Docker
114
+
108
115
  The docker container can be found at [boxyhq/jackson](https://hub.docker.com/r/boxyhq/jackson/tags). It is preferable to use a specific version instead of the `latest` tag. Jackson uses two ports (configurable if needed, see below) 5000 and 6000. 6000 is the internal port and ideally should not be exposed to a public network.
109
-
116
+
110
117
  ```
111
118
  docker run -p 5000:5000 -p 6000:6000 boxyhq/jackson:78e9099d
112
119
  ```
@@ -116,17 +123,19 @@ Refer to https://github.com/boxyhq/jackson#configuration for the full configurat
116
123
  Kubernetes and docker-compose deployment files will be coming soon.
117
124
 
118
125
  ## Usage
119
-
126
+
120
127
  ### 1. Setting up SAML with your customer's Identity Provider
128
+
121
129
  Please follow the instructions [here](https://docs.google.com/document/d/1fk---Z9Ln59u-2toGKUkyO3BF6Dh3dscT2u4J2xHANE) to guide your customers in setting up SAML correctly for your product(s). You should create a copy of the doc and modify it with your custom settings, we have used the values that work for our demo apps.
122
-
130
+
123
131
  ### 2. SAML config API
132
+
124
133
  Once your customer has set up the SAML app on their Identity Provider, the Identity Provider will generate an IdP or SP metadata file. Some Identity Providers only generate an IdP metadata file but it usually works for the SP login flow as well. It is an XML file that contains various attributes Jackson needs to validate incoming SAML login requests. This step is the equivalent of setting an OAuth 2.0 app and generating a client ID and client secret that will be used in the login flow.
125
-
134
+
126
135
  You will need to provide a place in the UI for your customers (The account settings page is usually a good place for this) to configure this and then call the API below.
127
-
136
+
128
137
  The following API call sets up the configuration in Jackson:
129
-
138
+
130
139
  ```
131
140
  curl --location --request POST 'http://localhost:6000/api/v1/saml/config' \
132
141
  --header 'Content-Type: application/x-www-form-urlencoded' \
@@ -136,24 +145,27 @@ curl --location --request POST 'http://localhost:6000/api/v1/saml/config' \
136
145
  --data-urlencode 'tenant=boxyhq.com' \
137
146
  --data-urlencode 'product=demo'
138
147
  ```
139
-
148
+
140
149
  - rawMetadata: The XML metadata file your customer gets from their Identity Provider
141
150
  - defaultRedirectUrl: The redirect URL to use in the IdP login flow. Jackson will call this URL after completing an IdP login flow
142
151
  - redirectUrl: JSON encoded array containing a list of allowed redirect URLs. Jackson will disallow any redirects not on this list (or not the default URL above)
143
152
  - tenant: Jackson supports a multi-tenant architecture, this is a unique identifier you set from your side that relates back to your customer's tenant. This is normally an email, domain, an account id, or user-id
144
153
  - product: Jackson support multiple products, this is a unique identifier you set from your side that relates back to the product your customer is using
145
-
154
+
146
155
  The response returns a JSON with `client_id` and `client_secret` that can be stored against your tenant and product for a more secure OAuth 2.0 flow. If you do not want to store the `client_id` and `client_secret` you can alternatively use `client_id=tentant=<tenantID>&product=<productID>` and any arbitrary value for `client_secret` when setting up the OAuth 2.0 flow.
147
-
156
+
148
157
  ### 3. OAuth 2.0 Flow
158
+
149
159
  Jackson has been designed to abstract the SAML login flow as a pure OAuth 2.0 flow. This means it's compatible with any standard OAuth 2.0 library out there, both client-side and server-side. It is important to remember that SAML is configured per customer unlike OAuth 2.0 where you can have a single OAuth app supporting logins for all customers.
150
-
160
+
151
161
  Jackson also supports the PKCE authorization flow (https://oauth.net/2/pkce/), so you can protect your SPAs.
152
-
162
+
153
163
  If for any reason you need to implement the flow on your own, the steps are outlined below:
154
-
164
+
155
165
  ### 4. Authorize
166
+
156
167
  The OAuth flow begins with redirecting your user to the `authorize` URL:
168
+
157
169
  ```
158
170
  https://localhost:5000/oauth/authorize
159
171
  ?response_type=code&provider=saml
@@ -161,16 +173,18 @@ https://localhost:5000/oauth/authorize
161
173
  &redirect_uri=<redirect URL>
162
174
  &state=<randomly generated state id>
163
175
  ```
164
-
176
+
165
177
  - response_type=code: This is the only supported type for now but maybe extended in the future
166
178
  - client_id: Use the client_id returned by the SAML config API or use `tentant=<tenantID>&product=<productID>` to use the tenant and product IDs instead
167
179
  - redirect_uri: This is where the user will be taken back once the authorization flow is complete
168
180
  - state: Use a randomly generated string as the state, this will be echoed back as a query parameter when taking the user back to the `redirect_uri` above. You should validate the state to prevent XSRF attacks
169
-
181
+
170
182
  ### 5. Code Exchange
183
+
171
184
  After successful authorization, the user is redirected back to the `redirect_uri`. The query parameters will include the `code` and `state` parameters. You should validate that the state matches the one you sent in the `authorize` request.
172
-
185
+
173
186
  The code can then be exchanged for a token by making the following request:
187
+
174
188
  ```
175
189
  curl --request POST \
176
190
  --url 'http://localhost:5000/oauth/token' \
@@ -181,12 +195,14 @@ curl --request POST \
181
195
  --data 'redirect_uri=<redirect URL>' \
182
196
  --data code=<code from the query parameter above>
183
197
  ```
198
+
184
199
  - grant_type=authorization_code: This is the only supported flow, for now. We might extend this in the future
185
200
  - client_id: Use the client_id returned by the SAML config API or use `tentant=<tenantID>&product=<productID>` to use the tenant and product IDs instead
186
201
  - client_secret: Use the client_secret returned by the SAML config API or any arbitrary value if using the tenant and product in the clientID
187
202
  - redirect_uri: This is where the user will be taken back once the authorization flow is complete. Use the same redirect_uri as the previous request
188
-
203
+
189
204
  If everything goes well you should receive a JSON response that includes the access token. This token is needed for the next step where we fetch the user profile.
205
+
190
206
  ```
191
207
  {
192
208
  "access_token": <access token>,
@@ -194,17 +210,20 @@ If everything goes well you should receive a JSON response that includes the acc
194
210
  "expires_in": 300
195
211
  }
196
212
  ```
197
-
213
+
198
214
  ### 6. Profile Request
215
+
199
216
  The short-lived access token can now be used to request the user's profile. You'll need to make the following request:
217
+
200
218
  ```
201
219
  curl --request GET \
202
220
  --url https://localhost:5000/oauth/me \
203
221
  --header 'authorization: Bearer <access token>' \
204
222
  --header 'content-type: application/json'
205
223
  ```
206
-
224
+
207
225
  If everything goes well you should receive a JSON response with the user's profile:
226
+
208
227
  ```
209
228
  {
210
229
  "email": "sjackson@coolstartup.com",
@@ -213,53 +232,60 @@ If everything goes well you should receive a JSON response with the user's profi
213
232
  "lastName": "Jackson",
214
233
  }
215
234
  ```
216
-
235
+
217
236
  - email: The email address of the user as provided by the Identity Provider
218
237
  - id: The id of the user as provided by the Identity Provider
219
238
  - firstName: The first name of the user as provided by the Identity Provider
220
239
  - lastName: The last name of the user as provided by the Identity Provider
221
-
240
+
222
241
  ## Examples
242
+
223
243
  To Do
224
-
244
+
225
245
  ## Database Support
246
+
226
247
  Jackson currently supports the following databases.
227
-
248
+
228
249
  - Postgres
229
250
  - CockroachDB
230
251
  - MySQL
231
252
  - MariaDB
232
253
  - MongoDB
233
254
  - Redis
234
-
255
+
235
256
  ## Configuration
257
+
236
258
  Configuration is done via env vars (and in the case of the npm library via an options object).
237
-
259
+
238
260
  The following options are supported and will have to be configured during deployment.
239
-
261
+
240
262
  | Key | Description | Default |
241
- |-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------|
263
+ | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
242
264
  | HOST_URL | The URL to bind to | `localhost` |
243
265
  | HOST_PORT | The port to bind to | `5000` |
244
266
  | EXTERNAL_URL (npm: externalUrl) | The public URL to reach this service, used internally for documenting the SAML configuration instructions. | `http://{HOST_URL}:{HOST_PORT}` |
245
267
  | INTERNAL_HOST_URL | The URL to bind to expose the internal APIs. Do not configure this to a public network. | `localhost` |
246
268
  | INTERNAL_HOST_PORT | The port to bind to for the internal APIs. | `6000` |
247
- | SAML_AUDIENCE (npm: samlAudience) | This is just an identifier to validate the SAML audience, this value will also get configured in the SAML apps created by your customers. Once set do not change this value unless you get your customers to reconfigure their SAML again. It is case-sensitive. This does not have to be a real URL. | `https://saml.boxyhq.com` |
269
+ | SAML_AUDIENCE (npm: samlAudience) | This is just an identifier to validate the SAML audience, this value will also get configured in the SAML apps created by your customers. Once set do not change this value unless you get your customers to reconfigure their SAML again. It is case-sensitive. This does not have to be a real URL. | `https://saml.boxyhq.com` |
248
270
  | IDP_ENABLED (npm: idpEnabled) | Set to `true` to enable IdP initiated login for SAML. SP initiated login is the only recommended flow but you might have to support IdP login at times. | `false` |
249
271
  | DB_ENGINE (npm: db.engine) | Supported values are `redis`, `sql`, `mongo`, `mem`. | `sql` |
250
272
  | DB_URL (npm: db.url) | The database URL to connect to. For example `postgres://postgres:postgres@localhost:5450/jackson` | |
251
273
  | DB_TYPE (npm: db.type) | Only needed when DB_ENGINE is `sql`. Supported values are `postgres`, `cockroachdb`, `mysql`, `mariadb`. | `postgres` |
252
274
  | PRE_LOADED_CONFIG | If you only need a single tenant or a handful of pre-configured tenants then this config will help you read and load SAML configs. It works well with the mem DB engine so you don't have to configure any external databases for this to work (though it works with those as well). This is a path (absolute or relative) to a directory that contains files organized in the format described in the next section. | |
253
-
275
+
254
276
  ## Pre-loaded SAML Configuration
277
+
255
278
  If PRE_LOADED_CONFIG is set then it should point to a directory with the following structure (example below):-
279
+
256
280
  ```
257
281
  boxyhq.js
258
282
  boxyhq.xml
259
283
  anothertenant.js
260
284
  anothertenant.xml
261
285
  ```
286
+
262
287
  The JS file has the following structure:-
288
+
263
289
  ```
264
290
  module.exports = {
265
291
  defaultRedirectUrl: 'http://localhost:3000/login/saml',
@@ -268,29 +294,34 @@ module.exports = {
268
294
  product: 'demo',
269
295
  };
270
296
  ```
297
+
271
298
  The XML file (should share the name with the .js file) is the raw XML metadata file you receive from your Identity Provider. Please ensure it is saved in the `utf-8` encoding.
272
-
299
+
273
300
  The config and XML above correspond to the `SAML API config` (see below).
274
-
301
+
275
302
  ## SAML Login flows
303
+
276
304
  There are two kinds of SAML login flows - SP-initiated and IdP-initiated. We highly recommend sticking to the SP-initiated flow since it is more secure but Jackson also supports the IdP-initiated flow if you enable it. For an in-depth understanding of SAML and the two flows please refer to Okta's comprehensive guide - https://developer.okta.com/docs/concepts/saml/.
277
-
305
+
278
306
  ## Contributing
307
+
279
308
  Thanks for taking the time to contribute! Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make will benefit everybody else and are appreciated.
280
-
309
+
281
310
  Please try to create bug reports that are:
282
-
311
+
283
312
  - _Reproducible._ Include steps to reproduce the problem.
284
313
  - _Specific._ Include as much detail as possible: which version, what environment, etc.
285
314
  - _Unique._ Do not duplicate existing opened issues.
286
315
  - _Scoped to a Single Bug._ One bug per report.
287
-
316
+
288
317
  ## Support
318
+
289
319
  Reach out to the maintainer at one of the following places:
290
320
 
291
321
  - [GitHub Discussions](https://github.com/boxyhq/jackson/discussions)
292
322
  - [GitHub Issues](https://github.com/boxyhq/jackson/issues)
293
323
  - The email which is located [in GitHub profile](https://github.com/deepakprabhakara)
294
-
324
+
295
325
  ## License
326
+
296
327
  [Apache 2.0 License](https://github.com/boxyhq/jackson/blob/main/LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boxyhq/saml-jackson",
3
- "version": "0.1.5-beta.112",
3
+ "version": "0.1.5-beta.117",
4
4
  "license": "Apache 2.0",
5
5
  "description": "SAML 2.0 service",
6
6
  "main": "src/index.js",