@taruvi/sdk 1.0.2 → 1.0.4
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/AI_IMPLEMENTATION_GUIDE.md +733 -0
- package/package.json +1 -1
- package/src/client.ts +32 -6
- package/src/index.ts +2 -1
- package/src/lib/Database/DatabaseClient.ts +16 -7
- package/src/lib/Storage/StorageClient.ts +24 -9
- package/src/lib/auth/AuthClient.ts +201 -48
- package/src/lib-internal/routes/DatabaseRoutes.ts +5 -1
- package/src/lib-internal/routes/StorageRoutes.ts +9 -1
- package/src/lib-internal/token/TokenClient.ts +249 -13
- package/src/lib-internal/routes/RouteBuilder.ts +0 -0
|
@@ -1,30 +1,266 @@
|
|
|
1
1
|
import { getRuntimeEnvironment } from "../../utils/utils.js"
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Token management interface for Web UI Flow authentication
|
|
5
|
+
*/
|
|
6
|
+
export interface AuthTokens {
|
|
7
|
+
sessionToken?: string | undefined;
|
|
8
|
+
accessToken: string;
|
|
9
|
+
refreshToken: string;
|
|
10
|
+
expiresIn?: number | undefined;
|
|
11
|
+
expiresAt?: number | undefined;
|
|
12
|
+
tokenType?: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* TokenClient - Manages authentication tokens for both browser and server environments
|
|
17
|
+
* Implements Web UI Flow token handling as described in Taruvi documentation
|
|
18
|
+
*/
|
|
3
19
|
export class TokenClient {
|
|
4
|
-
|
|
20
|
+
private static readonly ACCESS_TOKEN_KEY = 'jwt';
|
|
21
|
+
private static readonly REFRESH_TOKEN_KEY = 'refresh_token';
|
|
22
|
+
private static readonly SESSION_TOKEN_KEY = 'session_token';
|
|
23
|
+
private static readonly EXPIRES_AT_KEY = 'token_expires_at';
|
|
24
|
+
private static readonly TOKEN_TYPE_KEY = 'token_type';
|
|
25
|
+
|
|
5
26
|
private runTimeEnvironment: string
|
|
6
27
|
private browserRunTime: boolean
|
|
7
|
-
|
|
28
|
+
private serverToken: string | null = null
|
|
8
29
|
|
|
9
30
|
constructor(token?: string) {
|
|
10
31
|
this.runTimeEnvironment = getRuntimeEnvironment()
|
|
11
32
|
this.browserRunTime = this.runTimeEnvironment == "Browser"
|
|
12
|
-
|
|
33
|
+
|
|
34
|
+
// For server-side usage, store the token
|
|
35
|
+
if (!this.browserRunTime && token) {
|
|
36
|
+
this.serverToken = token
|
|
37
|
+
}
|
|
13
38
|
}
|
|
14
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Get access token (supports both browser and server)
|
|
42
|
+
*/
|
|
15
43
|
getToken(): string | null {
|
|
16
44
|
if (this.browserRunTime) {
|
|
17
|
-
|
|
18
|
-
|
|
45
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
return localStorage.getItem(TokenClient.ACCESS_TOKEN_KEY)
|
|
49
|
+
}
|
|
50
|
+
return this.serverToken
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Set all authentication tokens from Web UI Flow callback
|
|
55
|
+
*/
|
|
56
|
+
setTokens(tokens: AuthTokens): void {
|
|
57
|
+
if (!this.browserRunTime) {
|
|
58
|
+
console.warn('Token storage is only available in browser environment')
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
// Store access token
|
|
68
|
+
localStorage.setItem(TokenClient.ACCESS_TOKEN_KEY, tokens.accessToken)
|
|
69
|
+
|
|
70
|
+
// Store refresh token
|
|
71
|
+
localStorage.setItem(TokenClient.REFRESH_TOKEN_KEY, tokens.refreshToken)
|
|
72
|
+
|
|
73
|
+
// Store session token if provided
|
|
74
|
+
if (tokens.sessionToken) {
|
|
75
|
+
localStorage.setItem(TokenClient.SESSION_TOKEN_KEY, tokens.sessionToken)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Calculate and store expiration time
|
|
79
|
+
if (tokens.expiresIn) {
|
|
80
|
+
const expiresAt = Date.now() + (tokens.expiresIn * 1000)
|
|
81
|
+
localStorage.setItem(TokenClient.EXPIRES_AT_KEY, expiresAt.toString())
|
|
82
|
+
} else if (tokens.expiresAt) {
|
|
83
|
+
localStorage.setItem(TokenClient.EXPIRES_AT_KEY, tokens.expiresAt.toString())
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Store token type
|
|
87
|
+
if (tokens.tokenType) {
|
|
88
|
+
localStorage.setItem(TokenClient.TOKEN_TYPE_KEY, tokens.tokenType)
|
|
89
|
+
}
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error('Failed to store authentication tokens:', err)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get all stored tokens
|
|
97
|
+
*/
|
|
98
|
+
getTokens(): AuthTokens | null {
|
|
99
|
+
if (!this.browserRunTime) {
|
|
100
|
+
return null
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const accessToken = localStorage.getItem(TokenClient.ACCESS_TOKEN_KEY)
|
|
109
|
+
const refreshToken = localStorage.getItem(TokenClient.REFRESH_TOKEN_KEY)
|
|
110
|
+
const sessionToken = localStorage.getItem(TokenClient.SESSION_TOKEN_KEY)
|
|
111
|
+
const expiresAt = localStorage.getItem(TokenClient.EXPIRES_AT_KEY)
|
|
112
|
+
const tokenType = localStorage.getItem(TokenClient.TOKEN_TYPE_KEY)
|
|
113
|
+
|
|
114
|
+
if (!accessToken || !refreshToken) {
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const tokens: any = {
|
|
119
|
+
accessToken,
|
|
120
|
+
refreshToken,
|
|
121
|
+
tokenType: tokenType || 'Bearer',
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (sessionToken) {
|
|
125
|
+
tokens.sessionToken = sessionToken
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (expiresAt) {
|
|
129
|
+
tokens.expiresIn = Math.floor((parseInt(expiresAt) - Date.now()) / 1000)
|
|
130
|
+
tokens.expiresAt = parseInt(expiresAt)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return tokens
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error('Failed to retrieve authentication tokens:', err)
|
|
136
|
+
return null
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get refresh token
|
|
142
|
+
*/
|
|
143
|
+
getRefreshToken(): string | null {
|
|
144
|
+
if (!this.browserRunTime) {
|
|
145
|
+
return null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return localStorage.getItem(TokenClient.REFRESH_TOKEN_KEY)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get session token
|
|
157
|
+
*/
|
|
158
|
+
getSessionToken(): string | null {
|
|
159
|
+
if (!this.browserRunTime) {
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
164
|
+
return null
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return localStorage.getItem(TokenClient.SESSION_TOKEN_KEY)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Check if user is authenticated (has valid access token)
|
|
172
|
+
*/
|
|
173
|
+
isAuthenticated(): boolean {
|
|
174
|
+
return !!this.getToken()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Check if access token is expired
|
|
179
|
+
*/
|
|
180
|
+
isTokenExpired(): boolean {
|
|
181
|
+
if (!this.browserRunTime) {
|
|
182
|
+
return true
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
186
|
+
return true
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const expiresAt = localStorage.getItem(TokenClient.EXPIRES_AT_KEY)
|
|
190
|
+
if (!expiresAt) {
|
|
191
|
+
return true
|
|
19
192
|
}
|
|
20
|
-
|
|
21
|
-
|
|
193
|
+
|
|
194
|
+
return parseInt(expiresAt) < Date.now()
|
|
22
195
|
}
|
|
23
196
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Update access token after refresh
|
|
199
|
+
* ⚠️ IMPORTANT: Taruvi uses refresh token rotation
|
|
200
|
+
* When you refresh, you get BOTH a new access token AND a new refresh token
|
|
201
|
+
*/
|
|
202
|
+
updateAccessToken(accessToken: string, expiresIn: number): void {
|
|
203
|
+
if (!this.browserRunTime) {
|
|
204
|
+
console.warn('Token update is only available in browser environment')
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
localStorage.setItem(TokenClient.ACCESS_TOKEN_KEY, accessToken)
|
|
214
|
+
const expiresAt = Date.now() + (expiresIn * 1000)
|
|
215
|
+
localStorage.setItem(TokenClient.EXPIRES_AT_KEY, expiresAt.toString())
|
|
216
|
+
} catch (err) {
|
|
217
|
+
console.error('Failed to update access token:', err)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Update refresh token after rotation
|
|
223
|
+
* ⚠️ IMPORTANT: Taruvi rotates refresh tokens
|
|
224
|
+
* Always update the refresh token when you receive a new one
|
|
225
|
+
*/
|
|
226
|
+
updateRefreshToken(refreshToken: string): void {
|
|
227
|
+
if (!this.browserRunTime) {
|
|
228
|
+
console.warn('Token update is only available in browser environment')
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
localStorage.setItem(TokenClient.REFRESH_TOKEN_KEY, refreshToken)
|
|
238
|
+
} catch (err) {
|
|
239
|
+
console.error('Failed to update refresh token:', err)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Clear all tokens (logout)
|
|
245
|
+
*/
|
|
246
|
+
clearTokens(): void {
|
|
247
|
+
if (!this.browserRunTime) {
|
|
248
|
+
this.serverToken = null
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
localStorage.removeItem(TokenClient.ACCESS_TOKEN_KEY)
|
|
258
|
+
localStorage.removeItem(TokenClient.REFRESH_TOKEN_KEY)
|
|
259
|
+
localStorage.removeItem(TokenClient.SESSION_TOKEN_KEY)
|
|
260
|
+
localStorage.removeItem(TokenClient.EXPIRES_AT_KEY)
|
|
261
|
+
localStorage.removeItem(TokenClient.TOKEN_TYPE_KEY)
|
|
262
|
+
} catch (err) {
|
|
263
|
+
console.error('Failed to clear tokens:', err)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
30
266
|
}
|
|
File without changes
|