@gandalan/weblibs 0.0.41 → 1.0.1

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 CHANGED
@@ -4,27 +4,28 @@ WebLibs for Gandalan JS/TS/Svelte projects
4
4
 
5
5
 
6
6
  ```js
7
- import { IDAS } from '@gandalan/weblibs/api/IDAS';
8
- let idas = new IDAS();
9
-
10
- // bei Bedarf wird der Client zur Anmeldung umgeleitet, danach wird die aktuelle Seite wieder aufgerufen
11
- idas.authenticateWithSSO();
7
+ import { IDASFactory } from '@gandalan/weblibs';
8
+ let idas = IDASFactory.create();
12
9
  ```
13
10
 
11
+ IDAS ab Version 1.0.0 verwendet JWT-Token für die Authentifizierung mit WebAPI.
12
+
14
13
  Danach z.B. Zugriff auf die Mandant-Guid:
15
14
 
16
15
  ```js
17
- let mandantGuid = idas.mandantGuid;
16
+ let mandantGuid = await idas.then(i => i.mandantGuid);
18
17
  ```
19
18
 
20
19
  Datenzugriffe erfolgen über die Objekte innerhalb der IDAS-Klasse
21
20
 
22
21
  ```js
23
22
  let loader = Promise.all([
24
- idas.mandanten.getAll()
23
+ idas.
24
+ .then(i => i.mandanten.getAll())
25
25
  .then(d => mandanten = d.sort((a,b) => a.Name.localeCompare(b.Name)))
26
26
  .catch(e => error = e),
27
- idas.rollen.getAll()
27
+ idas
28
+ .then(i => i.rollen.getAll())
28
29
  .then(d => rollen = d.sort((a,b) => a.Name.localeCompare(b.Name)))
29
30
  .catch(e => error = e)
30
31
  ])
package/api/IDAS.js CHANGED
@@ -2,142 +2,155 @@ import { RESTClient } from './RESTClient';
2
2
 
3
3
  let appToken = localStorage.getItem('IDAS_AppToken') || '66B70E0B-F7C4-4829-B12A-18AD309E3970';
4
4
  let authToken = localStorage.getItem('IDAS_AuthToken');
5
- let authJwtToken = localStorage.getItem('IDAS_AuthJwtToken');
6
5
  let apiBaseUrl = localStorage.getItem('IDAS_ApiBaseUrl') || 'https://api.dev.idas-cloudservices.net/api/';
7
-
8
- let restClient = new RESTClient(apiBaseUrl, authToken);
9
- restClient.onError = (error, message) => {
10
- if (message.indexOf("401") != -1 || message.indexOf("403") != -1) {
11
- localStorage.removeItem('IDAS_AuthToken');
12
- localStorage.removeItem('IDAS_AuthJwtToken');
13
- new IDAS().authenticateWithSSO(true);
14
- }
15
- }
16
-
17
- export class IDAS {
18
- async authenticate(authDTO) {
19
- authDTO.AppToken = appToken;
20
- let { data } = await restClient.post('/Login/Authenticate', authDTO);
21
- if (data?.Token) {
22
- authToken = data.Token;
23
- localStorage.setItem('IDAS_AuthToken', authToken);
24
- restClient = new RESTClient(apiBaseUrl, authToken);
6
+ let authJwtCallbackPath = localStorage.getItem('IDAS_AuthJwtCallbackPath') || '';
7
+ let authJwtToken;
8
+
9
+ export let IDASFactory = {
10
+ async create() {
11
+ return new Promise((resolve, reject) => {
12
+ let idas = new IDAS();
13
+ resolve(idas.authenticateWithJwt(authJwtCallbackPath));
14
+ });
15
+ },
16
+
17
+ authorize() {
18
+ var urlParams = new URLSearchParams(location.search);
19
+ if (urlParams.has('t')) {
20
+ let idas = new IDAS();
21
+ idas.authorizeWithJwt(urlParams.get('t'));
25
22
  }
26
- return data;
27
- }
28
-
29
- authorizeWithJwt(jwtToken) {
30
- localStorage.setItem('IDAS_AuthJwtToken', jwtToken);
31
- restClient = new RESTClient(apiBaseUrl, jwtToken, true);
23
+ if (urlParams.has("m")) {
24
+ localStorage.setItem("IDAS_MandantGuid", urlParams.get("m"));
25
+ }
26
+ if (urlParams.has("a")) {
27
+ localStorage.setItem("IDAS_ApiBaseUrl", urlParams.get("a"));
28
+ }
29
+ window.location.search = "";
32
30
  }
31
+ }
33
32
 
34
- async authenticateWithSSO(forceRenew = false) {
35
- if (!authToken)
36
- {
37
- const url = new URL(apiBaseUrl);
38
- url.pathname = "/SSO";
39
- url.search = "?a=" + appToken;
40
- if (forceRenew)
41
- url.search = url.search + "&forceRenew=true";
42
- url.search = url.search + "&r=%target%%3Ft=%token%%26m=%mandant%";
43
- let ssoAuthUrl = url.toString();
33
+ class IDAS {
34
+ restClient = undefined;
44
35
 
45
- var ssoURL = ssoAuthUrl.replace("%target%", encodeURIComponent(window.location.href));
46
- window.location = ssoURL;
47
- }
36
+ authorizeWithJwt(jwtToken, mandant = '') {
37
+ authJwtToken = jwtToken;
38
+ mandant && localStorage.setItem('IDAS_MandantGuid', mandant);
39
+ this.restClient = new RESTClient(apiBaseUrl, jwtToken, true);
48
40
  }
49
41
 
50
42
  async authenticateWithJwt(authPath) {
51
- if (!authJwtToken) {
52
- const authEndpoint = (new URL(window.location.href).origin) + authPath;
53
- let authUrlCallback = `${authEndpoint}?r=%target%&t=%jwt%`;
54
- authUrlCallback = authUrlCallback.replace('%target%', encodeURIComponent(window.location.href));
55
-
56
- const url = new URL(apiBaseUrl);
57
- url.pathname = "/Session";
58
- url.search = `?a=${appToken}&r=${encodeURIComponent(authUrlCallback)}`;
59
- let jwtUrl = url.toString();
60
-
61
- window.location = jwtUrl;
62
- } else {
63
- restClient = new RESTClient(apiBaseUrl, authJwtToken, true);
64
- }
43
+ return new Promise(async (resolve, reject) => {
44
+ // no valid JWT, but try to use "refresh token" first to retrive new JWT
45
+ var refreshClient = new RESTClient(apiBaseUrl, '');
46
+ await refreshClient.checkRefreshToken(authJwtToken, () => {
47
+ authJwtToken = undefined;
48
+ // ... so repeat authenticate (should lead to /Session login page)
49
+ new IDAS().authenticateWithJwt(authPath);
50
+ });
51
+
52
+ // still not valid JWT -> authenticate
53
+ if (!refreshClient.token) {
54
+ localStorage.setItem('IDAS_AuthJwtCallbackPath', authPath);
55
+ const authEndpoint = (new URL(window.location.href).origin) + authPath;
56
+ let authUrlCallback = `${authEndpoint}?r=%target%&t=%jwt%&m=%mandant%`;
57
+ authUrlCallback = authUrlCallback.replace('%target%', encodeURIComponent(window.location.href));
58
+
59
+ const url = new URL(apiBaseUrl);
60
+ url.pathname = "/Session";
61
+ url.search = `?a=${appToken}&r=${encodeURIComponent(authUrlCallback)}`;
62
+ let jwtUrl = url.toString();
63
+
64
+ window.location = jwtUrl;
65
+ reject('not authenticated yet');
66
+ } else {
67
+ this.authorizeWithJwt(refreshClient.token);
68
+ resolve(this);
69
+ }
70
+ });
65
71
  }
66
72
 
67
73
  mandantGuid = localStorage.getItem('IDAS_MandantGuid');
68
74
 
69
75
  auth = {
76
+ _self: this,
70
77
  async getCurrentAuthToken() {
71
- return await restClient.put('/Login/Update/', { Token: authToken })
78
+ return await this._self.restClient.put('/Login/Update/', { Token: authToken })
72
79
  },
73
80
  };
74
81
 
75
82
  mandanten = {
83
+ _self: this,
76
84
  async getAll() {
77
- return await restClient.get('/Mandanten');
85
+ return await this._self.restClient.get('/Mandanten');
78
86
  },
79
87
  async get(guid) {
80
- return await restClient.get(`/Mandanten/${guid}`);
88
+ return await this._self.restClient.get(`/Mandanten/${guid}`);
81
89
  },
82
90
  async save(m) {
83
- await restClient.put('/Mandanten', m);
91
+ await this._self.restClient.put('/Mandanten', m);
84
92
  },
85
93
  };
86
94
 
87
95
  benutzer = {
96
+ _self: this,
88
97
  async getAll(mandantGuid) {
89
- return await restClient.get(`/BenutzerListe/${mandantGuid }/?mitRollenUndRechten=true`);
98
+ return await this._self.restClient.get(`/BenutzerListe/${mandantGuid }/?mitRollenUndRechten=true`);
90
99
  },
91
100
  async get(guid) {
92
- return await restClient.get(`/Benutzer/${guid}`);
101
+ return await this._self.restClient.get(`/Benutzer/${guid}`);
93
102
  },
94
103
  async save(m) {
95
- await restClient.put('/Benutzer', m);
104
+ await this._self.restClient.put('/Benutzer', m);
96
105
  },
97
106
  };
98
107
 
99
108
  feedback = {
109
+ _self: this,
100
110
  async getAll() {
101
- return await restClient.get('/Feedback/');
111
+ return await this._self.restClient.get('/Feedback/');
102
112
  },
103
113
  async get(guid) {
104
- return await restClient.get(`/Feedback/${guid}`);
114
+ return await this._self.restClient.get(`/Feedback/${guid}`);
105
115
  },
106
116
  async save(m) {
107
- await restClient.put('/Feedback', m);
117
+ await this._self.restClient.put('/Feedback', m);
108
118
  },
109
119
  async comment(guid, commentData) {
110
- await restClient.put(`/FeedbackKommentar/${guid}`, commentData);
120
+ await this._self.restClient.put(`/FeedbackKommentar/${guid}`, commentData);
111
121
  },
112
122
  async attachFile(guid, filename, data) {
113
- await restClient.put(`/FeedbackAttachment/?feedbackGuid=${guid}&filename=${filename}`, data);
123
+ await this._self.restClient.put(`/FeedbackAttachment/?feedbackGuid=${guid}&filename=${filename}`, data);
114
124
  },
115
125
  async deleteFile(guid) {
116
- await restClient.delete(`/FeedbackAttachment/${guid}`);
126
+ await this._self.restClient.delete(`/FeedbackAttachment/${guid}`);
117
127
  },
118
128
  };
119
129
 
120
130
  rollen = {
131
+ _self: this,
121
132
  async getAll() {
122
- return await restClient.get('/Rollen');
133
+ return await this._self.restClient.get('/Rollen');
123
134
  },
124
135
  async save(m) {
125
- await restClient.put('/Rollen', m);
136
+ await this._self.restClient.put('/Rollen', m);
126
137
  },
127
138
  };
128
139
 
129
140
  vorgaenge = {
141
+ _self: this,
130
142
  async getByVorgangsnummer(vorgangsNummer, jahr) {
131
- return await restClient.get(`/Vorgang/${vorgangsNummer}/${jahr}`);
143
+ return await this._self.restClient.get(`/Vorgang/${vorgangsNummer}/${jahr}`);
132
144
  },
133
145
  };
134
146
 
135
147
  positionen = {
148
+ _self: this,
136
149
  async getByPcode(pcode) {
137
- return await restClient.get(`/BelegPositionen/GetByPcode/${pcode}`);
150
+ return await this._self.restClient.get(`/BelegPositionen/GetByPcode/${pcode}`);
138
151
  },
139
152
  async get(guid) {
140
- return await restClient.get(`/BelegPositionen/Get/${guid}`);
153
+ return await this._self.restClient.get(`/BelegPositionen/Get/${guid}`);
141
154
  },
142
155
  };
143
156
  }
package/api/RESTClient.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import axios from 'axios';
2
+ import jwt_decode from 'jwt-decode';
2
3
 
3
4
  /*export let AppToken = "66B70E0B-F7C4-4829-B12A-18AD309E3970";
4
5
  export let AuthToken = localStorage.getItem("AuthToken");
@@ -7,6 +8,8 @@ export let ApiBaseUrl = localStorage.getItem("ApiBaseUrl") || "https://api.dev.i
7
8
  export let SiteBaseUrl = window.location.origin;
8
9
  export let SSOAuthUrl = ApiBaseUrl.replace("/api", '') + "/SSO?a=" + AppToken + "&r=%target%?t=%token%%26m=%mandant%";*/
9
10
 
11
+ let authJwtRefreshToken = localStorage.getItem('IDAS_AuthJwtRefreshToken');
12
+
10
13
  export class RESTClient {
11
14
  lastError = '';
12
15
  token = '';
@@ -22,11 +25,85 @@ export class RESTClient {
22
25
  axios.defaults.headers.common['X-Gdl-AuthToken'] = this.token;
23
26
  }
24
27
 
25
- axios.interceptors.request.use(req => {
26
- return req;
28
+ if (this.token && isJWT) {
29
+ this.updateJwtToken(token);
30
+ }
31
+
32
+ axios.interceptors.request.use(async (config) => {
33
+ await this.checkAuthorizationHeader(config);
34
+ return config;
27
35
  });
28
36
  }
29
37
 
38
+ async checkAuthorizationHeader(config) {
39
+ let authHeader = config.headers['Authorization'];
40
+ if (authHeader && authHeader.toString().startsWith('Bearer ')) {
41
+ let parts = authHeader.toString().split(' ');
42
+ let jwt = parts[1];
43
+ if (!this.isJwtTokenExpired(jwt)) {
44
+ // JWT token is not expired
45
+ return;
46
+ }
47
+
48
+ // expired token - refresh
49
+ await this.checkRefreshToken(jwt);
50
+ }
51
+ }
52
+
53
+ async checkRefreshToken(jwt, authCallback) {
54
+ if (!jwt && authJwtRefreshToken) {
55
+ this.onError = (error, message) => {
56
+ // LoginJwt/Refresh failed, which means "refresh token" is expired/invalid...
57
+ if (message.indexOf("401") != -1 || message.indexOf("403") != -1) {
58
+ authJwtRefreshToken = undefined;
59
+ localStorage.removeItem('IDAS_AuthJwtRefreshToken');
60
+ // ... so repeat authenticate
61
+ authCallback && authCallback();
62
+ }
63
+ };
64
+ // fetch fresh JWT
65
+ await this.refreshToken();
66
+ return;
67
+ }
68
+ this.token = jwt;
69
+ this.isJWT = true;
70
+ }
71
+
72
+ isJwtTokenExpired(jwt) {
73
+ if (!jwt) {
74
+ return true;
75
+ }
76
+
77
+ let decoded = jwt_decode(jwt);
78
+ const utcNow = Date.parse(new Date().toUTCString()) / 1000;
79
+
80
+ if (decoded && decoded.exp >= utcNow) {
81
+ return false;
82
+ }
83
+
84
+ return true;
85
+ }
86
+
87
+ updateJwtToken(jwt) {
88
+ let decoded = jwt_decode(jwt);
89
+ let refreshToken = decoded['refreshToken'];
90
+ localStorage.setItem('IDAS_AuthJwtRefreshToken', refreshToken);
91
+ authJwtRefreshToken = refreshToken;
92
+ this.token = jwt;
93
+ this.isJWT = true;
94
+ }
95
+
96
+ async refreshToken() {
97
+ try {
98
+ await axios.put(this.baseurl + '/LoginJwt/Refresh', { token: localStorage.getItem('IDAS_AuthJwtRefreshToken') })
99
+ .then(resp => {
100
+ this.updateJwtToken(resp.data);
101
+ });
102
+ } catch (error) {
103
+ this.handleError(error);
104
+ }
105
+ }
106
+
30
107
  updateToken(token) {
31
108
  this.token = token;
32
109
  }
package/index.js CHANGED
@@ -12,7 +12,7 @@ export {
12
12
  AddButton, RemoveButton, SaveButton,
13
13
  }
14
14
 
15
- import { IDAS } from './api/IDAS';
15
+ import { IDASFactory } from './api/IDAS';
16
16
  import { RESTClient } from './api/RESTClient';
17
17
 
18
- export { IDAS, RESTClient };
18
+ export { IDASFactory, RESTClient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gandalan/weblibs",
3
- "version": "0.0.41",
3
+ "version": "1.0.1",
4
4
  "description": "WebLibs for Gandalan JS/TS/Svelte projects",
5
5
  "author": "Philipp Reif",
6
6
  "license": "ISC",
@@ -23,6 +23,7 @@
23
23
  "dependencies": {
24
24
  "@mdi/js": "^7.0.96",
25
25
  "axios": "^0.27.2",
26
+ "jwt-decode": "^3.1.2",
26
27
  "svelte-table": "^0.5.1"
27
28
  },
28
29
  "peerDependencies": {