jwtbutler 1.6.7 → 1.7.0
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.
- package/README.md +82 -82
- package/_js/_build/_helpers.js +30 -67
- package/_js/_build/script.js +292 -441
- package/package.json +59 -59
package/README.md
CHANGED
|
@@ -4,20 +4,20 @@ jwtbutler is a helper library for setting up a single sign on with jwt in a mult
|
|
|
4
4
|
|
|
5
5
|
## features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
7
|
+
- works in all js applications
|
|
8
|
+
- renders a simple loginform if needed
|
|
9
|
+
- requires no code changes on the auth server
|
|
10
|
+
- has a fetch helper function for api calls that do all the heavy lifting (e.g. refreshing token, repeating calls) under the hood
|
|
11
|
+
- provides a fallback for clients that have third party cookies disabled
|
|
12
|
+
- includes a timeout for broken connections
|
|
13
|
+
- syncs cookies via iframes through postmessage
|
|
14
|
+
- works also without single sign on
|
|
15
|
+
- full ie11 support
|
|
16
|
+
- full chrome support (SameSite=None)
|
|
17
17
|
|
|
18
18
|
## requirements
|
|
19
19
|
|
|
20
|
-
-
|
|
20
|
+
- a fully setup jwt auth server (like [simpleauth](https://github.com/vielhuber/simpleauth)) with the following routes
|
|
21
21
|
|
|
22
22
|
| route | method | arguments | header | response |
|
|
23
23
|
| -------- | ------ | -------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -26,7 +26,7 @@ jwtbutler is a helper library for setting up a single sign on with jwt in a mult
|
|
|
26
26
|
| /logout | POST | -- | Authorization: Bearer token | `([ 'success' => true, 'message' => 'logout successful', 'public_message' => '...' ], 200)` |
|
|
27
27
|
| /check | POST | access_token | -- | `([ 'success' => true, 'message' => 'valid token', 'public_message' => '...', 'data' => [ 'expires_in' => 3600, 'user_id' => 42, 'client_id' => 7000000 ] ], 200)` |
|
|
28
28
|
|
|
29
|
-
-
|
|
29
|
+
- 401 response code on bad authentication
|
|
30
30
|
|
|
31
31
|
## installation
|
|
32
32
|
|
|
@@ -50,7 +50,7 @@ now instantiate the object with the basic configuration:
|
|
|
50
50
|
|
|
51
51
|
```js
|
|
52
52
|
const api = new jwtbutler({
|
|
53
|
-
auth_server: '
|
|
53
|
+
auth_server: 'https://example-auth-server.vielhuber.dev',
|
|
54
54
|
auth_login: 'email'
|
|
55
55
|
});
|
|
56
56
|
```
|
|
@@ -61,9 +61,9 @@ if you want to use sso, add all pages to the configuration object:
|
|
|
61
61
|
|
|
62
62
|
```js
|
|
63
63
|
sso: [
|
|
64
|
-
'
|
|
65
|
-
'
|
|
66
|
-
'
|
|
64
|
+
'https://example-auth-page1.vielhuber.dev',
|
|
65
|
+
'https://example-auth-page2.vielhuber.dev',
|
|
66
|
+
'https://example-auth-page3.vielhuber.dev'
|
|
67
67
|
];
|
|
68
68
|
```
|
|
69
69
|
|
|
@@ -138,8 +138,8 @@ api.getUserId();
|
|
|
138
138
|
// if the user is not logged in and a new token cannot be generated,
|
|
139
139
|
// a login form is rendered and after a succesful login, the request is again repeated
|
|
140
140
|
// fetch has the same interface as the official javascript Fetch API
|
|
141
|
-
api.fetch('
|
|
142
|
-
api.fetch('
|
|
141
|
+
api.fetch('https://example-auth-page1.vielhuber.dev/protected/');
|
|
142
|
+
api.fetch('https://example-auth-page2.vielhuber.dev/protected/', {
|
|
143
143
|
method: 'POST',
|
|
144
144
|
body: JSON.stringify({ foo: 'bar' }),
|
|
145
145
|
cache: 'no-cache',
|
|
@@ -161,15 +161,15 @@ api.logout().then(() => {
|
|
|
161
161
|
|
|
162
162
|
use the following classes to style idle states:
|
|
163
163
|
|
|
164
|
-
-
|
|
165
|
-
-
|
|
166
|
-
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
164
|
+
- `html.jwtbutler-logging-in`
|
|
165
|
+
- `html.jwtbutler-logging-out`
|
|
166
|
+
- `html.jwtbutler-loading` (for logging in and out)
|
|
167
|
+
- `html.jwtbutler-fetching`
|
|
168
|
+
- `html.jwtbutler-login-form-visible`
|
|
169
169
|
|
|
170
170
|
to style the login form, use the class of the main container:
|
|
171
171
|
|
|
172
|
-
-
|
|
172
|
+
- `.login-form`
|
|
173
173
|
|
|
174
174
|
some basic styling of the login form can look like this:
|
|
175
175
|
|
|
@@ -201,6 +201,7 @@ composer require firebase/php-jwt
|
|
|
201
201
|
```php
|
|
202
202
|
require_once __DIR__ . '/vendor/autoload.php';
|
|
203
203
|
use Firebase\JWT\JWT;
|
|
204
|
+
use Firebase\JWT\Key;
|
|
204
205
|
|
|
205
206
|
// cors
|
|
206
207
|
header('Access-Control-Allow-Origin: *');
|
|
@@ -213,8 +214,7 @@ if (@$_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
|
|
|
213
214
|
try {
|
|
214
215
|
$user_id = JWT::decode(
|
|
215
216
|
str_replace('Bearer ', '', @$_SERVER['HTTP_AUTHORIZATION']), // access token
|
|
216
|
-
'WM38tprPABEgkldbt2yTAgxf2CGstfr5', // secret key
|
|
217
|
-
['HS256']
|
|
217
|
+
new Key('WM38tprPABEgkldbt2yTAgxf2CGstfr5', 'HS256'), // secret key
|
|
218
218
|
)->sub;
|
|
219
219
|
http_response_code(200);
|
|
220
220
|
echo json_encode([
|
|
@@ -248,10 +248,10 @@ RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
|
|
|
248
248
|
|
|
249
249
|
setup the following vhosts:
|
|
250
250
|
|
|
251
|
-
-
|
|
252
|
-
-
|
|
253
|
-
-
|
|
254
|
-
-
|
|
251
|
+
- https://example-auth-server.vielhuber.dev => jwt auth server (like [simpleauth](https://github.com/vielhuber/simpleauth))
|
|
252
|
+
- https://example-auth-page1.vielhuber.dev => \_tests/page1
|
|
253
|
+
- https://example-auth-page2.vielhuber.dev => \_tests/page2
|
|
254
|
+
- https://example-auth-page3.vielhuber.dev => \_tests/page3
|
|
255
255
|
|
|
256
256
|
and then run the test:
|
|
257
257
|
|
|
@@ -261,55 +261,55 @@ gulp js-test
|
|
|
261
261
|
|
|
262
262
|
## pseudo code
|
|
263
263
|
|
|
264
|
-
-
|
|
265
|
-
-
|
|
266
|
-
-
|
|
267
|
-
-
|
|
268
|
-
-
|
|
269
|
-
-
|
|
270
|
-
-
|
|
271
|
-
-
|
|
272
|
-
-
|
|
273
|
-
-
|
|
274
|
-
-
|
|
275
|
-
-
|
|
276
|
-
-
|
|
277
|
-
-
|
|
278
|
-
-
|
|
279
|
-
-
|
|
280
|
-
-
|
|
281
|
-
-
|
|
282
|
-
-
|
|
283
|
-
-
|
|
284
|
-
-
|
|
285
|
-
-
|
|
286
|
-
-
|
|
287
|
-
-
|
|
288
|
-
-
|
|
289
|
-
-
|
|
290
|
-
-
|
|
291
|
-
-
|
|
292
|
-
-
|
|
293
|
-
-
|
|
294
|
-
-
|
|
295
|
-
-
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
-
|
|
300
|
-
-
|
|
301
|
-
-
|
|
302
|
-
-
|
|
303
|
-
-
|
|
304
|
-
-
|
|
305
|
-
-
|
|
306
|
-
-
|
|
307
|
-
-
|
|
308
|
-
-
|
|
309
|
-
-
|
|
310
|
-
-
|
|
311
|
-
-
|
|
312
|
-
-
|
|
313
|
-
-
|
|
314
|
-
-
|
|
315
|
-
-
|
|
264
|
+
- if pageX wants to check if user is logged in on client side (without a backend call)
|
|
265
|
+
- pageX frontend checks if access_token is present in cookie and can be decoded (the token is not validated and it also can be expired)
|
|
266
|
+
- if no, the user is considered to be logged out
|
|
267
|
+
- if yes, the user is considered to be logged in
|
|
268
|
+
- if a logged in user makes an backend/api call on pageX
|
|
269
|
+
- pageX frontend checks if access_token is present in cookie and can be decoded (the token is not validated and it also can be expired)
|
|
270
|
+
- if no, the user is considered to be not logged in
|
|
271
|
+
- see login procedure
|
|
272
|
+
- the call is repeated automatically after a succesful login
|
|
273
|
+
- if yes, the user is considered to be logged in
|
|
274
|
+
- the user sets the access_token in the header ("Bearer")
|
|
275
|
+
- pageX backend validates the access token with the secret key
|
|
276
|
+
- if the validation is ok
|
|
277
|
+
- the backend extracts the user id from the token and uses that to provide data
|
|
278
|
+
- the final response is served to the client
|
|
279
|
+
- if the validation is not ok
|
|
280
|
+
- an error is served to the client
|
|
281
|
+
- the client tries to generate a new token from the old one
|
|
282
|
+
- if that was successful
|
|
283
|
+
- pageX sets cookie for oneself
|
|
284
|
+
- if user has enabled third party cookies
|
|
285
|
+
- pageX server sets new access token in cookie via iframes for all other pages
|
|
286
|
+
- the call will be repeated automatically
|
|
287
|
+
- if that was not successful
|
|
288
|
+
- see login procedure
|
|
289
|
+
- the call is repeated automatically after a succesful login
|
|
290
|
+
- login procedure
|
|
291
|
+
- pageX frontend verifies the access_token via secret_key via the /check route
|
|
292
|
+
- if available and not expired
|
|
293
|
+
- pageX sets cookie for oneself
|
|
294
|
+
- if user has enabled third party cookies
|
|
295
|
+
- pageX server sets new access token in cookie via iframes for all other pages
|
|
296
|
+
- if available and expired
|
|
297
|
+
- pageX frontend tries to generate a new token from old token via the /refresh route
|
|
298
|
+
- if it worked
|
|
299
|
+
- pageX sets cookie for oneself
|
|
300
|
+
- if user has enabled third party cookies
|
|
301
|
+
- pageX server sets new access token in cookie via iframes for all other pages
|
|
302
|
+
- if it didn't work
|
|
303
|
+
- render login form
|
|
304
|
+
- if not available
|
|
305
|
+
- render login form
|
|
306
|
+
- if the user submits the rendered login form on pageX
|
|
307
|
+
- pageX gets back access token from auth server
|
|
308
|
+
- pageX sets cookie for oneself
|
|
309
|
+
- if user has enabled third party cookies
|
|
310
|
+
- pageX sets cookie via iframes for all other pages
|
|
311
|
+
- if the user calls the logout function on pageX
|
|
312
|
+
- pageX calls auth server to logout (this invalidates the token on the server side)
|
|
313
|
+
- pageX removes cookie for oneself
|
|
314
|
+
- if user has enabled third party cookies
|
|
315
|
+
- pageX removes cookie via iframes for all other pages
|
package/_js/_build/_helpers.js
CHANGED
|
@@ -1,80 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
5
3
|
Object.defineProperty(exports, "__esModule", {
|
|
6
4
|
value: true
|
|
7
5
|
});
|
|
8
6
|
exports.default = void 0;
|
|
9
|
-
|
|
10
|
-
require("core-js/modules/es.regexp.exec.js");
|
|
11
|
-
|
|
12
|
-
require("core-js/modules/es.string.match.js");
|
|
13
|
-
|
|
14
7
|
require("core-js/modules/es.regexp.constructor.js");
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
21
|
-
|
|
22
|
-
var helpers = /*#__PURE__*/function () {
|
|
23
|
-
function helpers() {
|
|
24
|
-
(0, _classCallCheck2.default)(this, helpers);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
(0, _createClass2.default)(helpers, null, [{
|
|
28
|
-
key: "cookieExists",
|
|
29
|
-
value: function cookieExists(cookie_name) {
|
|
30
|
-
if (document.cookie !== undefined && this.cookieGet(cookie_name) !== null) {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return false;
|
|
8
|
+
require("core-js/modules/es.regexp.exec.js");
|
|
9
|
+
class helpers {
|
|
10
|
+
static cookieExists(cookie_name) {
|
|
11
|
+
if (document.cookie !== undefined && this.cookieGet(cookie_name) !== null) {
|
|
12
|
+
return true;
|
|
35
13
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return decodeURIComponent(cookie_match[1]);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return null;
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
static cookieGet(cookie_name) {
|
|
17
|
+
var cookie_match = document.cookie.match(new RegExp(cookie_name + '=([^;]+)'));
|
|
18
|
+
if (cookie_match) {
|
|
19
|
+
return decodeURIComponent(cookie_match[1]);
|
|
46
20
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
samesite = '; SameSite=None; Secure';
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
document.cookie = cookie_name + '=' + encodeURIComponent(cookie_value) + '; ' + 'expires=' + new Date(new Date().getTime() + days * 24 * 60 * 60 * 1000).toUTCString() + '; path=/' + samesite;
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
static cookieSet(cookie_name, cookie_value, days) {
|
|
24
|
+
let samesite = '';
|
|
25
|
+
if (window.location.protocol.indexOf('https') > -1) {
|
|
26
|
+
samesite = '; SameSite=None; Secure';
|
|
57
27
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
samesite = '; SameSite=None; Secure';
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
document.cookie = cookie_name + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/' + samesite;
|
|
28
|
+
document.cookie = cookie_name + '=' + encodeURIComponent(cookie_value) + '; ' + 'expires=' + new Date(new Date().getTime() + days * 24 * 60 * 60 * 1000).toUTCString() + '; path=/' + samesite;
|
|
29
|
+
}
|
|
30
|
+
static cookieDelete(cookie_name) {
|
|
31
|
+
let samesite = '';
|
|
32
|
+
if (window.location.protocol.indexOf('https') > -1) {
|
|
33
|
+
samesite = '; SameSite=None; Secure';
|
|
68
34
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
35
|
+
document.cookie = cookie_name + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/' + samesite;
|
|
36
|
+
}
|
|
37
|
+
static remove(el) {
|
|
38
|
+
if (el !== null) {
|
|
39
|
+
el.parentNode.removeChild(el);
|
|
75
40
|
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
}();
|
|
79
|
-
|
|
41
|
+
}
|
|
42
|
+
}
|
|
80
43
|
exports.default = helpers;
|