zavadil-ts-common 1.1.48 → 1.1.50
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/dist/cache/CacheAsync.d.ts +1 -1
- package/dist/cache/HashCacheAsync.d.ts +4 -3
- package/dist/cache/LazyAsync.d.ts +1 -0
- package/dist/client/RestClient.d.ts +2 -2
- package/dist/index.d.ts +62 -44
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/oauth/OAuthRestClient.d.ts +1 -1
- package/dist/oauth/OAuthTokenManager.d.ts +5 -4
- package/dist/oauth/RestClientWithOAuth.d.ts +20 -4
- package/package.json +1 -1
- package/src/cache/CacheAsync.ts +3 -2
- package/src/cache/HashCacheAsync.ts +4 -4
- package/src/cache/LazyAsync.ts +11 -4
- package/src/client/RestClient.ts +10 -10
- package/src/oauth/OAuthRestClient.ts +1 -1
- package/src/oauth/OAuthTokenManager.ts +29 -24
- package/src/oauth/RestClientWithOAuth.ts +80 -20
package/src/client/RestClient.ts
CHANGED
@@ -18,7 +18,7 @@ export class RestClient {
|
|
18
18
|
/**
|
19
19
|
* Override this to customize http headers.
|
20
20
|
*/
|
21
|
-
getHeaders(): Promise<RestClientHeaders> {
|
21
|
+
getHeaders(url: string): Promise<RestClientHeaders> {
|
22
22
|
return Promise.resolve(
|
23
23
|
{
|
24
24
|
'Content-Type': 'application/json'
|
@@ -46,8 +46,8 @@ export class RestClient {
|
|
46
46
|
return url;
|
47
47
|
}
|
48
48
|
|
49
|
-
getRequestOptions(method: string = 'GET', data: object | null = null): Promise<RequestInit> {
|
50
|
-
return this.getHeaders()
|
49
|
+
getRequestOptions(url: string, method: string = 'GET', data: object | null = null): Promise<RequestInit> {
|
50
|
+
return this.getHeaders(url)
|
51
51
|
.then(
|
52
52
|
(headers) => {
|
53
53
|
return {
|
@@ -116,31 +116,31 @@ export class RestClient {
|
|
116
116
|
if (params) {
|
117
117
|
url = `${url}${this.paramsToQueryString(params)}`;
|
118
118
|
}
|
119
|
-
return this.getRequestOptions().then(o => this.processRequestJson(url, o));
|
119
|
+
return this.getRequestOptions(url).then(o => this.processRequestJson(url, o));
|
120
120
|
}
|
121
121
|
|
122
122
|
postJson(url: string, data: object | null = null): Promise<any> {
|
123
|
-
return this.getRequestOptions('POST', data).then(o => this.processRequestJson(url, o));
|
123
|
+
return this.getRequestOptions(url, 'POST', data).then(o => this.processRequestJson(url, o));
|
124
124
|
}
|
125
125
|
|
126
126
|
putJson(url: string, data: object | null = null): Promise<any> {
|
127
|
-
return this.getRequestOptions('PUT', data).then(o => this.processRequestJson(url, o));
|
127
|
+
return this.getRequestOptions(url, 'PUT', data).then(o => this.processRequestJson(url, o));
|
128
128
|
}
|
129
129
|
|
130
130
|
get(url: string): Promise<Response> {
|
131
|
-
return this.getRequestOptions().then(o => this.processRequest(url, o));
|
131
|
+
return this.getRequestOptions(url).then(o => this.processRequest(url, o));
|
132
132
|
}
|
133
133
|
|
134
134
|
del(url: string): Promise<Response> {
|
135
|
-
return this.getRequestOptions('DELETE').then(o => this.processRequest(url, o));
|
135
|
+
return this.getRequestOptions(url, 'DELETE').then(o => this.processRequest(url, o));
|
136
136
|
}
|
137
137
|
|
138
138
|
post(url: string, data: object | null = null): Promise<Response> {
|
139
|
-
return this.getRequestOptions('POST', data).then(o => this.processRequest(url, o));
|
139
|
+
return this.getRequestOptions(url, 'POST', data).then(o => this.processRequest(url, o));
|
140
140
|
}
|
141
141
|
|
142
142
|
put(url: string, data: object | null = null): Promise<Response> {
|
143
|
-
return this.getRequestOptions('PUT', data).then(o => this.processRequest(url, o));
|
143
|
+
return this.getRequestOptions(url, 'PUT', data).then(o => this.processRequest(url, o));
|
144
144
|
}
|
145
145
|
|
146
146
|
}
|
@@ -7,6 +7,7 @@ export type TokenRequestPayloadBase = {
|
|
7
7
|
|
8
8
|
export type RequestAccessTokenPayload = TokenRequestPayloadBase & {
|
9
9
|
idToken: string;
|
10
|
+
privilege: string;
|
10
11
|
}
|
11
12
|
|
12
13
|
export type RequestIdTokenFromPrevTokenPayload = {
|
@@ -29,7 +30,6 @@ export type IdTokenPayload = TokenResponsePayloadBase & {
|
|
29
30
|
|
30
31
|
export type AccessTokenPayload = TokenResponsePayloadBase & {
|
31
32
|
accessToken: string;
|
32
|
-
refreshToken: string;
|
33
33
|
}
|
34
34
|
|
35
35
|
export type JwKeyPayload = {
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {AccessTokenPayload, IdTokenPayload, OAuthRestClient
|
1
|
+
import {AccessTokenPayload, IdTokenPayload, OAuthRestClient} from "./OAuthRestClient";
|
2
2
|
import {EventManager} from "../component";
|
3
3
|
|
4
4
|
/**
|
@@ -14,14 +14,15 @@ export class OAuthTokenManager {
|
|
14
14
|
|
15
15
|
idToken?: IdTokenPayload;
|
16
16
|
|
17
|
-
|
17
|
+
accessTokens: Map<string, AccessTokenPayload>;
|
18
18
|
|
19
19
|
constructor(oAuthServerBaseUrl: string, targetAudience: string) {
|
20
20
|
this.audience = targetAudience;
|
21
21
|
this.oAuthServer = new OAuthRestClient(oAuthServerBaseUrl);
|
22
|
+
this.accessTokens = new Map<string, AccessTokenPayload>();
|
22
23
|
}
|
23
24
|
|
24
|
-
addIdTokenChangedHandler(handler: () => any) {
|
25
|
+
addIdTokenChangedHandler(handler: (t: IdTokenPayload) => any) {
|
25
26
|
this.eventManager.addEventListener('id-token-changed', handler);
|
26
27
|
}
|
27
28
|
|
@@ -51,13 +52,13 @@ export class OAuthTokenManager {
|
|
51
52
|
return accessToken !== undefined && !this.isTokenExpired(accessToken.expires);
|
52
53
|
}
|
53
54
|
|
54
|
-
hasValidAccessToken(): boolean {
|
55
|
-
return this.isValidAccessToken(this.
|
55
|
+
hasValidAccessToken(privilege: string): boolean {
|
56
|
+
return this.isValidAccessToken(this.accessTokens.get(privilege));
|
56
57
|
}
|
57
58
|
|
58
59
|
reset() {
|
59
60
|
this.setIdToken(undefined);
|
60
|
-
this.
|
61
|
+
this.accessTokens.clear();
|
61
62
|
}
|
62
63
|
|
63
64
|
getIdToken(): Promise<string> {
|
@@ -82,7 +83,7 @@ export class OAuthTokenManager {
|
|
82
83
|
throw new Error("Received ID token is not valid!");
|
83
84
|
}
|
84
85
|
this.idToken = token;
|
85
|
-
this.eventManager.triggerEvent('id-token-changed');
|
86
|
+
this.eventManager.triggerEvent('id-token-changed', token);
|
86
87
|
}
|
87
88
|
|
88
89
|
verifyIdToken(token: string): Promise<boolean> {
|
@@ -101,23 +102,27 @@ export class OAuthTokenManager {
|
|
101
102
|
})
|
102
103
|
}
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
105
|
+
private getAccessTokenInternal(privilege: string): Promise<AccessTokenPayload> {
|
106
|
+
return this.getIdToken()
|
107
|
+
.then(
|
108
|
+
(idToken: string) => this.oAuthServer
|
109
|
+
.requestAccessToken({idToken: idToken, targetAudience: this.audience, privilege: privilege})
|
110
|
+
.then((act: AccessTokenPayload) => {
|
111
|
+
if (!this.isValidAccessToken(act)) {
|
112
|
+
return Promise.reject("Received access token is not valid!");
|
113
|
+
}
|
114
|
+
this.accessTokens.set(privilege, act);
|
115
|
+
return act;
|
116
|
+
})
|
117
|
+
);
|
118
|
+
}
|
119
|
+
|
120
|
+
getAccessToken(privilege: string): Promise<string> {
|
121
|
+
const existing = this.accessTokens.get(privilege);
|
122
|
+
if (existing === undefined || !this.isValidAccessToken(existing)) return this.getAccessTokenInternal(privilege).then((t) => t.accessToken);
|
123
|
+
// preload access token if it is going to expire soon
|
124
|
+
if (this.isTokenReadyForRefresh(existing.issuedAt, existing.expires)) this.getAccessTokenInternal(privilege);
|
125
|
+
return Promise.resolve(existing.accessToken);
|
121
126
|
}
|
122
127
|
|
123
128
|
}
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import {OAuthTokenManager} from "./OAuthTokenManager";
|
2
|
-
import {
|
2
|
+
import {RestClient, RestClientHeaders} from "../client";
|
3
3
|
import {IdTokenPayload} from "./OAuthRestClient";
|
4
|
+
import {LazyAsync} from "../cache";
|
5
|
+
import {StringUtil} from "../util";
|
4
6
|
|
5
7
|
export type ServerOAuthInfoPayload = {
|
6
8
|
debugMode?: boolean;
|
@@ -11,39 +13,87 @@ export type ServerOAuthInfoPayload = {
|
|
11
13
|
|
12
14
|
export class RestClientWithOAuth extends RestClient {
|
13
15
|
|
14
|
-
private
|
16
|
+
private insecureClient: RestClient;
|
15
17
|
|
16
|
-
private
|
18
|
+
private tokenManager: LazyAsync<OAuthTokenManager>;
|
17
19
|
|
18
|
-
private
|
20
|
+
private serverInfo: LazyAsync<ServerOAuthInfoPayload>;
|
19
21
|
|
20
|
-
|
22
|
+
private defaultPrivilege: string;
|
23
|
+
|
24
|
+
constructor(url: string, defaultPrivilege: string = '*') {
|
21
25
|
super(url);
|
26
|
+
this.defaultPrivilege = defaultPrivilege;
|
27
|
+
|
22
28
|
// rest client without OAuth headers
|
23
29
|
this.insecureClient = new RestClient(url);
|
30
|
+
|
31
|
+
this.serverInfo = new LazyAsync<ServerOAuthInfoPayload>(() => this.getServerInfoInternal());
|
32
|
+
this.tokenManager = new LazyAsync<OAuthTokenManager>(() => this.getTokenManagerInternal());
|
33
|
+
|
34
|
+
// initialize
|
35
|
+
const urlToken = this.getIdTokenFromUrl();
|
36
|
+
if (urlToken !== null) {
|
37
|
+
this.setIdTokenRaw(urlToken);
|
38
|
+
} else {
|
39
|
+
const storageToken = this.getIdTokenFromLocalStorage();
|
40
|
+
if (storageToken) this.setIdToken(storageToken);
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Override this if a different privilege is needed for different endpoints
|
46
|
+
* @param url
|
47
|
+
*/
|
48
|
+
getPrivilege(url: string): string {
|
49
|
+
return this.defaultPrivilege;
|
50
|
+
}
|
51
|
+
|
52
|
+
getIdTokenFromUrl(): string | null {
|
53
|
+
const up = new URLSearchParams(document.location.search);
|
54
|
+
return up.get('token');
|
55
|
+
}
|
56
|
+
|
57
|
+
getIdTokenFromLocalStorage(): IdTokenPayload | null {
|
58
|
+
const raw = localStorage.getItem('id-token');
|
59
|
+
if (!raw) return null;
|
60
|
+
return JSON.parse(raw);
|
61
|
+
}
|
62
|
+
|
63
|
+
saveIdTokenToLocalStorage(token: IdTokenPayload | null) {
|
64
|
+
const raw = token ? JSON.stringify(token) : null;
|
65
|
+
if (raw === null) {
|
66
|
+
localStorage.removeItem('id-token');
|
67
|
+
return;
|
68
|
+
}
|
69
|
+
localStorage.setItem('id-token', raw);
|
24
70
|
}
|
25
71
|
|
26
72
|
addIdTokenChangedHandler(handler: () => any) {
|
27
73
|
this.getTokenManager().then((m) => m.addIdTokenChangedHandler(handler));
|
28
74
|
}
|
29
75
|
|
76
|
+
private getServerInfoInternal(): Promise<ServerOAuthInfoPayload> {
|
77
|
+
return this.insecureClient.getJson('status/info');
|
78
|
+
}
|
79
|
+
|
30
80
|
getServerInfo(): Promise<ServerOAuthInfoPayload> {
|
31
|
-
|
32
|
-
return Promise.resolve(this.serverInfo);
|
33
|
-
}
|
34
|
-
return this.insecureClient.getJson('status/info')
|
35
|
-
.then((si: ServerOAuthInfoPayload) => {
|
36
|
-
this.serverInfo = si;
|
37
|
-
return this.serverInfo;
|
38
|
-
});
|
81
|
+
return this.serverInfo.get();
|
39
82
|
}
|
40
83
|
|
41
|
-
|
42
|
-
if (this.tokenManager !== undefined) {
|
43
|
-
return Promise.resolve(this.tokenManager);
|
44
|
-
}
|
84
|
+
private getTokenManagerInternal(): Promise<OAuthTokenManager> {
|
45
85
|
return this.getServerInfo()
|
46
|
-
.then(
|
86
|
+
.then(
|
87
|
+
(info) => {
|
88
|
+
const tm = new OAuthTokenManager(info.oauthServerUrl, info.targetAudience);
|
89
|
+
tm.addIdTokenChangedHandler((t: IdTokenPayload) => this.saveIdTokenToLocalStorage(t));
|
90
|
+
return tm;
|
91
|
+
}
|
92
|
+
);
|
93
|
+
}
|
94
|
+
|
95
|
+
getTokenManager(): Promise<OAuthTokenManager> {
|
96
|
+
return this.tokenManager.get();
|
47
97
|
}
|
48
98
|
|
49
99
|
login(login: string, password: string): Promise<boolean> {
|
@@ -61,9 +111,9 @@ export class RestClientWithOAuth extends RestClient {
|
|
61
111
|
.then(m => m.verifyIdToken(token))
|
62
112
|
}
|
63
113
|
|
64
|
-
getHeaders(): Promise<RestClientHeaders> {
|
114
|
+
getHeaders(url: string): Promise<RestClientHeaders> {
|
65
115
|
return this.getTokenManager()
|
66
|
-
.then(
|
116
|
+
.then(tm => tm.getAccessToken(this.getPrivilege(url)))
|
67
117
|
.then(
|
68
118
|
(accessToken) => {
|
69
119
|
return {
|
@@ -74,4 +124,14 @@ export class RestClientWithOAuth extends RestClient {
|
|
74
124
|
);
|
75
125
|
}
|
76
126
|
|
127
|
+
/**
|
128
|
+
* Try to obtain access token, then return true or throw an Error.
|
129
|
+
* This is basically only used when initializing and trying to determine whether we need to redirect user to login page
|
130
|
+
*/
|
131
|
+
checkAccessToken(privilege?: string): Promise<boolean> {
|
132
|
+
return this.getTokenManager()
|
133
|
+
.then(tm => tm.getAccessToken(StringUtil.getNonEmpty(privilege, this.defaultPrivilege)))
|
134
|
+
.then((accessToken) => true);
|
135
|
+
}
|
136
|
+
|
77
137
|
}
|