@vario-software/vario-app-framework-backend 2026.3.1 → 2026.4.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/api/ErpApi.js +6 -1
- package/app.js +1 -0
- package/package.json +1 -1
- package/utils/keycloak.js +19 -13
- package/utils/token.js +39 -0
package/api/ErpApi.js
CHANGED
|
@@ -8,7 +8,7 @@ const Migration = require('#backend/api/modules/migration.js');
|
|
|
8
8
|
const TextEnum = require('#backend/api/modules/textEnum.js');
|
|
9
9
|
const Webhook = require('#backend/api/modules/webhook.js');
|
|
10
10
|
const PermittedToken = require('#backend/api/modules/permittedToken.js');
|
|
11
|
-
const { validateOfflineToken } = require('#backend/utils/token.js');
|
|
11
|
+
const { validateOfflineToken, isAppTokenExpired, refreshAppToken } = require('#backend/utils/token.js');
|
|
12
12
|
|
|
13
13
|
const singletonPromise = new PromiseSingletonMap();
|
|
14
14
|
class ErpApi extends Api
|
|
@@ -35,6 +35,11 @@ class ErpApi extends Api
|
|
|
35
35
|
|
|
36
36
|
if (!this.executeAsAppUser)
|
|
37
37
|
{
|
|
38
|
+
if (isAppTokenExpired())
|
|
39
|
+
{
|
|
40
|
+
await refreshAppToken();
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
this.setHeaders({
|
|
39
44
|
'X-Forwarded-App-Token': getAppToken(),
|
|
40
45
|
});
|
package/app.js
CHANGED
|
@@ -20,6 +20,7 @@ const VarioCloudApp = class
|
|
|
20
20
|
{
|
|
21
21
|
this.onUnhandledError = options.onUnhandledError ?? console.error;
|
|
22
22
|
this.onMigrationError = options.onMigrationError ?? console.error;
|
|
23
|
+
this.onKeycloakError = options.onKeycloakError;
|
|
23
24
|
|
|
24
25
|
exceptionHandler = setupException(this);
|
|
25
26
|
|
package/package.json
CHANGED
package/utils/keycloak.js
CHANGED
|
@@ -26,20 +26,26 @@ async function refreshAccessToken(offlineToken, refreshUrl)
|
|
|
26
26
|
|
|
27
27
|
const timer = performance.now();
|
|
28
28
|
|
|
29
|
-
const { data } = await VarioApi.fetch(refreshUrl, refreshOptions)
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const { data } = await VarioApi.fetch(refreshUrl, refreshOptions)
|
|
30
|
+
.catch(async error =>
|
|
31
|
+
{
|
|
32
|
+
await app.log(
|
|
33
|
+
{
|
|
34
|
+
request: { url: refreshUrl, body: '[secret]' },
|
|
35
|
+
response: `[secret(${Object.keys(typeof error?.data === 'object' ? error.data : {})})]`,
|
|
36
|
+
duration: `${(performance.now() - timer).toFixed(2)}ms`,
|
|
37
|
+
},
|
|
38
|
+
'utils/keycloak',
|
|
39
|
+
'DEBUG',
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (app.onKeycloakError)
|
|
32
43
|
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
'DEBUG',
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
throw error;
|
|
42
|
-
});
|
|
44
|
+
app.onKeycloakError(error);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw error;
|
|
48
|
+
});
|
|
43
49
|
|
|
44
50
|
await app.log(
|
|
45
51
|
{
|
package/utils/token.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { jwtVerify, decodeJwt, importJWK } = require('jose');
|
|
2
2
|
const { getApp } = require('#backend/utils/context.js');
|
|
3
|
+
const { getAppToken, getContext } = require('#backend/utils/context.js');
|
|
3
4
|
|
|
4
5
|
function validateOfflineToken(offlineToken)
|
|
5
6
|
{
|
|
@@ -82,7 +83,45 @@ function validateAppToken(appToken)
|
|
|
82
83
|
});
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
function isAppTokenExpired()
|
|
87
|
+
{
|
|
88
|
+
const { accessToken } = getContext();
|
|
89
|
+
|
|
90
|
+
const { exp } = accessToken;
|
|
91
|
+
|
|
92
|
+
return ((exp - 2) < (Date.now() / 1000));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function refreshAppToken()
|
|
96
|
+
{
|
|
97
|
+
const appToken = getAppToken();
|
|
98
|
+
const app = getApp();
|
|
99
|
+
|
|
100
|
+
const { data } = await app.erp.fetch(`/cmn/apps/${app.client.appIdentifier}/refresh-token`, {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
body: appToken,
|
|
103
|
+
useInternalApi: true,
|
|
104
|
+
headers: {
|
|
105
|
+
'Content-Type': 'text/plain',
|
|
106
|
+
},
|
|
107
|
+
secret: true,
|
|
108
|
+
executeAsAppUser: true,
|
|
109
|
+
secretsToMask: ['bearerToken'],
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const newAppToken = data.bearerToken;
|
|
113
|
+
|
|
114
|
+
const accessToken = await validateAppToken(newAppToken);
|
|
115
|
+
|
|
116
|
+
const context = getContext();
|
|
117
|
+
|
|
118
|
+
context.appToken = newAppToken;
|
|
119
|
+
context.accessToken = accessToken;
|
|
120
|
+
}
|
|
121
|
+
|
|
85
122
|
module.exports = {
|
|
86
123
|
validateOfflineToken,
|
|
87
124
|
validateAppToken,
|
|
125
|
+
isAppTokenExpired,
|
|
126
|
+
refreshAppToken,
|
|
88
127
|
};
|