@trimble-oss/trimble-id-react 1.0.0 → 1.0.2-rc1
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 +26 -2
- package/dist/TIDClient/TIDClient.d.ts +24 -6
- package/dist/TIDClient/constants.d.ts +6 -0
- package/dist/TIDClient/exceptions.d.ts +3 -0
- package/dist/TIDClient/interfaces.d.ts +18 -0
- package/dist/TIDClient/storage/cookies/CookiesManager.d.ts +16 -2
- package/dist/index.d.ts +1 -1
- package/dist/trimble-id-react.es.js +385 -282
- package/dist/trimble-id-react.umd.js +1 -1
- package/package.json +17 -16
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ process related to the authentication for you. Configure the SDK by wrapping you
|
|
|
67
67
|
|
|
68
68
|
Here TIDProvider can take two parameters :
|
|
69
69
|
* **tidClient** : TID client instance. You can send an instance of the TID Client if you want to handle the initialization yourself
|
|
70
|
-
* **onRedirectCallback** - When the redirect callback occur this function will be call once the user is login using the TIDClient. This
|
|
70
|
+
* **onRedirectCallback** - When the redirect callback occur this function will be call once the user is login using the TIDClient. This function receives an `authState` parameter that contains a `returnTo` property with the user's original location before authentication, allowing you to redirect the user back to their intended destination after login.
|
|
71
71
|
|
|
72
72
|
|
|
73
73
|
After wrapping your app with the TIDProvider, you have to configure the TID credentials registered in TrimbleCloud console. There are two ways of doing this:
|
|
@@ -102,6 +102,15 @@ After wrapping your app with the TIDProvider, you have to configure the TID cred
|
|
|
102
102
|
</TIDProvider>
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
+
```tsx
|
|
106
|
+
const handleRedirect = (authState: AuthState) => {
|
|
107
|
+
// Use returnTo for automatic redirection to original location
|
|
108
|
+
const redirectTo = authState.returnTo || '/dashboard'
|
|
109
|
+
// Navigate to the intended destination
|
|
110
|
+
navigate(redirectTo)
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
105
114
|
Below are the parameters of TIDClient.
|
|
106
115
|
### 1. TID Client configurations:
|
|
107
116
|
|
|
@@ -131,6 +140,21 @@ const {logout}= useAuth()
|
|
|
131
140
|
await logout()
|
|
132
141
|
```
|
|
133
142
|
|
|
143
|
+
### handleCallback
|
|
144
|
+
|
|
145
|
+
Handle OAuth callback to complete user authentication and returns the auth state with redirect path.
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
const { handleCallback } = useAuth()
|
|
149
|
+
|
|
150
|
+
// Handle callback and get redirect information
|
|
151
|
+
const authState = await handleCallback()
|
|
152
|
+
|
|
153
|
+
// Use returnTo for automatic redirection to original location
|
|
154
|
+
const redirectTo = authState.returnTo || '/dashboard'
|
|
155
|
+
navigate(redirectTo)
|
|
156
|
+
```
|
|
157
|
+
|
|
134
158
|
### isAuthenticated
|
|
135
159
|
|
|
136
160
|
True if the user is authenticated.
|
|
@@ -198,7 +222,7 @@ See here for [Sample Code](https://github.com/trimble-oss/trimble-id-sdk-docs-fo
|
|
|
198
222
|
|
|
199
223
|
## Release notes
|
|
200
224
|
|
|
201
|
-
See here for [releases](
|
|
225
|
+
See here for [releases](https://github.com/trimble-oss/trimble-id-sdk-docs-for-react/blob/main/release-notes/CHANGELOG.md)
|
|
202
226
|
|
|
203
227
|
## Raise an issue
|
|
204
228
|
|
|
@@ -90,16 +90,15 @@ export declare class TIDClient {
|
|
|
90
90
|
* @type {string}
|
|
91
91
|
*/
|
|
92
92
|
private readonly redirectUrl;
|
|
93
|
-
/**
|
|
94
|
-
* AnalyticsHttpClient for sending events
|
|
95
|
-
* @type {AnalyticsHttpClient}
|
|
96
|
-
*/
|
|
97
|
-
private readonly analyticshttpclient;
|
|
98
93
|
/**
|
|
99
94
|
* Create a TID client to handle manage all user authentication functions and information
|
|
100
95
|
* @param {CacheManagerOptions} props - TID client configuration
|
|
101
96
|
*/
|
|
102
97
|
constructor(props: TIDClientOptions);
|
|
98
|
+
/**
|
|
99
|
+
* Clean up expired state payloads to prevent storage bloat
|
|
100
|
+
*/
|
|
101
|
+
private cleanupExpiredState;
|
|
103
102
|
/**
|
|
104
103
|
* Redirect the user to TID using the browser
|
|
105
104
|
* @param {LoginWithRedirectOptions} options - Custom configuration for the redirection
|
|
@@ -107,6 +106,7 @@ export declare class TIDClient {
|
|
|
107
106
|
* @example No configuration
|
|
108
107
|
* loginWithRedirect()
|
|
109
108
|
* // Automatically redirects the user to TID with all necessary parameters
|
|
109
|
+
* // After authentication, user will be redirected back to the current page
|
|
110
110
|
* @example Custom redirect
|
|
111
111
|
* loginWithRedirect({onRedirect: (url) => router.navigate(url)})
|
|
112
112
|
* // Redirect calls onRedirect with the log-out url for TID
|
|
@@ -116,8 +116,9 @@ export declare class TIDClient {
|
|
|
116
116
|
/**
|
|
117
117
|
* Authenticated the user using the url callback params
|
|
118
118
|
* @param {string} url - Custom configuration for the redirection
|
|
119
|
-
* @return {Promise<AuthState>} Object contain the state returned from TID
|
|
119
|
+
* @return {Promise<AuthState>} Object contain the state returned from TID and redirect path
|
|
120
120
|
* @throws {CodeVerifierNotFoundException} Will throw an exception if the session doesn't contain the code verifier
|
|
121
|
+
* @throws {Error} Will throw an exception if state validation fails or replay attack is detected
|
|
121
122
|
* @example No configuration
|
|
122
123
|
* handleCallback()
|
|
123
124
|
* // Will automatically take the url from the browser and try to log in the user
|
|
@@ -133,6 +134,23 @@ export declare class TIDClient {
|
|
|
133
134
|
*/
|
|
134
135
|
private generateToken;
|
|
135
136
|
private reloadCodeVerifier;
|
|
137
|
+
/**
|
|
138
|
+
* Create and encode state payload for replay attack protection
|
|
139
|
+
* @param {string} redirectTo - Path to redirect to after authentication
|
|
140
|
+
* @return {string} - Base64 encoded state payload
|
|
141
|
+
*/
|
|
142
|
+
private createStatePayload;
|
|
143
|
+
/**
|
|
144
|
+
* Generate a random nonce for state validation
|
|
145
|
+
* @return {string} - Random nonce
|
|
146
|
+
*/
|
|
147
|
+
private generateNonce;
|
|
148
|
+
/**
|
|
149
|
+
* Validate state payload to prevent replay attacks
|
|
150
|
+
* @param {string} receivedState - State received from OAuth callback
|
|
151
|
+
* @return {StateValidationResult} - Validation result with redirect path if valid
|
|
152
|
+
*/
|
|
153
|
+
private validateStatePayload;
|
|
136
154
|
/**
|
|
137
155
|
* Return the user stored in cache
|
|
138
156
|
* @return {Promise<TIDUser | undefined>} User in cache
|
|
@@ -8,3 +8,9 @@ export declare const CLOCK_SKEW_TIME: number;
|
|
|
8
8
|
* @type {Array<string>}
|
|
9
9
|
*/
|
|
10
10
|
export declare const LIST_PARAMS_REQUIRED: Array<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Maximum time allowed for state parameter to be valid (10 minutes)
|
|
13
|
+
* Used to prevent replay attacks
|
|
14
|
+
* @type {number}
|
|
15
|
+
*/
|
|
16
|
+
export declare const STATE_EXPIRATION_TIME: number;
|
|
@@ -6,3 +6,6 @@ export declare class TokenNotFoundException extends Error {
|
|
|
6
6
|
}
|
|
7
7
|
export declare class CodeVerifierNotFoundException extends Error {
|
|
8
8
|
}
|
|
9
|
+
/** Class representing an OAuth state validation exception for CSRF protection */
|
|
10
|
+
export declare class StateValidationException extends Error {
|
|
11
|
+
}
|
|
@@ -51,6 +51,8 @@ export interface TIDUser {
|
|
|
51
51
|
email?: string;
|
|
52
52
|
/** True if the End-User's e-mail address has been verified; otherwise false. */
|
|
53
53
|
email_verified?: boolean;
|
|
54
|
+
/** Account id of the user */
|
|
55
|
+
account_id?: string;
|
|
54
56
|
}
|
|
55
57
|
export interface TIDJWTUser {
|
|
56
58
|
/**
|
|
@@ -172,7 +174,23 @@ export interface TIDJWTUser {
|
|
|
172
174
|
* @type {string}
|
|
173
175
|
*/
|
|
174
176
|
data_region: string;
|
|
177
|
+
/**
|
|
178
|
+
* Account id of the user
|
|
179
|
+
* @type {string}
|
|
180
|
+
*/
|
|
181
|
+
account_id: string;
|
|
175
182
|
}
|
|
176
183
|
export interface AuthState {
|
|
177
184
|
authState: any;
|
|
185
|
+
returnTo?: string;
|
|
186
|
+
}
|
|
187
|
+
export interface StatePayload {
|
|
188
|
+
redirectTo: string;
|
|
189
|
+
timestamp: number;
|
|
190
|
+
nonce: string;
|
|
191
|
+
}
|
|
192
|
+
export interface StateValidationResult {
|
|
193
|
+
isValid: boolean;
|
|
194
|
+
redirectTo?: string;
|
|
195
|
+
error?: string;
|
|
178
196
|
}
|
|
@@ -12,6 +12,11 @@ interface CookieValue {
|
|
|
12
12
|
* @type {string}
|
|
13
13
|
*/
|
|
14
14
|
code_verifier: string;
|
|
15
|
+
/**
|
|
16
|
+
* State information used for replay attack protection
|
|
17
|
+
* @type {string}
|
|
18
|
+
*/
|
|
19
|
+
state_payload?: string;
|
|
15
20
|
}
|
|
16
21
|
/** Class to manage cookies */
|
|
17
22
|
export declare class CookiesManager {
|
|
@@ -32,9 +37,18 @@ export declare class CookiesManager {
|
|
|
32
37
|
constructor(options: CookiesManagerOptions);
|
|
33
38
|
/**
|
|
34
39
|
* Store cookies in the browser
|
|
35
|
-
* @param {CookieValue} value - information to store
|
|
40
|
+
* @param {Partial<CookieValue>} value - information to store
|
|
41
|
+
*/
|
|
42
|
+
save(value: Partial<CookieValue>): void;
|
|
43
|
+
/**
|
|
44
|
+
* Retrieve state payload from cookies
|
|
45
|
+
* @return {string | undefined} - State payload from cookies
|
|
46
|
+
*/
|
|
47
|
+
getStatePayload(): string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Clear state payload from cookies
|
|
36
50
|
*/
|
|
37
|
-
|
|
51
|
+
clearStatePayload(): void;
|
|
38
52
|
/**
|
|
39
53
|
* Retrieve cookies in the browser
|
|
40
54
|
* @return {CookieValue | undefined} - Cookies from the browser
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { TIDClient } from './TIDClient';
|
|
2
2
|
export { TIDContext, useAuth, TIDProvider } from './TIDProvider';
|
|
3
3
|
export { AuthenticationGuard } from './AuthenticationGuard/AuthenticationGuard';
|
|
4
|
-
export type { TokenResponse } from './TIDClient';
|
|
4
|
+
export type { TokenResponse, AuthState } from './TIDClient';
|
|
@@ -1,75 +1,70 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var c = (i, e, t) => (B(i, typeof e != "symbol" ? e + "" : e, t), t);
|
|
4
|
-
import { OpenIdEndpointProvider as $, AuthorizationCodeGrantTokenProvider as R, AnalyticsHttpClient as H, BearerTokenHttpClientProvider as Q } from "@trimble-oss/trimble-id";
|
|
5
|
-
import * as w from "es-cookie";
|
|
1
|
+
import { OpenIdEndpointProvider as $, AuthorizationCodeGrantTokenProvider as R, AnalyticsHttpClient as h, BearerTokenHttpClientProvider as B } from "@trimble-oss/trimble-id";
|
|
2
|
+
import * as m from "es-cookie";
|
|
6
3
|
import J from "jwt-decode";
|
|
7
|
-
import { createContext as
|
|
8
|
-
import { jsx as
|
|
9
|
-
class
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}());
|
|
63
|
-
}
|
|
4
|
+
import { createContext as H, useContext as L, useState as Q, useReducer as X, useRef as q, useMemo as M, useEffect as N, useCallback as y } from "react";
|
|
5
|
+
import { jsx as V, Fragment as z } from "react/jsx-runtime";
|
|
6
|
+
class Y {
|
|
7
|
+
/**
|
|
8
|
+
* This function generate a encapsulation function to store
|
|
9
|
+
* the user and token information in a secure way disabled the
|
|
10
|
+
* access from the outside
|
|
11
|
+
* @see {https://medium.com/javascript-scene/encapsulation-in-javascript-26be60e325b4} Encapsulation
|
|
12
|
+
* @return {CacheStorage} Key for the token
|
|
13
|
+
*/
|
|
14
|
+
generateCache = /* @__PURE__ */ (function() {
|
|
15
|
+
const e = {
|
|
16
|
+
token: void 0,
|
|
17
|
+
user: void 0
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
/**
|
|
21
|
+
* Get token store in cache
|
|
22
|
+
* @return {Promise<TIDAuthToken | undefined>} Token store in memory
|
|
23
|
+
*/
|
|
24
|
+
async getToken() {
|
|
25
|
+
return e.token;
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* Get user store in cache
|
|
29
|
+
* @return {Promise<TIDUser | undefined>} User store in memory
|
|
30
|
+
*/
|
|
31
|
+
async getUser() {
|
|
32
|
+
return e.user;
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* Store token in cache
|
|
36
|
+
* @param {TIDAuthToken} token - Token that you want to store in cache
|
|
37
|
+
* @return {Promise<void>} Empty promise
|
|
38
|
+
*/
|
|
39
|
+
async storeToken(t) {
|
|
40
|
+
e.token = t;
|
|
41
|
+
},
|
|
42
|
+
/**
|
|
43
|
+
* Store user in cache
|
|
44
|
+
* @param {TIDUser} user - User that you want to store in cache
|
|
45
|
+
* @return {Promise<void>} Empty promise
|
|
46
|
+
*/
|
|
47
|
+
async storeUser(t) {
|
|
48
|
+
e.user = t;
|
|
49
|
+
},
|
|
50
|
+
/**
|
|
51
|
+
* The clear the cache from the storage
|
|
52
|
+
* @return {Promise<void>} Empty promise
|
|
53
|
+
*/
|
|
54
|
+
clear() {
|
|
55
|
+
return e.token = void 0, e.user = void 0, Promise.resolve(void 0);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
})();
|
|
64
59
|
}
|
|
65
|
-
class
|
|
60
|
+
class Z {
|
|
61
|
+
/**
|
|
62
|
+
* Cache option selected
|
|
63
|
+
* @type {CacheStorage}
|
|
64
|
+
*/
|
|
65
|
+
cacheStorage;
|
|
66
66
|
constructor() {
|
|
67
|
-
|
|
68
|
-
* Cache option selected
|
|
69
|
-
* @type {CacheStorage}
|
|
70
|
-
*/
|
|
71
|
-
c(this, "cacheStorage");
|
|
72
|
-
this.cacheStorage = new j().generateCache;
|
|
67
|
+
this.cacheStorage = new Y().generateCache;
|
|
73
68
|
}
|
|
74
69
|
/**
|
|
75
70
|
* Store token in cache
|
|
@@ -109,41 +104,42 @@ class ee {
|
|
|
109
104
|
await this.cacheStorage.clear();
|
|
110
105
|
}
|
|
111
106
|
}
|
|
112
|
-
const x = 5 * 6e4,
|
|
113
|
-
let e =
|
|
114
|
-
if (!
|
|
107
|
+
const x = 5 * 6e4, j = ["code", "state"], A = 600 * 1e3, U = (o) => o.split("?")[0], ee = (o) => o.split("?")[1], b = (o) => {
|
|
108
|
+
let e = o;
|
|
109
|
+
if (!o.startsWith("?"))
|
|
115
110
|
try {
|
|
116
|
-
e = new URL(
|
|
111
|
+
e = new URL(o).search;
|
|
117
112
|
} catch {
|
|
118
113
|
}
|
|
119
114
|
return new URLSearchParams(e);
|
|
120
|
-
},
|
|
115
|
+
}, te = (o = window.location.href, e) => {
|
|
121
116
|
if (e != null) {
|
|
122
|
-
const
|
|
123
|
-
if (
|
|
117
|
+
const r = U(e), n = U(o ?? "");
|
|
118
|
+
if (r !== n)
|
|
124
119
|
return !1;
|
|
125
120
|
}
|
|
126
|
-
const t = b(
|
|
127
|
-
for (const
|
|
128
|
-
if (!t.has(
|
|
121
|
+
const t = b(o);
|
|
122
|
+
for (const r of j)
|
|
123
|
+
if (!t.has(r))
|
|
129
124
|
return !1;
|
|
130
125
|
return !0;
|
|
131
|
-
},
|
|
132
|
-
id:
|
|
133
|
-
name: `${
|
|
134
|
-
given_name:
|
|
135
|
-
family_name:
|
|
136
|
-
picture:
|
|
137
|
-
email:
|
|
138
|
-
email_verified:
|
|
139
|
-
|
|
140
|
-
|
|
126
|
+
}, oe = (o) => ({
|
|
127
|
+
id: o.sub,
|
|
128
|
+
name: `${o.given_name} ${o.family_name}`,
|
|
129
|
+
given_name: o.given_name,
|
|
130
|
+
family_name: o.family_name,
|
|
131
|
+
picture: o.picture,
|
|
132
|
+
email: o.email,
|
|
133
|
+
email_verified: o.email_verified,
|
|
134
|
+
...o.account_id && { account_id: o.account_id }
|
|
135
|
+
}), re = "@TID_COOKIE";
|
|
136
|
+
class ne {
|
|
141
137
|
/**
|
|
142
138
|
* Retrieve a cookie from the browser
|
|
143
139
|
* @param {string} key - Key to retrieve the cookies
|
|
144
140
|
*/
|
|
145
141
|
get(e) {
|
|
146
|
-
const t =
|
|
142
|
+
const t = m.get(e);
|
|
147
143
|
if (t == null)
|
|
148
144
|
throw new Error("Cookie not found");
|
|
149
145
|
return JSON.parse(t);
|
|
@@ -158,12 +154,12 @@ class oe {
|
|
|
158
154
|
* @example Save cookies with a custom domain
|
|
159
155
|
* cookiesStorage.set('key',{data:{...}},{domain:'https://example.com/subpath'})
|
|
160
156
|
*/
|
|
161
|
-
set(e, t,
|
|
162
|
-
let
|
|
163
|
-
window.location.protocol === "https:" && (
|
|
157
|
+
set(e, t, r) {
|
|
158
|
+
let n = {};
|
|
159
|
+
window.location.protocol === "https:" && (n = {
|
|
164
160
|
secure: !0,
|
|
165
161
|
sameSite: "none"
|
|
166
|
-
}),
|
|
162
|
+
}), r?.expires && (n.expires = r.expires), r?.domain && (n.domain = r.domain), m.set(e, JSON.stringify(t), n);
|
|
167
163
|
}
|
|
168
164
|
/**
|
|
169
165
|
* Remove a cookie from the browser
|
|
@@ -175,37 +171,52 @@ class oe {
|
|
|
175
171
|
* cookiesStorage.remove('key',{domain:'https://example.com/subpath'})
|
|
176
172
|
*/
|
|
177
173
|
remove(e, t) {
|
|
178
|
-
const
|
|
179
|
-
t
|
|
174
|
+
const r = {};
|
|
175
|
+
t?.domain && (r.domain = t.domain), m.remove(e, r);
|
|
180
176
|
}
|
|
181
177
|
}
|
|
182
|
-
class
|
|
178
|
+
class ie {
|
|
179
|
+
/**
|
|
180
|
+
* Cookie full key to store and retrieve the information from cookies
|
|
181
|
+
* @type {string}
|
|
182
|
+
*/
|
|
183
|
+
cookieKey;
|
|
184
|
+
/**
|
|
185
|
+
* Cookie storage. This object contain all functions necessary to retrieve and store tokens using cookies
|
|
186
|
+
* @type {CookiesStorage}
|
|
187
|
+
*/
|
|
188
|
+
cookiesStorage;
|
|
183
189
|
/**
|
|
184
190
|
* Create a cookies manager to extract or save cookies in the browser
|
|
185
191
|
* @param {CookiesManagerOptions} options - Configuration for the managing the cookies
|
|
186
192
|
*/
|
|
187
193
|
constructor(e) {
|
|
188
|
-
|
|
189
|
-
* Cookie full key to store and retrieve the information from cookies
|
|
190
|
-
* @type {string}
|
|
191
|
-
*/
|
|
192
|
-
c(this, "cookieKey");
|
|
193
|
-
/**
|
|
194
|
-
* Cookie storage. This object contain all functions necessary to retrieve and store tokens using cookies
|
|
195
|
-
* @type {CookiesStorage}
|
|
196
|
-
*/
|
|
197
|
-
c(this, "cookiesStorage");
|
|
198
|
-
this.cookieKey = `${se}.${e.clientId}`, this.cookiesStorage = new oe();
|
|
194
|
+
this.cookieKey = `${re}.${e.clientId}`, this.cookiesStorage = new ne();
|
|
199
195
|
}
|
|
200
196
|
/**
|
|
201
197
|
* Store cookies in the browser
|
|
202
|
-
* @param {CookieValue} value - information to store
|
|
198
|
+
* @param {Partial<CookieValue>} value - information to store
|
|
203
199
|
*/
|
|
204
200
|
save(e) {
|
|
205
|
-
this.
|
|
201
|
+
const r = { ...this.get() || {}, ...e };
|
|
202
|
+
this.cookiesStorage.set(this.cookieKey, r, {
|
|
206
203
|
expires: 1
|
|
207
204
|
});
|
|
208
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Retrieve state payload from cookies
|
|
208
|
+
* @return {string | undefined} - State payload from cookies
|
|
209
|
+
*/
|
|
210
|
+
getStatePayload() {
|
|
211
|
+
return this.get()?.state_payload;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Clear state payload from cookies
|
|
215
|
+
*/
|
|
216
|
+
clearStatePayload() {
|
|
217
|
+
const e = this.get();
|
|
218
|
+
e && (delete e.state_payload, this.cookiesStorage.set(this.cookieKey, e, { expires: 1 }));
|
|
219
|
+
}
|
|
209
220
|
/**
|
|
210
221
|
* Retrieve cookies in the browser
|
|
211
222
|
* @return {CookieValue | undefined} - Cookies from the browser
|
|
@@ -224,70 +235,79 @@ class ae {
|
|
|
224
235
|
this.cookiesStorage.remove(this.cookieKey);
|
|
225
236
|
}
|
|
226
237
|
}
|
|
227
|
-
class U extends Error {
|
|
228
|
-
}
|
|
229
238
|
class O extends Error {
|
|
230
239
|
}
|
|
231
|
-
class
|
|
240
|
+
class D extends Error {
|
|
232
241
|
}
|
|
233
|
-
|
|
242
|
+
class ae extends Error {
|
|
243
|
+
}
|
|
244
|
+
const u = "@trimble-oss/trimble-id-react", g = "1.0.2-rc1", se = {
|
|
234
245
|
configurationEndpoint: "",
|
|
235
246
|
clientId: "",
|
|
236
247
|
redirectUrl: "",
|
|
237
248
|
logoutRedirectUrl: "",
|
|
238
249
|
scopes: []
|
|
239
250
|
};
|
|
240
|
-
class
|
|
251
|
+
class ce {
|
|
252
|
+
/**
|
|
253
|
+
* Token provider SDK. This object handles all necessary communication with TID
|
|
254
|
+
* @type {AuthorizationCodeGrantTokenProvider}
|
|
255
|
+
*/
|
|
256
|
+
tokenProvider;
|
|
257
|
+
/**
|
|
258
|
+
* This object manage all caching, and all configurations necessary
|
|
259
|
+
* @type {CacheManager}
|
|
260
|
+
*/
|
|
261
|
+
cacheManager;
|
|
262
|
+
/**
|
|
263
|
+
* This object manage all cookies administration, and all configurations necessary
|
|
264
|
+
* @type {CookiesManager}
|
|
265
|
+
*/
|
|
266
|
+
cookiesManager;
|
|
267
|
+
/**
|
|
268
|
+
* Client id of the application created in trimble developer console
|
|
269
|
+
* @type {string}
|
|
270
|
+
*/
|
|
271
|
+
clientId;
|
|
272
|
+
/**
|
|
273
|
+
* Callback url to redirect the user after the authentication is successful
|
|
274
|
+
* @type {string}
|
|
275
|
+
*/
|
|
276
|
+
redirectUrl;
|
|
241
277
|
/**
|
|
242
278
|
* Create a TID client to handle manage all user authentication functions and information
|
|
243
279
|
* @param {CacheManagerOptions} props - TID client configuration
|
|
244
280
|
*/
|
|
245
281
|
constructor(e) {
|
|
246
|
-
|
|
247
|
-
* Token provider SDK. This object handles all necessary communication with TID
|
|
248
|
-
* @type {AuthorizationCodeGrantTokenProvider}
|
|
249
|
-
*/
|
|
250
|
-
c(this, "tokenProvider");
|
|
251
|
-
/**
|
|
252
|
-
* This object manage all caching, and all configurations necessary
|
|
253
|
-
* @type {CacheManager}
|
|
254
|
-
*/
|
|
255
|
-
c(this, "cacheManager");
|
|
256
|
-
/**
|
|
257
|
-
* This object manage all cookies administration, and all configurations necessary
|
|
258
|
-
* @type {CookiesManager}
|
|
259
|
-
*/
|
|
260
|
-
c(this, "cookiesManager");
|
|
261
|
-
/**
|
|
262
|
-
* Client id of the application created in trimble developer console
|
|
263
|
-
* @type {string}
|
|
264
|
-
*/
|
|
265
|
-
c(this, "clientId");
|
|
266
|
-
/**
|
|
267
|
-
* Callback url to redirect the user after the authentication is successful
|
|
268
|
-
* @type {string}
|
|
269
|
-
*/
|
|
270
|
-
c(this, "redirectUrl");
|
|
271
|
-
/**
|
|
272
|
-
* AnalyticsHttpClient for sending events
|
|
273
|
-
* @type {AnalyticsHttpClient}
|
|
274
|
-
*/
|
|
275
|
-
c(this, "analyticshttpclient");
|
|
276
|
-
const { config: t = le } = e;
|
|
282
|
+
const { config: t = se } = e;
|
|
277
283
|
if (this.redirectUrl = t.redirectUrl, t.configurationEndpoint == null || t.configurationEndpoint == "")
|
|
278
284
|
throw new Error("Configuration endpoint not defined");
|
|
279
285
|
if (t.clientId == null || t.clientId == "")
|
|
280
286
|
throw new Error("Consumer key is not defined");
|
|
281
|
-
this.cookiesManager = new
|
|
287
|
+
this.cookiesManager = new ie({
|
|
282
288
|
clientId: t.clientId
|
|
283
289
|
});
|
|
284
|
-
const
|
|
285
|
-
let
|
|
286
|
-
|
|
290
|
+
const r = this.cookiesManager.get(), n = new $(t.configurationEndpoint);
|
|
291
|
+
let i = new R(
|
|
292
|
+
n,
|
|
287
293
|
t.clientId,
|
|
288
294
|
t.redirectUrl
|
|
289
295
|
).WithScopes(t.scopes);
|
|
290
|
-
t.logoutRedirectUrl && (
|
|
296
|
+
t.logoutRedirectUrl && (i = i.WithLogoutRedirect(t.logoutRedirectUrl)), r?.code_verifier != null && (i = i.WithProofKeyForCodeExchange(r.code_verifier)), this.tokenProvider = i, this.cacheManager = new Z(), this.clientId = t.clientId, h.sendInitEvent("TIDClient", this.clientId, u, g), this.cleanupExpiredState();
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Clean up expired state payloads to prevent storage bloat
|
|
300
|
+
*/
|
|
301
|
+
cleanupExpiredState() {
|
|
302
|
+
try {
|
|
303
|
+
const e = this.cookiesManager.getStatePayload();
|
|
304
|
+
if (e) {
|
|
305
|
+
const t = atob(e), r = JSON.parse(t);
|
|
306
|
+
Date.now() - r.timestamp > A && this.cookiesManager.clearStatePayload();
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
this.cookiesManager.clearStatePayload();
|
|
310
|
+
}
|
|
291
311
|
}
|
|
292
312
|
/**
|
|
293
313
|
* Redirect the user to TID using the browser
|
|
@@ -296,23 +316,27 @@ class de {
|
|
|
296
316
|
* @example No configuration
|
|
297
317
|
* loginWithRedirect()
|
|
298
318
|
* // Automatically redirects the user to TID with all necessary parameters
|
|
319
|
+
* // After authentication, user will be redirected back to the current page
|
|
299
320
|
* @example Custom redirect
|
|
300
321
|
* loginWithRedirect({onRedirect: (url) => router.navigate(url)})
|
|
301
322
|
* // Redirect calls onRedirect with the log-out url for TID
|
|
302
323
|
* // So it can be handled by the developer
|
|
303
324
|
*/
|
|
304
325
|
async loginWithRedirect(e) {
|
|
305
|
-
|
|
306
|
-
const { onRedirect: t } = e || {}, n =
|
|
307
|
-
this.cookiesManager.save({
|
|
308
|
-
const
|
|
309
|
-
|
|
326
|
+
h.sendMethodEvent(this.loginWithRedirect.name, this.clientId, u, g);
|
|
327
|
+
const { onRedirect: t } = e || {}, r = window.location.pathname + window.location.search, n = this.createStatePayload(r);
|
|
328
|
+
this.cookiesManager.save({ state_payload: n });
|
|
329
|
+
const i = R.GenerateCodeVerifier();
|
|
330
|
+
this.cookiesManager.save({ code_verifier: i }), this.tokenProvider = this.tokenProvider.WithProofKeyForCodeExchange(i);
|
|
331
|
+
const c = await this.tokenProvider.GetOAuthRedirect(n);
|
|
332
|
+
t != null ? t(c) : window.location.assign(c);
|
|
310
333
|
}
|
|
311
334
|
/**
|
|
312
335
|
* Authenticated the user using the url callback params
|
|
313
336
|
* @param {string} url - Custom configuration for the redirection
|
|
314
|
-
* @return {Promise<AuthState>} Object contain the state returned from TID
|
|
337
|
+
* @return {Promise<AuthState>} Object contain the state returned from TID and redirect path
|
|
315
338
|
* @throws {CodeVerifierNotFoundException} Will throw an exception if the session doesn't contain the code verifier
|
|
339
|
+
* @throws {Error} Will throw an exception if state validation fails or replay attack is detected
|
|
316
340
|
* @example No configuration
|
|
317
341
|
* handleCallback()
|
|
318
342
|
* // Will automatically take the url from the browser and try to log in the user
|
|
@@ -322,11 +346,14 @@ class de {
|
|
|
322
346
|
*/
|
|
323
347
|
async handleCallback(e = window.location.href) {
|
|
324
348
|
const t = this.cookiesManager.get();
|
|
325
|
-
if (t == null ||
|
|
326
|
-
throw new
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
349
|
+
if (t == null || t?.code_verifier == null)
|
|
350
|
+
throw new ae("Code verifier not available");
|
|
351
|
+
const r = ee(e), n = b(e), i = n.get("identity_provider") ?? "", c = n.get("state") ?? "", d = this.validateStatePayload(c);
|
|
352
|
+
if (!d.isValid)
|
|
353
|
+
throw new Error(`State validation failed: ${d.error}`);
|
|
354
|
+
return await this.tokenProvider.ValidateQuery(r), await this.generateToken(i), {
|
|
355
|
+
authState: c,
|
|
356
|
+
returnTo: d.redirectTo
|
|
330
357
|
};
|
|
331
358
|
}
|
|
332
359
|
/**
|
|
@@ -335,32 +362,81 @@ class de {
|
|
|
335
362
|
* @return {Promise<void>} Empty promise
|
|
336
363
|
*/
|
|
337
364
|
async generateToken(e) {
|
|
338
|
-
|
|
339
|
-
const t = await this.tokenProvider.RetrieveToken(), n = await this.tokenProvider.RetrieveRefreshToken(), r = await this.tokenProvider.RetrieveIdToken(), a = await this.tokenProvider.RetrieveTokenExpiry(), k = new Date(a).getTime(), v = (f = (s = this.tokenProvider) == null ? void 0 : s._scopes) == null ? void 0 : f.join(" ");
|
|
365
|
+
const t = await this.tokenProvider.RetrieveToken(), r = await this.tokenProvider.RetrieveRefreshToken(), n = await this.tokenProvider.RetrieveIdToken(), i = await this.tokenProvider.RetrieveTokenExpiry(), d = new Date(i).getTime(), k = this.tokenProvider?._scopes?.join(" ");
|
|
340
366
|
await this.cacheManager.setToken({
|
|
341
|
-
scope:
|
|
367
|
+
scope: k,
|
|
342
368
|
state: "",
|
|
343
369
|
session_state: "",
|
|
344
370
|
identity_provider: e,
|
|
345
371
|
token_type: "bearer",
|
|
346
372
|
access_token: t,
|
|
347
|
-
refresh_token:
|
|
348
|
-
id_token:
|
|
349
|
-
expires_at:
|
|
373
|
+
refresh_token: r,
|
|
374
|
+
id_token: n,
|
|
375
|
+
expires_at: d
|
|
350
376
|
});
|
|
351
|
-
const
|
|
352
|
-
await this.cacheManager.setUser(
|
|
377
|
+
const v = J(n), p = oe(v);
|
|
378
|
+
await this.cacheManager.setUser(p), await this.reloadCodeVerifier();
|
|
353
379
|
}
|
|
354
380
|
async reloadCodeVerifier() {
|
|
355
381
|
const e = await this.tokenProvider.RetrieveCodeVerifier();
|
|
356
382
|
this.cookiesManager.save({ code_verifier: e }), this.tokenProvider = this.tokenProvider.WithProofKeyForCodeExchange(e);
|
|
357
383
|
}
|
|
384
|
+
/**
|
|
385
|
+
* Create and encode state payload for replay attack protection
|
|
386
|
+
* @param {string} redirectTo - Path to redirect to after authentication
|
|
387
|
+
* @return {string} - Base64 encoded state payload
|
|
388
|
+
*/
|
|
389
|
+
createStatePayload(e) {
|
|
390
|
+
const t = this.generateNonce(), r = Date.now();
|
|
391
|
+
return btoa(JSON.stringify({
|
|
392
|
+
redirectTo: e,
|
|
393
|
+
timestamp: r,
|
|
394
|
+
nonce: t
|
|
395
|
+
}));
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Generate a random nonce for state validation
|
|
399
|
+
* @return {string} - Random nonce
|
|
400
|
+
*/
|
|
401
|
+
generateNonce() {
|
|
402
|
+
const e = new Uint8Array(16);
|
|
403
|
+
return crypto.getRandomValues(e), Array.from(e, (t) => t.toString(16).padStart(2, "0")).join("");
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Validate state payload to prevent replay attacks
|
|
407
|
+
* @param {string} receivedState - State received from OAuth callback
|
|
408
|
+
* @return {StateValidationResult} - Validation result with redirect path if valid
|
|
409
|
+
*/
|
|
410
|
+
validateStatePayload(e) {
|
|
411
|
+
try {
|
|
412
|
+
if (!e || e.trim() === "")
|
|
413
|
+
return { isValid: !1, error: "Empty state parameter" };
|
|
414
|
+
const t = this.cookiesManager.getStatePayload();
|
|
415
|
+
if (!t)
|
|
416
|
+
return { isValid: !1, error: "No stored state found" };
|
|
417
|
+
const r = atob(e), n = JSON.parse(r), i = atob(t), c = JSON.parse(i);
|
|
418
|
+
if (!n.nonce || !n.timestamp || !n.redirectTo)
|
|
419
|
+
return { isValid: !1, error: "Invalid state payload structure" };
|
|
420
|
+
if (n.nonce !== c.nonce)
|
|
421
|
+
return { isValid: !1, error: "State nonce mismatch" };
|
|
422
|
+
const k = Date.now() - n.timestamp;
|
|
423
|
+
return k > A ? { isValid: !1, error: "State expired - possible replay attack" } : k < 0 ? {
|
|
424
|
+
isValid: !1,
|
|
425
|
+
error: "State timestamp is in the future - possible replay attack"
|
|
426
|
+
} : (this.cookiesManager.clearStatePayload(), {
|
|
427
|
+
isValid: !0,
|
|
428
|
+
redirectTo: n.redirectTo
|
|
429
|
+
});
|
|
430
|
+
} catch {
|
|
431
|
+
return { isValid: !1, error: "Invalid state format" };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
358
434
|
/**
|
|
359
435
|
* Return the user stored in cache
|
|
360
436
|
* @return {Promise<TIDUser | undefined>} User in cache
|
|
361
437
|
*/
|
|
362
438
|
async getUser() {
|
|
363
|
-
return
|
|
439
|
+
return h.sendMethodEvent(this.getUser.name, this.clientId, u, g), await this.cacheManager.getUser();
|
|
364
440
|
}
|
|
365
441
|
/**
|
|
366
442
|
* Gets the access token from cache. If the token already expired,
|
|
@@ -370,46 +446,75 @@ class de {
|
|
|
370
446
|
* @throws {TokenExpiredException} Will throw an exception if the user token expired
|
|
371
447
|
*/
|
|
372
448
|
async getAccessTokenSilently() {
|
|
373
|
-
|
|
449
|
+
h.sendMethodEvent(
|
|
450
|
+
this.getAccessTokenSilently.name,
|
|
451
|
+
this.clientId,
|
|
452
|
+
u,
|
|
453
|
+
g
|
|
454
|
+
);
|
|
374
455
|
let e = await this.cacheManager.getToken();
|
|
375
456
|
if (e == null)
|
|
376
|
-
throw
|
|
457
|
+
throw h.sendExceptionEvent(
|
|
458
|
+
this.getAccessTokenSilently.name,
|
|
459
|
+
"No token available",
|
|
460
|
+
this.clientId,
|
|
461
|
+
u,
|
|
462
|
+
g
|
|
463
|
+
), new D("No token available");
|
|
377
464
|
const t = new Date((/* @__PURE__ */ new Date()).getTime() + x);
|
|
378
|
-
if (
|
|
465
|
+
if (e?.expires_at == null || e?.expires_at < t.getTime()) {
|
|
379
466
|
try {
|
|
380
467
|
await this.tokenProvider.RetrieveToken();
|
|
381
|
-
} catch (
|
|
382
|
-
throw
|
|
468
|
+
} catch (r) {
|
|
469
|
+
throw h.sendExceptionEvent(
|
|
470
|
+
this.getAccessTokenSilently.name,
|
|
471
|
+
r.message,
|
|
472
|
+
this.clientId,
|
|
473
|
+
u,
|
|
474
|
+
g
|
|
475
|
+
), new O(r.message);
|
|
383
476
|
}
|
|
384
|
-
await this.generateToken(
|
|
477
|
+
await this.generateToken(e?.identity_provider ?? ""), e = await this.cacheManager.getToken();
|
|
385
478
|
}
|
|
386
|
-
return
|
|
479
|
+
return e?.access_token || "";
|
|
387
480
|
}
|
|
388
481
|
/**
|
|
389
482
|
* Retrieves token details from the cache, including the access token, ID token, and expiration time.
|
|
390
|
-
* If the token already expired, will try to refresh it using the refresh token.
|
|
483
|
+
* If the token already expired, will try to refresh it using the refresh token.
|
|
391
484
|
* @return {Promise<TokenResponse>} Token response
|
|
392
485
|
* @throws {TokenNotFoundException} Will throw an exception if there are no tokens in cache
|
|
393
486
|
* @throws {TokenExpiredException} Will throw an exception if the user token expired
|
|
394
487
|
*/
|
|
395
488
|
async getTokens() {
|
|
396
|
-
|
|
489
|
+
h.sendMethodEvent(this.getTokens.name, this.clientId, u, g);
|
|
397
490
|
let e = await this.cacheManager.getToken();
|
|
398
491
|
if (e == null)
|
|
399
|
-
throw
|
|
492
|
+
throw h.sendExceptionEvent(
|
|
493
|
+
this.getTokens.name,
|
|
494
|
+
"No token available",
|
|
495
|
+
this.clientId,
|
|
496
|
+
u,
|
|
497
|
+
g
|
|
498
|
+
), new D("No token available");
|
|
400
499
|
const t = new Date((/* @__PURE__ */ new Date()).getTime() + x);
|
|
401
|
-
if (
|
|
500
|
+
if (e?.expires_at == null || e?.expires_at < t.getTime()) {
|
|
402
501
|
try {
|
|
403
502
|
await this.tokenProvider.RetrieveToken();
|
|
404
|
-
} catch (
|
|
405
|
-
throw
|
|
503
|
+
} catch (r) {
|
|
504
|
+
throw h.sendExceptionEvent(
|
|
505
|
+
this.getTokens.name,
|
|
506
|
+
r.message,
|
|
507
|
+
this.clientId,
|
|
508
|
+
u,
|
|
509
|
+
g
|
|
510
|
+
), new O(r.message);
|
|
406
511
|
}
|
|
407
|
-
await this.generateToken(
|
|
512
|
+
await this.generateToken(e?.identity_provider ?? ""), e = await this.cacheManager.getToken();
|
|
408
513
|
}
|
|
409
514
|
return {
|
|
410
|
-
access_token:
|
|
411
|
-
expires_at:
|
|
412
|
-
id_token:
|
|
515
|
+
access_token: e?.access_token || "",
|
|
516
|
+
expires_at: e?.expires_at || 0,
|
|
517
|
+
id_token: e?.id_token || ""
|
|
413
518
|
};
|
|
414
519
|
}
|
|
415
520
|
/**
|
|
@@ -425,13 +530,13 @@ class de {
|
|
|
425
530
|
* // So it can be handled by the developer
|
|
426
531
|
*/
|
|
427
532
|
async logout(e) {
|
|
428
|
-
|
|
429
|
-
const { onRedirect: t, disabledAutoRedirect:
|
|
533
|
+
h.sendMethodEvent(this.logout.name, this.clientId, u, g);
|
|
534
|
+
const { onRedirect: t, disabledAutoRedirect: r } = e || {};
|
|
430
535
|
this.cacheManager && await this.cacheManager.clear(), this.cookiesManager && this.cookiesManager.clear();
|
|
431
|
-
const
|
|
536
|
+
const n = await this.tokenProvider.GetOAuthLogoutRedirect("state");
|
|
432
537
|
if (t != null)
|
|
433
|
-
return t(
|
|
434
|
-
|
|
538
|
+
return t(n);
|
|
539
|
+
r || window.location.assign(n);
|
|
435
540
|
}
|
|
436
541
|
/**
|
|
437
542
|
* Check if the user still has a valid session
|
|
@@ -460,15 +565,15 @@ class de {
|
|
|
460
565
|
const e = await this.cacheManager.getToken();
|
|
461
566
|
if (e == null)
|
|
462
567
|
return;
|
|
463
|
-
const t = e.access_token,
|
|
464
|
-
this.tokenProvider = this.tokenProvider.WithAccessToken(t,
|
|
568
|
+
const t = e.access_token, r = e.refresh_token || "", n = e.id_token, i = e.expires_at;
|
|
569
|
+
this.tokenProvider = this.tokenProvider.WithAccessToken(t, i).WithRefreshToken(r).WithIdToken(n);
|
|
465
570
|
}
|
|
466
571
|
/**
|
|
467
572
|
* Get a http bearer token client to use it for another SDK (Ex: Processing framework)
|
|
468
573
|
* @return {any} - BearerTokenHttpClientProvider
|
|
469
574
|
*/
|
|
470
575
|
getBearerTokenHttpClient(e) {
|
|
471
|
-
return new
|
|
576
|
+
return new B(this.tokenProvider, e);
|
|
472
577
|
}
|
|
473
578
|
/**
|
|
474
579
|
* Get the redirect url to TID
|
|
@@ -478,11 +583,11 @@ class de {
|
|
|
478
583
|
return this.redirectUrl;
|
|
479
584
|
}
|
|
480
585
|
}
|
|
481
|
-
const
|
|
586
|
+
const T = H(null), K = () => L(T) ?? {}, de = (o, e) => {
|
|
482
587
|
switch (e.type) {
|
|
483
588
|
case "INIT":
|
|
484
589
|
return {
|
|
485
|
-
...
|
|
590
|
+
...o,
|
|
486
591
|
isLoading: !1,
|
|
487
592
|
isAuthenticated: e.user != null,
|
|
488
593
|
user: e.user,
|
|
@@ -492,7 +597,7 @@ const E = q(null), W = () => L(E) ?? {}, he = (i, e) => {
|
|
|
492
597
|
case "HANDLE_CALLBACK_COMPLETE":
|
|
493
598
|
case "GET_ACCESS_TOKEN_COMPLETE":
|
|
494
599
|
return {
|
|
495
|
-
...
|
|
600
|
+
...o,
|
|
496
601
|
isLoading: !1,
|
|
497
602
|
isAuthenticated: e.user != null,
|
|
498
603
|
user: e.user,
|
|
@@ -500,136 +605,134 @@ const E = q(null), W = () => L(E) ?? {}, he = (i, e) => {
|
|
|
500
605
|
};
|
|
501
606
|
case "LOGOUT":
|
|
502
607
|
return {
|
|
503
|
-
...
|
|
608
|
+
...o,
|
|
504
609
|
isAuthenticated: !1,
|
|
505
610
|
user: void 0
|
|
506
611
|
};
|
|
507
612
|
case "ERROR":
|
|
508
613
|
return {
|
|
509
|
-
...
|
|
614
|
+
...o,
|
|
510
615
|
isLoading: !1,
|
|
511
616
|
error: e.error
|
|
512
617
|
};
|
|
513
618
|
}
|
|
514
|
-
},
|
|
619
|
+
}, le = {
|
|
515
620
|
isLoading: !0,
|
|
516
621
|
isAuthenticated: !1
|
|
517
|
-
},
|
|
518
|
-
if (
|
|
622
|
+
}, he = (o) => {
|
|
623
|
+
if (o == null || Object.keys(o).length <= 0)
|
|
519
624
|
return !0;
|
|
520
|
-
const e = Object.keys(
|
|
625
|
+
const e = Object.keys(o);
|
|
521
626
|
for (const t of e)
|
|
522
|
-
if (
|
|
627
|
+
if (o[t] != null && o[t] !== "")
|
|
523
628
|
return !1;
|
|
524
629
|
return !0;
|
|
525
|
-
},
|
|
630
|
+
}, ue = (o) => !he(o.config), ge = (o) => {
|
|
526
631
|
const {
|
|
527
632
|
children: e,
|
|
528
633
|
configurationEndpoint: t,
|
|
529
|
-
clientId:
|
|
530
|
-
redirectUrl:
|
|
531
|
-
logoutRedirectUrl:
|
|
532
|
-
scopes:
|
|
533
|
-
onRedirectCallback:
|
|
534
|
-
checkRedirectUrlMatch:
|
|
535
|
-
} =
|
|
536
|
-
if (L(
|
|
634
|
+
clientId: r,
|
|
635
|
+
redirectUrl: n,
|
|
636
|
+
logoutRedirectUrl: i,
|
|
637
|
+
scopes: c,
|
|
638
|
+
onRedirectCallback: d,
|
|
639
|
+
checkRedirectUrlMatch: k
|
|
640
|
+
} = o;
|
|
641
|
+
if (L(T) != null)
|
|
537
642
|
throw new Error("TID Provider already defined");
|
|
538
|
-
const
|
|
643
|
+
const p = {
|
|
539
644
|
config: {
|
|
540
645
|
configurationEndpoint: t ?? "",
|
|
541
|
-
clientId:
|
|
542
|
-
redirectUrl:
|
|
543
|
-
logoutRedirectUrl:
|
|
544
|
-
scopes:
|
|
646
|
+
clientId: r ?? "",
|
|
647
|
+
redirectUrl: n ?? "",
|
|
648
|
+
logoutRedirectUrl: i ?? "",
|
|
649
|
+
scopes: c ?? [""]
|
|
545
650
|
}
|
|
546
|
-
}, [
|
|
547
|
-
() =>
|
|
548
|
-
[
|
|
651
|
+
}, [a] = Q(o.tidClient ?? new ce(p)), [w, f] = X(de, le), E = q(!1), W = M(
|
|
652
|
+
() => k ? a.getRedirectUrl() : void 0,
|
|
653
|
+
[k, a]
|
|
549
654
|
);
|
|
550
|
-
|
|
551
|
-
|
|
655
|
+
N(() => {
|
|
656
|
+
E.current || (o.tidClient != null && ue({
|
|
552
657
|
config: {
|
|
553
658
|
configurationEndpoint: t,
|
|
554
|
-
clientId:
|
|
555
|
-
redirectUrl:
|
|
556
|
-
logoutRedirectUrl:
|
|
557
|
-
scopes:
|
|
659
|
+
clientId: r,
|
|
660
|
+
redirectUrl: n,
|
|
661
|
+
logoutRedirectUrl: i,
|
|
662
|
+
scopes: c
|
|
558
663
|
}
|
|
559
664
|
}) && console.warn(
|
|
560
665
|
"When TID client is pass as prop, any client configuration property sent directly to the TID Provider component will be ignored"
|
|
561
|
-
),
|
|
666
|
+
), E.current = !0, (async () => {
|
|
562
667
|
try {
|
|
563
|
-
let
|
|
564
|
-
if (
|
|
565
|
-
const
|
|
566
|
-
|
|
668
|
+
let s;
|
|
669
|
+
if (te(void 0, W)) {
|
|
670
|
+
const l = await a.handleCallback();
|
|
671
|
+
s = await a.getUser(), d?.(l);
|
|
567
672
|
} else
|
|
568
|
-
await
|
|
569
|
-
|
|
570
|
-
} catch (
|
|
571
|
-
let l =
|
|
572
|
-
typeof
|
|
673
|
+
await a.loadUserSession(), s = await a.getUser();
|
|
674
|
+
f({ type: "INIT", user: s });
|
|
675
|
+
} catch (s) {
|
|
676
|
+
let l = s;
|
|
677
|
+
typeof s == "string" && (l = new Error(s)), f({ type: "ERROR", error: l });
|
|
573
678
|
}
|
|
574
679
|
})());
|
|
575
|
-
}, [
|
|
576
|
-
const
|
|
577
|
-
const
|
|
578
|
-
return
|
|
680
|
+
}, [a, d]);
|
|
681
|
+
const S = y(async () => {
|
|
682
|
+
const s = await a.getAccessTokenSilently(), l = await a.getUser();
|
|
683
|
+
return f({
|
|
579
684
|
type: "GET_ACCESS_TOKEN_COMPLETE",
|
|
580
685
|
user: l
|
|
581
|
-
}),
|
|
582
|
-
}, [
|
|
583
|
-
const
|
|
584
|
-
return
|
|
686
|
+
}), s;
|
|
687
|
+
}, [a]), _ = y(async () => {
|
|
688
|
+
const s = await a.getTokens(), l = await a.getUser();
|
|
689
|
+
return f({
|
|
585
690
|
type: "GET_TOKENS_COMPLETE",
|
|
586
691
|
user: l
|
|
587
|
-
}),
|
|
588
|
-
}, [
|
|
589
|
-
async (
|
|
590
|
-
await
|
|
692
|
+
}), s;
|
|
693
|
+
}, [a]), P = y(
|
|
694
|
+
async (s) => {
|
|
695
|
+
await a.loginWithRedirect(s);
|
|
591
696
|
},
|
|
592
|
-
[
|
|
593
|
-
),
|
|
594
|
-
async (
|
|
595
|
-
await
|
|
697
|
+
[a]
|
|
698
|
+
), C = y(
|
|
699
|
+
async (s) => {
|
|
700
|
+
await a.logout(s), s?.disabledAutoRedirect != null && s.disabledAutoRedirect && f({
|
|
596
701
|
type: "LOGOUT"
|
|
597
702
|
});
|
|
598
703
|
},
|
|
599
|
-
[
|
|
600
|
-
),
|
|
601
|
-
async (
|
|
602
|
-
const
|
|
603
|
-
return
|
|
704
|
+
[a]
|
|
705
|
+
), I = y(
|
|
706
|
+
async (s) => {
|
|
707
|
+
const l = await a.handleCallback(s), F = await a.getUser();
|
|
708
|
+
return f({
|
|
604
709
|
type: "HANDLE_CALLBACK_COMPLETE",
|
|
605
|
-
user:
|
|
606
|
-
}),
|
|
607
|
-
authState: l
|
|
608
|
-
};
|
|
710
|
+
user: F
|
|
711
|
+
}), l;
|
|
609
712
|
},
|
|
610
|
-
[
|
|
713
|
+
[a]
|
|
611
714
|
), G = M(
|
|
612
715
|
() => ({
|
|
613
|
-
...
|
|
614
|
-
getAccessTokenSilently:
|
|
615
|
-
getTokens:
|
|
616
|
-
loginWithRedirect:
|
|
617
|
-
handleCallback:
|
|
618
|
-
logout:
|
|
716
|
+
...w,
|
|
717
|
+
getAccessTokenSilently: S,
|
|
718
|
+
getTokens: _,
|
|
719
|
+
loginWithRedirect: P,
|
|
720
|
+
handleCallback: I,
|
|
721
|
+
logout: C
|
|
619
722
|
}),
|
|
620
|
-
[
|
|
723
|
+
[w, S, _, P, I, C]
|
|
621
724
|
);
|
|
622
|
-
return /* @__PURE__ */
|
|
623
|
-
}, me =
|
|
624
|
-
const { isAuthenticated: t, isLoading:
|
|
625
|
-
return
|
|
626
|
-
!
|
|
627
|
-
}, [
|
|
725
|
+
return /* @__PURE__ */ V(T.Provider, { value: G, children: e });
|
|
726
|
+
}, me = K, Te = ge, ve = ({ renderComponent: o, loader: e }) => {
|
|
727
|
+
const { isAuthenticated: t, isLoading: r, loginWithRedirect: n } = K();
|
|
728
|
+
return N(() => {
|
|
729
|
+
!r && !t && (async () => await n())();
|
|
730
|
+
}, [r, t, n]), t ? o : e || /* @__PURE__ */ V(z, {});
|
|
628
731
|
};
|
|
629
732
|
export {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
733
|
+
ve as AuthenticationGuard,
|
|
734
|
+
ce as TIDClient,
|
|
735
|
+
T as TIDContext,
|
|
736
|
+
Te as TIDProvider,
|
|
634
737
|
me as useAuth
|
|
635
738
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(o,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("@trimble-oss/trimble-id"),require("es-cookie"),require("jwt-decode"),require("react"),require("react/jsx-runtime")):typeof define=="function"&&define.amd?define(["exports","@trimble-oss/trimble-id","es-cookie","jwt-decode","react","react/jsx-runtime"],c):(o=typeof globalThis<"u"?globalThis:o||self,c(o.ReactTID={},o.trimbleId,o.esCookie,o.jwt_decode,o.React,o.jsxRuntime))})(this,function(o,c,k,W,l,p){"use strict";var le=Object.defineProperty;var de=(o,c,k)=>c in o?le(o,c,{enumerable:!0,configurable:!0,writable:!0,value:k}):o[c]=k;var h=(o,c,k)=>(de(o,typeof c!="symbol"?c+"":c,k),k);function G(i){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(i){for(const t in i)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(i,t);Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:()=>i[t]})}}return e.default=i,Object.freeze(e)}const C=G(k);class V{constructor(){h(this,"generateCache",function(){const e={token:void 0,user:void 0};return{async getToken(){return e.token},async getUser(){return e.user},async storeToken(t){e.token=t},async storeUser(t){e.user=t},clear(){return e.token=void 0,e.user=void 0,Promise.resolve(void 0)}}}())}}class F{constructor(){h(this,"cacheStorage");this.cacheStorage=new V().generateCache}async setToken(e){await this.cacheStorage.storeToken(e)}async setUser(e){await this.cacheStorage.storeUser(e)}async getUser(){return this.cacheStorage.getUser()}async getToken(){return this.cacheStorage.getToken()}async clear(){await this.cacheStorage.clear()}}const S=5*6e4,j=["code","state"],P=i=>i.split("?")[0],q=i=>i.split("?")[1],I=i=>{let e=i;if(!i.startsWith("?"))try{e=new URL(i).search}catch{}return new URLSearchParams(e)},B=(i=window.location.href,e)=>{if(e!=null){const n=P(e),r=P(i??"");if(n!==r)return!1}const t=I(i);for(const n of j)if(!t.has(n))return!1;return!0},$=i=>({id:i.sub,name:`${i.given_name} ${i.family_name}`,given_name:i.given_name,family_name:i.family_name,picture:i.picture,email:i.email,email_verified:i.email_verified}),H="@TID_COOKIE";class z{get(e){const t=C.get(e);if(t==null)throw new Error("Cookie not found");return JSON.parse(t)}set(e,t,n){let r={};window.location.protocol==="https:"&&(r={secure:!0,sameSite:"none"}),n!=null&&n.expires&&(r.expires=n.expires),n!=null&&n.domain&&(r.domain=n.domain),C.set(e,JSON.stringify(t),r)}remove(e,t){const n={};t!=null&&t.domain&&(n.domain=t.domain),C.remove(e,n)}}class Q{constructor(e){h(this,"cookieKey");h(this,"cookiesStorage");this.cookieKey=`${H}.${e.clientId}`,this.cookiesStorage=new z}save(e){this.cookiesStorage.set(this.cookieKey,e,{expires:1})}get(){try{return this.cookiesStorage.get(this.cookieKey)}catch{return}}clear(){this.cookiesStorage.remove(this.cookieKey)}}class R extends Error{}class M extends Error{}class J extends Error{}const u="@trimble-oss/trimble-id-react",g="1.0.0",X={configurationEndpoint:"",clientId:"",redirectUrl:"",logoutRedirectUrl:"",scopes:[]};class x{constructor(e){h(this,"tokenProvider");h(this,"cacheManager");h(this,"cookiesManager");h(this,"clientId");h(this,"redirectUrl");h(this,"analyticshttpclient");const{config:t=X}=e;if(this.redirectUrl=t.redirectUrl,t.configurationEndpoint==null||t.configurationEndpoint=="")throw new Error("Configuration endpoint not defined");if(t.clientId==null||t.clientId=="")throw new Error("Consumer key is not defined");this.cookiesManager=new Q({clientId:t.clientId});const n=this.cookiesManager.get(),r=new c.OpenIdEndpointProvider(t.configurationEndpoint);let d=new c.AuthorizationCodeGrantTokenProvider(r,t.clientId,t.redirectUrl).WithScopes(t.scopes);t.logoutRedirectUrl&&(d=d.WithLogoutRedirect(t.logoutRedirectUrl)),(n==null?void 0:n.code_verifier)!=null&&(d=d.WithProofKeyForCodeExchange(n.code_verifier)),this.tokenProvider=d,this.cacheManager=new F,this.clientId=t.clientId,this.analyticshttpclient=c.AnalyticsHttpClient,this.analyticshttpclient.sendInitEvent("TIDClient",this.clientId,u,g)}async loginWithRedirect(e){this.analyticshttpclient.sendMethodEvent(this.loginWithRedirect.name,this.clientId,u,g);const{onRedirect:t}=e||{},n=c.AuthorizationCodeGrantTokenProvider.GenerateCodeVerifier();this.cookiesManager.save({code_verifier:n}),this.tokenProvider=this.tokenProvider.WithProofKeyForCodeExchange(n);const r=await this.tokenProvider.GetOAuthRedirect("state");t!=null?t(r):window.location.assign(r)}async handleCallback(e=window.location.href){const t=this.cookiesManager.get();if(t==null||(t==null?void 0:t.code_verifier)==null)throw new J("Code verifier not available");const n=q(e),r=I(e),d=r.get("identity_provider")??"",T=r.get("state")??"";return await this.tokenProvider.ValidateQuery(n),await this.generateToken(d),{authState:T}}async generateToken(e){var s,w;const t=await this.tokenProvider.RetrieveToken(),n=await this.tokenProvider.RetrieveRefreshToken(),r=await this.tokenProvider.RetrieveIdToken(),d=await this.tokenProvider.RetrieveTokenExpiry(),v=new Date(d).getTime(),m=(w=(s=this.tokenProvider)==null?void 0:s._scopes)==null?void 0:w.join(" ");await this.cacheManager.setToken({scope:m,state:"",session_state:"",identity_provider:e,token_type:"bearer",access_token:t,refresh_token:n,id_token:r,expires_at:v});const U=W(r),_=$(U);await this.cacheManager.setUser(_),await this.reloadCodeVerifier()}async reloadCodeVerifier(){const e=await this.tokenProvider.RetrieveCodeVerifier();this.cookiesManager.save({code_verifier:e}),this.tokenProvider=this.tokenProvider.WithProofKeyForCodeExchange(e)}async getUser(){return this.analyticshttpclient.sendMethodEvent(this.getUser.name,this.clientId,u,g),await this.cacheManager.getUser()}async getAccessTokenSilently(){this.analyticshttpclient.sendMethodEvent(this.getAccessTokenSilently.name,this.clientId,u,g);let e=await this.cacheManager.getToken();if(e==null)throw this.analyticshttpclient.sendExceptionEvent(this.getAccessTokenSilently.name,"No token available",this.clientId,u,g),new M("No token available");const t=new Date(new Date().getTime()+S);if((e==null?void 0:e.expires_at)==null||(e==null?void 0:e.expires_at)<t.getTime()){try{await this.tokenProvider.RetrieveToken()}catch(n){throw this.analyticshttpclient.sendExceptionEvent(this.getAccessTokenSilently.name,n.message,this.clientId,u,g),new R(n.message)}await this.generateToken((e==null?void 0:e.identity_provider)??""),e=await this.cacheManager.getToken()}return(e==null?void 0:e.access_token)||""}async getTokens(){this.analyticshttpclient.sendMethodEvent(this.getTokens.name,this.clientId,u,g);let e=await this.cacheManager.getToken();if(e==null)throw this.analyticshttpclient.sendExceptionEvent(this.getTokens.name,"No token available",this.clientId,u,g),new M("No token available");const t=new Date(new Date().getTime()+S);if((e==null?void 0:e.expires_at)==null||(e==null?void 0:e.expires_at)<t.getTime()){try{await this.tokenProvider.RetrieveToken()}catch(n){throw this.analyticshttpclient.sendExceptionEvent(this.getTokens.name,n.message,this.clientId,u,g),new R(n.message)}await this.generateToken((e==null?void 0:e.identity_provider)??""),e=await this.cacheManager.getToken()}return{access_token:(e==null?void 0:e.access_token)||"",expires_at:(e==null?void 0:e.expires_at)||0,id_token:(e==null?void 0:e.id_token)||""}}async logout(e){this.analyticshttpclient.sendMethodEvent(this.logout.name,this.clientId,u,g);const{onRedirect:t,disabledAutoRedirect:n}=e||{};this.cacheManager&&await this.cacheManager.clear(),this.cookiesManager&&this.cookiesManager.clear();const r=await this.tokenProvider.GetOAuthLogoutRedirect("state");if(t!=null)return t(r);n||window.location.assign(r)}async checkSession(){try{await this.getAccessTokenSilently()}catch{return!1}return!0}async loadUserSession(){await this.loadCacheSessionIntoSDK(),await this.checkSession()||await this.cacheManager.clear()}async loadCacheSessionIntoSDK(){const e=await this.cacheManager.getToken();if(e==null)return;const t=e.access_token,n=e.refresh_token||"",r=e.id_token,d=e.expires_at;this.tokenProvider=this.tokenProvider.WithAccessToken(t,d).WithRefreshToken(n).WithIdToken(r)}getBearerTokenHttpClient(e){return new c.BearerTokenHttpClientProvider(this.tokenProvider,e)}getRedirectUrl(){return this.redirectUrl}}const E=l.createContext(null),A=()=>l.useContext(E)??{},Y=(i,e)=>{switch(e.type){case"INIT":return{...i,isLoading:!1,isAuthenticated:e.user!=null,user:e.user,error:void 0};case"GET_TOKENS_COMPLETE":case"HANDLE_CALLBACK_COMPLETE":case"GET_ACCESS_TOKEN_COMPLETE":return{...i,isLoading:!1,isAuthenticated:e.user!=null,user:e.user,error:void 0};case"LOGOUT":return{...i,isAuthenticated:!1,user:void 0};case"ERROR":return{...i,isLoading:!1,error:e.error}}},Z={isLoading:!0,isAuthenticated:!1},ee=i=>{if(i==null||Object.keys(i).length<=0)return!0;const e=Object.keys(i);for(const t of e)if(i[t]!=null&&i[t]!=="")return!1;return!0},te=i=>!ee(i.config),ie=i=>{const{children:e,configurationEndpoint:t,clientId:n,redirectUrl:r,logoutRedirectUrl:d,scopes:T,onRedirectCallback:v,checkRedirectUrlMatch:m}=i;if(l.useContext(E)!=null)throw new Error("TID Provider already defined");const _={config:{configurationEndpoint:t??"",clientId:n??"",redirectUrl:r??"",logoutRedirectUrl:d??"",scopes:T??[""]}},[s]=l.useState(i.tidClient??new x(_)),[w,y]=l.useReducer(Y,Z),O=l.useRef(!1),oe=l.useMemo(()=>m?s.getRedirectUrl():void 0,[m,s]);l.useEffect(()=>{O.current||(i.tidClient!=null&&te({config:{configurationEndpoint:t,clientId:n,redirectUrl:r,logoutRedirectUrl:d,scopes:T}})&&console.warn("When TID client is pass as prop, any client configuration property sent directly to the TID Provider component will be ignored"),O.current=!0,(async()=>{try{let a;if(B(void 0,oe)){const{authState:f}=await s.handleCallback();a=await s.getUser(),v!=null&&v(f)}else await s.loadUserSession(),a=await s.getUser();y({type:"INIT",user:a})}catch(a){let f=a;typeof a=="string"&&(f=new Error(a)),y({type:"ERROR",error:f})}})())},[s,v]);const D=l.useCallback(async()=>{const a=await s.getAccessTokenSilently(),f=await s.getUser();return y({type:"GET_ACCESS_TOKEN_COMPLETE",user:f}),a},[s]),b=l.useCallback(async()=>{const a=await s.getTokens(),f=await s.getUser();return y({type:"GET_TOKENS_COMPLETE",user:f}),a},[s]),L=l.useCallback(async a=>{await s.loginWithRedirect(a)},[s]),K=l.useCallback(async a=>{await s.logout(a),(a==null?void 0:a.disabledAutoRedirect)!=null&&a.disabledAutoRedirect&&y({type:"LOGOUT"})},[s]),N=l.useCallback(async a=>{const{authState:f}=await s.handleCallback(a),ce=await s.getUser();return y({type:"HANDLE_CALLBACK_COMPLETE",user:ce}),{authState:f}},[s]),ae=l.useMemo(()=>({...w,getAccessTokenSilently:D,getTokens:b,loginWithRedirect:L,handleCallback:N,logout:K}),[w,D,b,L,N,K]);return p.jsx(E.Provider,{value:ae,children:e})},ne=A,re=ie,se=({renderComponent:i,loader:e})=>{const{isAuthenticated:t,isLoading:n,loginWithRedirect:r}=A();return l.useEffect(()=>{!n&&!t&&(async()=>await r())()},[n,t,r]),t?i:e||p.jsx(p.Fragment,{})};o.AuthenticationGuard=se,o.TIDClient=x,o.TIDContext=E,o.TIDProvider=re,o.useAuth=ne,Object.defineProperty(o,Symbol.toStringTag,{value:"Module"})});
|
|
1
|
+
(function(d,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("@trimble-oss/trimble-id"),require("es-cookie"),require("jwt-decode"),require("react"),require("react/jsx-runtime")):typeof define=="function"&&define.amd?define(["exports","@trimble-oss/trimble-id","es-cookie","jwt-decode","react","react/jsx-runtime"],c):(d=typeof globalThis<"u"?globalThis:d||self,c(d.ReactTID={},d.trimbleId,d.esCookie,d.jwt_decode,d.React,d.jsxRuntime))})(this,(function(d,c,b,K,l,m){"use strict";function W(n){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const t in n)if(t!=="default"){const i=Object.getOwnPropertyDescriptor(n,t);Object.defineProperty(e,t,i.get?i:{enumerable:!0,get:()=>n[t]})}}return e.default=n,Object.freeze(e)}const v=W(b);class G{generateCache=(function(){const e={token:void 0,user:void 0};return{async getToken(){return e.token},async getUser(){return e.user},async storeToken(t){e.token=t},async storeUser(t){e.user=t},clear(){return e.token=void 0,e.user=void 0,Promise.resolve(void 0)}}})()}class H{cacheStorage;constructor(){this.cacheStorage=new G().generateCache}async setToken(e){await this.cacheStorage.storeToken(e)}async setUser(e){await this.cacheStorage.storeUser(e)}async getUser(){return this.cacheStorage.getUser()}async getToken(){return this.cacheStorage.getToken()}async clear(){await this.cacheStorage.clear()}}const E=5*6e4,F=["code","state"],S=600*1e3,C=n=>n.split("?")[0],$=n=>n.split("?")[1],P=n=>{let e=n;if(!n.startsWith("?"))try{e=new URL(n).search}catch{}return new URLSearchParams(e)},q=(n=window.location.href,e)=>{if(e!=null){const i=C(e),o=C(n??"");if(i!==o)return!1}const t=P(n);for(const i of F)if(!t.has(i))return!1;return!0},B=n=>({id:n.sub,name:`${n.given_name} ${n.family_name}`,given_name:n.given_name,family_name:n.family_name,picture:n.picture,email:n.email,email_verified:n.email_verified,...n.account_id&&{account_id:n.account_id}}),J="@TID_COOKIE";class j{get(e){const t=v.get(e);if(t==null)throw new Error("Cookie not found");return JSON.parse(t)}set(e,t,i){let o={};window.location.protocol==="https:"&&(o={secure:!0,sameSite:"none"}),i?.expires&&(o.expires=i.expires),i?.domain&&(o.domain=i.domain),v.set(e,JSON.stringify(t),o)}remove(e,t){const i={};t?.domain&&(i.domain=t.domain),v.remove(e,i)}}class z{cookieKey;cookiesStorage;constructor(e){this.cookieKey=`${J}.${e.clientId}`,this.cookiesStorage=new j}save(e){const i={...this.get()||{},...e};this.cookiesStorage.set(this.cookieKey,i,{expires:1})}getStatePayload(){return this.get()?.state_payload}clearStatePayload(){const e=this.get();e&&(delete e.state_payload,this.cookiesStorage.set(this.cookieKey,e,{expires:1}))}get(){try{return this.cookiesStorage.get(this.cookieKey)}catch{return}}clear(){this.cookiesStorage.remove(this.cookieKey)}}class _ extends Error{}class A extends Error{}class Q extends Error{}const h="@trimble-oss/trimble-id-react",g="1.0.2-rc1",X={configurationEndpoint:"",clientId:"",redirectUrl:"",logoutRedirectUrl:"",scopes:[]};class M{tokenProvider;cacheManager;cookiesManager;clientId;redirectUrl;constructor(e){const{config:t=X}=e;if(this.redirectUrl=t.redirectUrl,t.configurationEndpoint==null||t.configurationEndpoint=="")throw new Error("Configuration endpoint not defined");if(t.clientId==null||t.clientId=="")throw new Error("Consumer key is not defined");this.cookiesManager=new z({clientId:t.clientId});const i=this.cookiesManager.get(),o=new c.OpenIdEndpointProvider(t.configurationEndpoint);let r=new c.AuthorizationCodeGrantTokenProvider(o,t.clientId,t.redirectUrl).WithScopes(t.scopes);t.logoutRedirectUrl&&(r=r.WithLogoutRedirect(t.logoutRedirectUrl)),i?.code_verifier!=null&&(r=r.WithProofKeyForCodeExchange(i.code_verifier)),this.tokenProvider=r,this.cacheManager=new H,this.clientId=t.clientId,c.AnalyticsHttpClient.sendInitEvent("TIDClient",this.clientId,h,g),this.cleanupExpiredState()}cleanupExpiredState(){try{const e=this.cookiesManager.getStatePayload();if(e){const t=atob(e),i=JSON.parse(t);Date.now()-i.timestamp>S&&this.cookiesManager.clearStatePayload()}}catch{this.cookiesManager.clearStatePayload()}}async loginWithRedirect(e){c.AnalyticsHttpClient.sendMethodEvent(this.loginWithRedirect.name,this.clientId,h,g);const{onRedirect:t}=e||{},i=window.location.pathname+window.location.search,o=this.createStatePayload(i);this.cookiesManager.save({state_payload:o});const r=c.AuthorizationCodeGrantTokenProvider.GenerateCodeVerifier();this.cookiesManager.save({code_verifier:r}),this.tokenProvider=this.tokenProvider.WithProofKeyForCodeExchange(r);const u=await this.tokenProvider.GetOAuthRedirect(o);t!=null?t(u):window.location.assign(u)}async handleCallback(e=window.location.href){const t=this.cookiesManager.get();if(t==null||t?.code_verifier==null)throw new Q("Code verifier not available");const i=$(e),o=P(e),r=o.get("identity_provider")??"",u=o.get("state")??"",f=this.validateStatePayload(u);if(!f.isValid)throw new Error(`State validation failed: ${f.error}`);return await this.tokenProvider.ValidateQuery(i),await this.generateToken(r),{authState:u,returnTo:f.redirectTo}}async generateToken(e){const t=await this.tokenProvider.RetrieveToken(),i=await this.tokenProvider.RetrieveRefreshToken(),o=await this.tokenProvider.RetrieveIdToken(),r=await this.tokenProvider.RetrieveTokenExpiry(),f=new Date(r).getTime(),y=this.tokenProvider?._scopes?.join(" ");await this.cacheManager.setToken({scope:y,state:"",session_state:"",identity_provider:e,token_type:"bearer",access_token:t,refresh_token:i,id_token:o,expires_at:f});const I=K(o),w=B(I);await this.cacheManager.setUser(w),await this.reloadCodeVerifier()}async reloadCodeVerifier(){const e=await this.tokenProvider.RetrieveCodeVerifier();this.cookiesManager.save({code_verifier:e}),this.tokenProvider=this.tokenProvider.WithProofKeyForCodeExchange(e)}createStatePayload(e){const t=this.generateNonce(),i=Date.now();return btoa(JSON.stringify({redirectTo:e,timestamp:i,nonce:t}))}generateNonce(){const e=new Uint8Array(16);return crypto.getRandomValues(e),Array.from(e,t=>t.toString(16).padStart(2,"0")).join("")}validateStatePayload(e){try{if(!e||e.trim()==="")return{isValid:!1,error:"Empty state parameter"};const t=this.cookiesManager.getStatePayload();if(!t)return{isValid:!1,error:"No stored state found"};const i=atob(e),o=JSON.parse(i),r=atob(t),u=JSON.parse(r);if(!o.nonce||!o.timestamp||!o.redirectTo)return{isValid:!1,error:"Invalid state payload structure"};if(o.nonce!==u.nonce)return{isValid:!1,error:"State nonce mismatch"};const y=Date.now()-o.timestamp;return y>S?{isValid:!1,error:"State expired - possible replay attack"}:y<0?{isValid:!1,error:"State timestamp is in the future - possible replay attack"}:(this.cookiesManager.clearStatePayload(),{isValid:!0,redirectTo:o.redirectTo})}catch{return{isValid:!1,error:"Invalid state format"}}}async getUser(){return c.AnalyticsHttpClient.sendMethodEvent(this.getUser.name,this.clientId,h,g),await this.cacheManager.getUser()}async getAccessTokenSilently(){c.AnalyticsHttpClient.sendMethodEvent(this.getAccessTokenSilently.name,this.clientId,h,g);let e=await this.cacheManager.getToken();if(e==null)throw c.AnalyticsHttpClient.sendExceptionEvent(this.getAccessTokenSilently.name,"No token available",this.clientId,h,g),new A("No token available");const t=new Date(new Date().getTime()+E);if(e?.expires_at==null||e?.expires_at<t.getTime()){try{await this.tokenProvider.RetrieveToken()}catch(i){throw c.AnalyticsHttpClient.sendExceptionEvent(this.getAccessTokenSilently.name,i.message,this.clientId,h,g),new _(i.message)}await this.generateToken(e?.identity_provider??""),e=await this.cacheManager.getToken()}return e?.access_token||""}async getTokens(){c.AnalyticsHttpClient.sendMethodEvent(this.getTokens.name,this.clientId,h,g);let e=await this.cacheManager.getToken();if(e==null)throw c.AnalyticsHttpClient.sendExceptionEvent(this.getTokens.name,"No token available",this.clientId,h,g),new A("No token available");const t=new Date(new Date().getTime()+E);if(e?.expires_at==null||e?.expires_at<t.getTime()){try{await this.tokenProvider.RetrieveToken()}catch(i){throw c.AnalyticsHttpClient.sendExceptionEvent(this.getTokens.name,i.message,this.clientId,h,g),new _(i.message)}await this.generateToken(e?.identity_provider??""),e=await this.cacheManager.getToken()}return{access_token:e?.access_token||"",expires_at:e?.expires_at||0,id_token:e?.id_token||""}}async logout(e){c.AnalyticsHttpClient.sendMethodEvent(this.logout.name,this.clientId,h,g);const{onRedirect:t,disabledAutoRedirect:i}=e||{};this.cacheManager&&await this.cacheManager.clear(),this.cookiesManager&&this.cookiesManager.clear();const o=await this.tokenProvider.GetOAuthLogoutRedirect("state");if(t!=null)return t(o);i||window.location.assign(o)}async checkSession(){try{await this.getAccessTokenSilently()}catch{return!1}return!0}async loadUserSession(){await this.loadCacheSessionIntoSDK(),await this.checkSession()||await this.cacheManager.clear()}async loadCacheSessionIntoSDK(){const e=await this.cacheManager.getToken();if(e==null)return;const t=e.access_token,i=e.refresh_token||"",o=e.id_token,r=e.expires_at;this.tokenProvider=this.tokenProvider.WithAccessToken(t,r).WithRefreshToken(i).WithIdToken(o)}getBearerTokenHttpClient(e){return new c.BearerTokenHttpClientProvider(this.tokenProvider,e)}getRedirectUrl(){return this.redirectUrl}}const T=l.createContext(null),R=()=>l.useContext(T)??{},Y=(n,e)=>{switch(e.type){case"INIT":return{...n,isLoading:!1,isAuthenticated:e.user!=null,user:e.user,error:void 0};case"GET_TOKENS_COMPLETE":case"HANDLE_CALLBACK_COMPLETE":case"GET_ACCESS_TOKEN_COMPLETE":return{...n,isLoading:!1,isAuthenticated:e.user!=null,user:e.user,error:void 0};case"LOGOUT":return{...n,isAuthenticated:!1,user:void 0};case"ERROR":return{...n,isLoading:!1,error:e.error}}},Z={isLoading:!0,isAuthenticated:!1},ee=n=>{if(n==null||Object.keys(n).length<=0)return!0;const e=Object.keys(n);for(const t of e)if(n[t]!=null&&n[t]!=="")return!1;return!0},te=n=>!ee(n.config),ne=n=>{const{children:e,configurationEndpoint:t,clientId:i,redirectUrl:o,logoutRedirectUrl:r,scopes:u,onRedirectCallback:f,checkRedirectUrlMatch:y}=n;if(l.useContext(T)!=null)throw new Error("TID Provider already defined");const w={config:{configurationEndpoint:t??"",clientId:i??"",redirectUrl:o??"",logoutRedirectUrl:r??"",scopes:u??[""]}},[a]=l.useState(n.tidClient??new M(w)),[x,p]=l.useReducer(Y,Z),O=l.useRef(!1),ae=l.useMemo(()=>y?a.getRedirectUrl():void 0,[y,a]);l.useEffect(()=>{O.current||(n.tidClient!=null&&te({config:{configurationEndpoint:t,clientId:i,redirectUrl:o,logoutRedirectUrl:r,scopes:u}})&&console.warn("When TID client is pass as prop, any client configuration property sent directly to the TID Provider component will be ignored"),O.current=!0,(async()=>{try{let s;if(q(void 0,ae)){const k=await a.handleCallback();s=await a.getUser(),f?.(k)}else await a.loadUserSession(),s=await a.getUser();p({type:"INIT",user:s})}catch(s){let k=s;typeof s=="string"&&(k=new Error(s)),p({type:"ERROR",error:k})}})())},[a,f]);const D=l.useCallback(async()=>{const s=await a.getAccessTokenSilently(),k=await a.getUser();return p({type:"GET_ACCESS_TOKEN_COMPLETE",user:k}),s},[a]),U=l.useCallback(async()=>{const s=await a.getTokens(),k=await a.getUser();return p({type:"GET_TOKENS_COMPLETE",user:k}),s},[a]),L=l.useCallback(async s=>{await a.loginWithRedirect(s)},[a]),N=l.useCallback(async s=>{await a.logout(s),s?.disabledAutoRedirect!=null&&s.disabledAutoRedirect&&p({type:"LOGOUT"})},[a]),V=l.useCallback(async s=>{const k=await a.handleCallback(s),ce=await a.getUser();return p({type:"HANDLE_CALLBACK_COMPLETE",user:ce}),k},[a]),se=l.useMemo(()=>({...x,getAccessTokenSilently:D,getTokens:U,loginWithRedirect:L,handleCallback:V,logout:N}),[x,D,U,L,V,N]);return m.jsx(T.Provider,{value:se,children:e})},ie=R,oe=ne,re=({renderComponent:n,loader:e})=>{const{isAuthenticated:t,isLoading:i,loginWithRedirect:o}=R();return l.useEffect(()=>{!i&&!t&&(async()=>await o())()},[i,t,o]),t?n:e||m.jsx(m.Fragment,{})};d.AuthenticationGuard=re,d.TIDClient=M,d.TIDContext=T,d.TIDProvider=oe,d.useAuth=ie,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trimble-oss/trimble-id-react",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2-rc1",
|
|
5
5
|
"homepage": "https://github.com/trimble-oss/trimble-id-sdk-docs-for-react",
|
|
6
6
|
"author": "Trimble developers <developers@trimble.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"build": "tsc && vite build",
|
|
15
15
|
"test": "jest --coverage",
|
|
16
16
|
"docs": "typedoc --out ./docs/documentation ./src/ --tsconfig ./tsconfig.json",
|
|
17
|
-
"lint": "eslint
|
|
18
|
-
"lint:fix": "eslint --fix
|
|
17
|
+
"lint": "eslint src/",
|
|
18
|
+
"lint:fix": "eslint --fix src/",
|
|
19
19
|
"format": "prettier --write src//**/*.{ts,tsx,css} --config ./.prettierrc",
|
|
20
20
|
"prepare": "husky install"
|
|
21
21
|
},
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"module": "./dist/trimble-id-react.es.js",
|
|
32
32
|
"types": "./dist/index.d.ts",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@trimble-oss/trimble-id": "^0.0
|
|
34
|
+
"@trimble-oss/trimble-id": "^1.0.0",
|
|
35
35
|
"es-cookie": "^1.4.0",
|
|
36
36
|
"husky": "^8.0.3",
|
|
37
37
|
"jwt-decode": "^3.1.2"
|
|
@@ -42,29 +42,30 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@babel/preset-env": "^7.22.20",
|
|
45
|
+
"@eslint/js": "^9.37.0",
|
|
45
46
|
"@testing-library/react": "^14.0.0",
|
|
46
47
|
"@types/jest": "^29.5.3",
|
|
47
48
|
"@types/react": "^18.2.15",
|
|
48
49
|
"@types/react-dom": "^18.2.7",
|
|
49
|
-
"@
|
|
50
|
-
"@typescript-eslint/parser": "^6.2.0",
|
|
51
|
-
"@vitejs/plugin-react": "^4.0.3",
|
|
50
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
52
51
|
"babel-jest": "^29.7.0",
|
|
53
|
-
"eslint": "^
|
|
54
|
-
"eslint-config-prettier": "^
|
|
55
|
-
"eslint-plugin-prettier": "^5.
|
|
56
|
-
"eslint-plugin-react": "^7.
|
|
57
|
-
"eslint-plugin-react-hooks": "^
|
|
58
|
-
"eslint-plugin-react-refresh": "^0.4.
|
|
59
|
-
"
|
|
52
|
+
"eslint": "^9.37.0",
|
|
53
|
+
"eslint-config-prettier": "^10.1.8",
|
|
54
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
55
|
+
"eslint-plugin-react": "^7.37.5",
|
|
56
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
57
|
+
"eslint-plugin-react-refresh": "^0.4.26",
|
|
58
|
+
"globals": "^16.2.0",
|
|
59
|
+
"gts": "^7.0.0",
|
|
60
|
+
"typescript-eslint": "^8.46.0",
|
|
60
61
|
"jest": "^29.6.2",
|
|
61
62
|
"jest-environment-jsdom": "^29.6.2",
|
|
62
|
-
"lint-staged": "^
|
|
63
|
+
"lint-staged": "^16.2.7",
|
|
63
64
|
"prettier": "^3.0.0",
|
|
64
65
|
"ts-jest": "^29.1.1",
|
|
65
66
|
"typedoc": "^0.25.2",
|
|
66
67
|
"typescript": "^5.0.2",
|
|
67
|
-
"vite": "^
|
|
68
|
+
"vite": "^7.3.0",
|
|
68
69
|
"vite-plugin-dts": "^1.4.1",
|
|
69
70
|
"vite-plugin-linter": "^1.2.0",
|
|
70
71
|
"vite-tsconfig-paths": "^3.5.0"
|