zavadil-ts-common 1.1.39 → 1.1.41

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.
@@ -1,4 +1,4 @@
1
- import {RestClient} from "./RestClient";
1
+ import {RestClient} from "../client/RestClient";
2
2
  import {StringUtil} from "../util";
3
3
 
4
4
  export type TokenRequestPayloadBase = {
@@ -9,8 +9,8 @@ export type RequestAccessTokenPayload = TokenRequestPayloadBase & {
9
9
  idToken: string;
10
10
  }
11
11
 
12
- export type RequestIdTokenFromSessionPayload = TokenRequestPayloadBase & {
13
- sessionId: string;
12
+ export type RequestIdTokenFromPrevTokenPayload = {
13
+ idToken: string;
14
14
  }
15
15
 
16
16
  export type RequestIdTokenFromLoginPayload = TokenRequestPayloadBase & {
@@ -18,12 +18,8 @@ export type RequestIdTokenFromLoginPayload = TokenRequestPayloadBase & {
18
18
  password: string;
19
19
  }
20
20
 
21
- export type RefreshAccessTokenPayload = TokenRequestPayloadBase & {
22
- accessToken: string;
23
- refreshToken: string;
24
- }
25
-
26
21
  export type TokenResponsePayloadBase = {
22
+ issuedAt: Date;
27
23
  expires?: Date | null;
28
24
  }
29
25
 
@@ -47,16 +43,6 @@ export type JwksPayload = {
47
43
  keys: Array<JwKeyPayload>;
48
44
  }
49
45
 
50
- export type SessionPayload = {
51
- session_id: string;
52
- plugin_id: number;
53
- user_id: number;
54
- account_id: number;
55
- server: string;
56
- user_name: string;
57
- timezone: string;
58
- }
59
-
60
46
  /**
61
47
  * This implements rest client for OAuth server - https://github.com/lotcz/oauth-server
62
48
  */
@@ -74,19 +60,12 @@ export class OAuthRestClient extends RestClient {
74
60
  return this.postJson('id-tokens/from-login', request);
75
61
  }
76
62
 
77
- requestIdTokenFromSession(request: RequestIdTokenFromSessionPayload): Promise<IdTokenPayload> {
78
- return this.postJson('id-tokens/from-session', request);
63
+ refreshIdToken(request: RequestIdTokenFromPrevTokenPayload): Promise<IdTokenPayload> {
64
+ return this.postJson('id-tokens/refresh', request);
79
65
  }
80
66
 
81
67
  requestAccessToken(request: RequestAccessTokenPayload): Promise<AccessTokenPayload> {
82
68
  return this.postJson('access-tokens/from-id-token', request);
83
69
  }
84
70
 
85
- refreshAccessToken(request: RefreshAccessTokenPayload): Promise<AccessTokenPayload> {
86
- return this.postJson('access-tokens/refresh', request);
87
- }
88
-
89
- loadSessionById(sessionId: string): Promise<SessionPayload> {
90
- return this.getJson(`sessions/${sessionId}`);
91
- }
92
71
  }
@@ -0,0 +1,110 @@
1
+ import {AccessTokenPayload, IdTokenPayload, OAuthRestClient} from "./OAuthRestClient";
2
+
3
+ /**
4
+ * Manages refresh of id and access tokens.
5
+ */
6
+ export class OAuthTokenManager {
7
+
8
+ oAuthServer: OAuthRestClient;
9
+
10
+ audience: string;
11
+
12
+ idToken?: IdTokenPayload;
13
+
14
+ accessToken?: AccessTokenPayload;
15
+
16
+ constructor(oAuthServerBaseUrl: string, targetAudience: string) {
17
+ this.audience = targetAudience;
18
+ this.oAuthServer = new OAuthRestClient(oAuthServerBaseUrl);
19
+ }
20
+
21
+ isTokenExpired(expires?: Date | null): boolean {
22
+ if (expires === undefined || expires === null) return false;
23
+ const now = Date.now();
24
+ const exp = new Date(expires).getTime();
25
+ return (exp < now);
26
+ }
27
+
28
+ isTokenReadyForRefresh(issuedAt: Date, expires?: Date | null): boolean {
29
+ if (expires === undefined || expires === null) return false;
30
+ const middle = new Date((expires.getTime() + issuedAt.getTime()) / 2);
31
+ const now = new Date();
32
+ return (middle < now);
33
+ }
34
+
35
+ isValidIdToken(idToken?: IdTokenPayload): boolean {
36
+ return idToken !== undefined && !this.isTokenExpired(idToken.expires);
37
+ }
38
+
39
+ hasValidIdToken(): boolean {
40
+ return this.isValidIdToken(this.idToken);
41
+ }
42
+
43
+ isValidAccessToken(accessToken?: AccessTokenPayload): boolean {
44
+ return accessToken !== undefined && !this.isTokenExpired(accessToken.expires);
45
+ }
46
+
47
+ hasValidAccessToken(): boolean {
48
+ return this.isValidAccessToken(this.accessToken);
49
+ }
50
+
51
+ reset() {
52
+ this.idToken = undefined;
53
+ this.accessToken = undefined;
54
+ }
55
+
56
+ getIdToken(): Promise<string> {
57
+ if (this.idToken === undefined || !this.hasValidIdToken()) {
58
+ return Promise.reject("No valid ID token!");
59
+ }
60
+ if (this.isTokenReadyForRefresh(this.idToken.issuedAt, this.idToken.expires)) {
61
+ return this.oAuthServer
62
+ .refreshIdToken({idToken: this.idToken.idToken})
63
+ .then(
64
+ (t) => {
65
+ if (!this.isValidIdToken(t)) {
66
+ return Promise.reject("Received ID token is not valid!");
67
+ }
68
+ this.idToken = t;
69
+ return t.idToken;
70
+ }
71
+ )
72
+ }
73
+ return Promise.resolve(this.idToken.idToken);
74
+ }
75
+
76
+ login(login: string, password: string): Promise<boolean> {
77
+ this.reset();
78
+ return this.oAuthServer.requestIdTokenFromLogin({login: login, password: password, targetAudience: this.audience})
79
+ .then(
80
+ (t) => {
81
+ if (!this.isValidIdToken(t)) {
82
+ return Promise.reject("Received ID token is not valid!");
83
+ }
84
+ this.idToken = t;
85
+ return true;
86
+ })
87
+ .catch((e) => false);
88
+ }
89
+
90
+ getAccessToken(): Promise<string> {
91
+ if (this.hasValidAccessToken()) {
92
+ return Promise.resolve(String(this.accessToken?.accessToken));
93
+ } else {
94
+ return this.getIdToken()
95
+ .then(
96
+ (idToken: string) => this.oAuthServer
97
+ .requestAccessToken({idToken: idToken, targetAudience: this.audience})
98
+ .then((act: AccessTokenPayload) => {
99
+ if (!this.isValidAccessToken(act)) {
100
+ return Promise.reject("Received access token is not valid!");
101
+ }
102
+ this.accessToken = act;
103
+ return act.accessToken;
104
+ })
105
+ );
106
+ }
107
+ }
108
+
109
+ }
110
+
@@ -0,0 +1,61 @@
1
+ import {OAuthTokenManager} from "./OAuthTokenManager";
2
+ import { RestClient, RestClientHeaders } from "../client/RestClient";
3
+
4
+ export type ServerOAuthInfoPayload = {
5
+ debugMode?: boolean;
6
+ targetAudience: string;
7
+ oauthServerUrl: string;
8
+ version: string;
9
+ }
10
+
11
+ export class RestClientWithOAuth extends RestClient {
12
+
13
+ private tokenManager?: OAuthTokenManager;
14
+
15
+ private serverInfo?: ServerOAuthInfoPayload;
16
+
17
+ private insecureClient: RestClient;
18
+
19
+ constructor(url: string) {
20
+ super(url);
21
+ // rest client without OAuth headers
22
+ this.insecureClient = new RestClient(url);
23
+ }
24
+
25
+ getServerInfo(): Promise<ServerOAuthInfoPayload> {
26
+ if (this.serverInfo !== undefined) {
27
+ return Promise.resolve(this.serverInfo);
28
+ }
29
+ return this.insecureClient.getJson('status/info')
30
+ .then((si: ServerOAuthInfoPayload) => {
31
+ this.serverInfo = si;
32
+ return this.serverInfo;
33
+ });
34
+ }
35
+
36
+ getTokenManager(): Promise<OAuthTokenManager> {
37
+ if (this.tokenManager !== undefined) {
38
+ return Promise.resolve(this.tokenManager);
39
+ }
40
+ return this.getServerInfo()
41
+ .then((info) => new OAuthTokenManager(info.oauthServerUrl, info.targetAudience));
42
+ }
43
+
44
+ login(login: string, password: string): Promise<boolean> {
45
+ return this.getTokenManager().then((m) => m.login(login, password));
46
+ }
47
+
48
+ getHeaders(): Promise<RestClientHeaders> {
49
+ return this.getTokenManager()
50
+ .then(sm => sm.getAccessToken())
51
+ .then(
52
+ (accessToken) => {
53
+ return {
54
+ 'Content-Type': 'application/json',
55
+ 'Authorization': `Bearer ${accessToken}`
56
+ }
57
+ }
58
+ );
59
+ }
60
+
61
+ }
@@ -0,0 +1,6 @@
1
+ export * from './OAuthRestClient';
2
+ export * from './OAuthTokenManager';
3
+ export * from './OAuthSubject';
4
+ export * from './OAuthTokenManager';
5
+ export * from './RestClientWithOAuth';
6
+
@@ -1,143 +0,0 @@
1
- import {AccessTokenPayload, IdTokenPayload, OAuthRestClient, SessionPayload} from "./OAuthRestClient";
2
-
3
- export class OAuthSessionManager {
4
-
5
- oAuthServer: OAuthRestClient;
6
-
7
- audience: string;
8
-
9
- idToken?: IdTokenPayload;
10
-
11
- accessToken?: AccessTokenPayload;
12
-
13
- sessionId?: string;
14
-
15
- session?: SessionPayload;
16
-
17
- constructor(oAuthServerBaseUrl: string, targetAudience: string) {
18
- this.audience = targetAudience;
19
- this.oAuthServer = new OAuthRestClient(oAuthServerBaseUrl);
20
- setInterval(() => this.checkAccessTokenRefresh(), 5000);
21
- }
22
-
23
- isTokenExpired(expires?: Date | null): boolean {
24
- if (expires === undefined || expires === null) return false;
25
- const now = Date.now();
26
- const exp = new Date(expires).getTime();
27
- return (exp < now);
28
- }
29
-
30
- isTokenReadyForRefresh(expires?: Date | null): boolean {
31
- if (expires === undefined || expires === null) return false;
32
- const now = Date.now();
33
- const exp = new Date(expires).getTime() - (1000 * 10);
34
- return (exp < now);
35
- }
36
-
37
- isValidIdToken(idToken?: IdTokenPayload): boolean {
38
- return idToken !== undefined && !this.isTokenExpired(idToken.expires);
39
- }
40
-
41
- hasValidIdToken(): boolean {
42
- return this.isValidIdToken(this.idToken);
43
- }
44
-
45
- isValidAccessToken(accessToken?: AccessTokenPayload): boolean {
46
- return accessToken !== undefined && !this.isTokenExpired(accessToken.expires);
47
- }
48
-
49
- hasValidAccessToken(): boolean {
50
- return this.isValidAccessToken(this.accessToken);
51
- }
52
-
53
- reset() {
54
- this.sessionId = undefined;
55
- this.session = undefined;
56
- this.idToken = undefined;
57
- this.accessToken = undefined;
58
- }
59
-
60
- initializeSessionId(sessionId: string) {
61
- this.reset();
62
- this.sessionId = sessionId;
63
- }
64
-
65
- getSession(): Promise<SessionPayload> {
66
- if (this.session !== undefined) {
67
- return Promise.resolve(this.session);
68
- } else {
69
- if (this.sessionId === undefined) {
70
- return Promise.reject("No session ID!");
71
- }
72
- return this.oAuthServer
73
- .loadSessionById(this.sessionId)
74
- .then((session: SessionPayload) => {
75
- this.session = session;
76
- return this.session;
77
- });
78
- }
79
- }
80
-
81
- getIdToken(): Promise<string> {
82
- if (this.hasValidIdToken()) {
83
- return Promise.resolve(String(this.idToken?.idToken));
84
- } else {
85
- if (this.sessionId === undefined) {
86
- return Promise.reject("No session ID!");
87
- }
88
- return this.oAuthServer
89
- .requestIdTokenFromSession({sessionId: this.sessionId, targetAudience: this.audience})
90
- .then((idToken: IdTokenPayload) => {
91
- if (!this.isValidIdToken(idToken)) {
92
- return Promise.reject("Received ID token is not valid!");
93
- }
94
- this.idToken = idToken;
95
- })
96
- .then(() => this.getIdToken());
97
- }
98
- }
99
-
100
- getAccessToken(): Promise<string> {
101
- if (this.hasValidAccessToken()) {
102
- return Promise.resolve(String(this.accessToken?.accessToken));
103
- } else {
104
- return this.getIdToken()
105
- .then(
106
- (idToken: string) => this.oAuthServer
107
- .requestAccessToken({idToken: idToken, targetAudience: this.audience})
108
- .then((act: AccessTokenPayload) => {
109
- if (!this.isValidAccessToken(act)) {
110
- return Promise.reject("Received access token is not valid!");
111
- }
112
- this.accessToken = act;
113
- })
114
- )
115
- .then(() => this.getAccessToken());
116
- }
117
- }
118
-
119
- refreshAccessToken() {
120
- if (this.accessToken === undefined) throw new Error("No access token to be refreshed!");
121
- this.oAuthServer.refreshAccessToken(
122
- {
123
- targetAudience: this.audience,
124
- accessToken: this.accessToken?.accessToken,
125
- refreshToken: this.accessToken?.refreshToken
126
- }
127
- ).then(
128
- (act: AccessTokenPayload) => {
129
- if (!this.isValidAccessToken(act)) {
130
- throw new Error("Refreshed access token is not valid!");
131
- }
132
- this.accessToken = act;
133
- }
134
- )
135
- }
136
-
137
- checkAccessTokenRefresh() {
138
- if (this.hasValidAccessToken() && this.isTokenReadyForRefresh(this.accessToken?.expires)) {
139
- this.refreshAccessToken();
140
- }
141
- }
142
- }
143
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes