@axa-fr/react-oidc 6.0.0 → 6.0.3
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 +7 -25
- package/bin/copy.js +37 -0
- package/dist/vanilla/parseTokens.d.ts +5 -0
- package/dist/vanilla/parseTokens.d.ts.map +1 -0
- package/dist/vanilla/parseTokens.js +107 -0
- package/dist/vanilla/parseTokens.js.map +1 -0
- package/package.json +6 -4
- /package/src/oidc/vanilla/{parseTokens.js → parseTokens.ts} +0 -0
package/README.md
CHANGED
|
@@ -49,25 +49,15 @@ It use AppAuthJS behind the scene because it very lightweight and created by ope
|
|
|
49
49
|
# Getting Started
|
|
50
50
|
|
|
51
51
|
```sh
|
|
52
|
-
npm install @axa-fr/react-oidc
|
|
52
|
+
npm install @axa-fr/react-oidc --save
|
|
53
|
+
|
|
54
|
+
# If you have a "public" folder, the 2 files will be created :
|
|
55
|
+
# ./public/OidcServiceWorker.js <-- will be updated at each "npm install"
|
|
56
|
+
# ./public/OidcTrustedDomains.js <-- won't be updated if already exist
|
|
53
57
|
```
|
|
54
58
|
|
|
55
59
|
If you need a very secure mode where refresh_token and access_token will be hide behind a service worker that will proxify requests.
|
|
56
|
-
|
|
57
|
-
Add a copy task in order to install and stay up to date an Oidc Service Worker.
|
|
58
|
-
The only file you should edit is "OidcTrustedDomains.js" which will never be erased with following configuration bellow.
|
|
59
|
-
|
|
60
|
-
```sh
|
|
61
|
-
#package.json
|
|
62
|
-
{
|
|
63
|
-
"scripts": {
|
|
64
|
-
"copy": "copyfiles -f ./node_modules/@axa-fr/react-oidc/dist/OidcServiceWorker.js ./public && copyfiles -f -s ./node_modules/@axa-fr/react-oidc/dist/OidcTrustedDomains.js ./public",
|
|
65
|
-
"start:server": "react-scripts start",
|
|
66
|
-
"build:server": "npm run copy && react-scripts build",
|
|
67
|
-
"prepare": "npm run copy"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
```
|
|
60
|
+
The only file you should edit is "OidcTrustedDomains.js".
|
|
71
61
|
|
|
72
62
|
```javascript
|
|
73
63
|
// OidcTrustedDomains.js
|
|
@@ -527,15 +517,7 @@ const configuration = {
|
|
|
527
517
|
redirect_uri: 'http://localhost:3001/#authentication/callback',
|
|
528
518
|
silent_redirect_uri: 'http://localhost:3001/#authentication/silent-callback', // Optional activate silent-login that use cookies between OIDC server and client javascript to restore the session
|
|
529
519
|
scope: 'openid profile email api offline_access',
|
|
530
|
-
authority: 'https://demo.duendesoftware.com'
|
|
531
|
-
authority_configuration: {
|
|
532
|
-
authorization_endpoint: 'https://demo.duendesoftware.com/connect/authorize',
|
|
533
|
-
token_endpoint: 'https://demo.duendesoftware.com/connect/token',
|
|
534
|
-
userinfo_endpoint: 'https://demo.duendesoftware.com/connect/userinfo',
|
|
535
|
-
end_session_endpoint: 'https://demo.duendesoftware.com/connect/endsession',
|
|
536
|
-
revocation_endpoint: 'https://demo.duendesoftware.com/connect/revocation',
|
|
537
|
-
check_session_iframe: 'https://demo.duendesoftware.com/connect/checksession'
|
|
538
|
-
},
|
|
520
|
+
authority: 'https://demo.duendesoftware.com'
|
|
539
521
|
};
|
|
540
522
|
|
|
541
523
|
const onEvent=(configurationName, eventName, data )=>{
|
package/bin/copy.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const applyCopy=(input) => {
|
|
6
|
+
try {
|
|
7
|
+
const destinationPath = path.join(__dirname, input);
|
|
8
|
+
if (fs.existsSync(destinationPath)) {
|
|
9
|
+
const serviceworkerFilename = "OidcServiceWorker.js";
|
|
10
|
+
const serviceWorkerDestinationPath = path.join(destinationPath, `${serviceworkerFilename}`);
|
|
11
|
+
if (fs.existsSync(serviceWorkerDestinationPath)) {
|
|
12
|
+
fs.unlinkSync(serviceWorkerDestinationPath);
|
|
13
|
+
}
|
|
14
|
+
fs.copyFileSync(path.join(__dirname, `..\\dist\\${serviceworkerFilename}`), serviceWorkerDestinationPath);
|
|
15
|
+
console.log(`File copied successfully at ${serviceWorkerDestinationPath}`);
|
|
16
|
+
|
|
17
|
+
const trustedDomainsFilename = "OidcTrustedDomains.js";
|
|
18
|
+
const trustedDomainsDestinationPath = path.join(destinationPath, `${trustedDomainsFilename}`);
|
|
19
|
+
if(!fs.existsSync(trustedDomainsDestinationPath)){
|
|
20
|
+
fs.copyFileSync(path.join(__dirname, `..\\dist\\${trustedDomainsFilename}`), trustedDomainsDestinationPath);
|
|
21
|
+
console.log(`File copied successfully at ${trustedDomainsDestinationPath}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch (ex) {
|
|
25
|
+
console.error(ex);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const args = process.argv;
|
|
30
|
+
if (args.length >= 3) {
|
|
31
|
+
const input = args[2];
|
|
32
|
+
applyCopy(input);
|
|
33
|
+
} else if(__dirname.includes("@axa-fr")) {
|
|
34
|
+
applyCopy("../../../../public");
|
|
35
|
+
} else {
|
|
36
|
+
applyCopy("../public");
|
|
37
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const setTokens: (tokens: any) => any;
|
|
2
|
+
export declare const parseOriginalTokens: (tokens: any) => any;
|
|
3
|
+
export declare const computeTimeLeft: (refreshTimeBeforeTokensExpirationInSecond: any, expiresAt: any) => number;
|
|
4
|
+
export declare const isTokensValid: (tokens: any) => boolean;
|
|
5
|
+
//# sourceMappingURL=parseTokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseTokens.d.ts","sourceRoot":"","sources":["../../src/oidc/vanilla/parseTokens.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,SAAS,sBAyBrB,CAAA;AAGD,eAAO,MAAM,mBAAmB,sBA+B/B,CAAA;AAED,eAAO,MAAM,eAAe,4EAG3B,CAAA;AAED,eAAO,MAAM,aAAa,0BAKzB,CAAA"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.isTokensValid = exports.computeTimeLeft = exports.parseOriginalTokens = exports.setTokens = void 0;
|
|
15
|
+
var idTokenPayload = function (token) {
|
|
16
|
+
if (!token) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
var base64Url = token.split('.')[1];
|
|
20
|
+
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
21
|
+
var jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
|
|
22
|
+
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
23
|
+
}).join(''));
|
|
24
|
+
return JSON.parse(jsonPayload);
|
|
25
|
+
};
|
|
26
|
+
var countLetter = function (str, find) {
|
|
27
|
+
return (str.split(find)).length - 1;
|
|
28
|
+
};
|
|
29
|
+
var extractAccessTokenPayload = function (tokens) {
|
|
30
|
+
if (tokens.accessTokenPayload) {
|
|
31
|
+
return tokens.accessTokenPayload;
|
|
32
|
+
}
|
|
33
|
+
var accessToken = tokens.accessToken;
|
|
34
|
+
try {
|
|
35
|
+
if (!accessToken || countLetter(accessToken, '.') !== 2) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return JSON.parse(atob(accessToken.split('.')[1]));
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
console.warn(e);
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
};
|
|
45
|
+
var setTokens = function (tokens) {
|
|
46
|
+
if (!tokens) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
var accessTokenPayload;
|
|
50
|
+
if (!tokens.issuedAt) {
|
|
51
|
+
var currentTimeUnixSecond = new Date().getTime() / 1000;
|
|
52
|
+
tokens.issuedAt = currentTimeUnixSecond;
|
|
53
|
+
}
|
|
54
|
+
if (tokens.accessTokenPayload !== undefined) {
|
|
55
|
+
accessTokenPayload = tokens.accessTokenPayload;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
accessTokenPayload = extractAccessTokenPayload(tokens);
|
|
59
|
+
}
|
|
60
|
+
var _idTokenPayload = idTokenPayload(tokens.idToken);
|
|
61
|
+
var idTokenExipreAt = (_idTokenPayload && _idTokenPayload.exp) ? _idTokenPayload.exp : Number.MAX_VALUE;
|
|
62
|
+
var accessTokenExpiresAt = (accessTokenPayload && accessTokenPayload.exp) ? accessTokenPayload.exp : tokens.issuedAt + tokens.expiresIn;
|
|
63
|
+
var expiresAt = idTokenExipreAt < accessTokenExpiresAt ? idTokenExipreAt : accessTokenExpiresAt;
|
|
64
|
+
return __assign(__assign({}, tokens), { idTokenPayload: _idTokenPayload, accessTokenPayload: accessTokenPayload, expiresAt: expiresAt });
|
|
65
|
+
};
|
|
66
|
+
exports.setTokens = setTokens;
|
|
67
|
+
var parseOriginalTokens = function (tokens) {
|
|
68
|
+
if (!tokens) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (!tokens.issued_at) {
|
|
72
|
+
var currentTimeUnixSecond = new Date().getTime() / 1000;
|
|
73
|
+
tokens.issued_at = currentTimeUnixSecond;
|
|
74
|
+
}
|
|
75
|
+
var data = {
|
|
76
|
+
accessToken: tokens.access_token,
|
|
77
|
+
expiresIn: tokens.expires_in,
|
|
78
|
+
idToken: tokens.id_token,
|
|
79
|
+
refreshToken: tokens.refresh_token,
|
|
80
|
+
scope: tokens.scope,
|
|
81
|
+
tokenType: tokens.token_type,
|
|
82
|
+
issuedAt: tokens.issued_at
|
|
83
|
+
};
|
|
84
|
+
if (tokens.accessTokenPayload !== undefined) {
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
data.accessTokenPayload = tokens.accessTokenPayload;
|
|
87
|
+
}
|
|
88
|
+
if (tokens.idTokenPayload !== undefined) {
|
|
89
|
+
// @ts-ignore
|
|
90
|
+
data.idTokenPayload = tokens.idTokenPayload;
|
|
91
|
+
}
|
|
92
|
+
return (0, exports.setTokens)(data);
|
|
93
|
+
};
|
|
94
|
+
exports.parseOriginalTokens = parseOriginalTokens;
|
|
95
|
+
var computeTimeLeft = function (refreshTimeBeforeTokensExpirationInSecond, expiresAt) {
|
|
96
|
+
var currentTimeUnixSecond = new Date().getTime() / 1000;
|
|
97
|
+
return Math.round(((expiresAt - refreshTimeBeforeTokensExpirationInSecond) - currentTimeUnixSecond));
|
|
98
|
+
};
|
|
99
|
+
exports.computeTimeLeft = computeTimeLeft;
|
|
100
|
+
var isTokensValid = function (tokens) {
|
|
101
|
+
if (!tokens) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
return (0, exports.computeTimeLeft)(0, tokens.expiresAt) > 0;
|
|
105
|
+
};
|
|
106
|
+
exports.isTokensValid = isTokensValid;
|
|
107
|
+
//# sourceMappingURL=parseTokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseTokens.js","sourceRoot":"","sources":["../../src/oidc/vanilla/parseTokens.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axa-fr/react-oidc",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"jsnext:main": "dist/index.js",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
14
|
+
"bin",
|
|
14
15
|
"src/oidc",
|
|
15
16
|
"README.md",
|
|
16
17
|
"tsconfig.json",
|
|
@@ -33,7 +34,8 @@
|
|
|
33
34
|
"test": "react-scripts test --coverage",
|
|
34
35
|
"eject": "react-scripts eject",
|
|
35
36
|
"clean": "rimraf dist",
|
|
36
|
-
"prepare": "npm run clean && tsc --build \"./tsconfig.json\" && copyfiles -f ./src/oidc/vanilla/OidcServiceWorker.js ./dist && copyfiles -f ./src/oidc/vanilla/OidcTrustedDomains.js ./dist"
|
|
37
|
+
"prepare": "npm run clean && tsc --build \"./tsconfig.json\" && copyfiles -f ./src/oidc/vanilla/OidcServiceWorker.js ./dist && copyfiles -f ./src/oidc/vanilla/OidcTrustedDomains.js ./dist",
|
|
38
|
+
"postinstall": "node ./bin/copy.js"
|
|
37
39
|
},
|
|
38
40
|
"dependencies": {
|
|
39
41
|
"@openid/appauth": "1.3.1"
|
|
@@ -43,8 +45,6 @@
|
|
|
43
45
|
"react-dom": "x"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
46
|
-
"react": "x",
|
|
47
|
-
"react-dom": "x",
|
|
48
48
|
"@testing-library/jest-dom": "5.16.4",
|
|
49
49
|
"@testing-library/react": "13.1.1",
|
|
50
50
|
"@testing-library/user-event": "14.1.1",
|
|
@@ -54,6 +54,8 @@
|
|
|
54
54
|
"cross-env": "^7.0.3",
|
|
55
55
|
"cypress": "^9.5.0",
|
|
56
56
|
"msw": "0.39.2",
|
|
57
|
+
"react": "x",
|
|
58
|
+
"react-dom": "x",
|
|
57
59
|
"react-router-dom": "6.3.0",
|
|
58
60
|
"react-scripts": "5.0.1",
|
|
59
61
|
"typescript": "4.6.3"
|
|
File without changes
|