@uipath/uipath-typescript 1.0.0 → 1.1.0
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/LICENSE +2 -2
- package/README.md +2 -0
- package/dist/assets/index.cjs +199 -221
- package/dist/assets/index.mjs +199 -221
- package/dist/buckets/index.cjs +199 -221
- package/dist/buckets/index.mjs +199 -221
- package/dist/cases/index.cjs +220 -238
- package/dist/cases/index.mjs +220 -238
- package/dist/conversational-agent/index.cjs +6622 -0
- package/dist/conversational-agent/index.d.ts +6579 -0
- package/dist/conversational-agent/index.mjs +6575 -0
- package/dist/core/index.cjs +1196 -1158
- package/dist/core/index.d.ts +2 -10
- package/dist/core/index.mjs +1196 -1158
- package/dist/entities/index.cjs +199 -221
- package/dist/entities/index.mjs +199 -221
- package/dist/index.cjs +744 -418
- package/dist/index.d.ts +4342 -13
- package/dist/index.mjs +738 -419
- package/dist/index.umd.js +744 -418
- package/dist/maestro-processes/index.cjs +199 -221
- package/dist/maestro-processes/index.mjs +199 -221
- package/dist/processes/index.cjs +246 -232
- package/dist/processes/index.d.ts +1 -1
- package/dist/processes/index.mjs +246 -232
- package/dist/queues/index.cjs +199 -221
- package/dist/queues/index.mjs +199 -221
- package/dist/tasks/index.cjs +199 -221
- package/dist/tasks/index.mjs +199 -221
- package/package.json +22 -4
package/dist/core/index.mjs
CHANGED
|
@@ -3887,1333 +3887,1371 @@ function hasSecretConfig(config) {
|
|
|
3887
3887
|
}
|
|
3888
3888
|
|
|
3889
3889
|
/**
|
|
3890
|
-
*
|
|
3891
|
-
*
|
|
3892
|
-
* - For OAuth tokens: Uses session storage with client ID-based keys
|
|
3893
|
-
* - For Secret tokens: Stores only in memory, allowing multiple instances
|
|
3890
|
+
* Base error class for all UiPath SDK errors
|
|
3891
|
+
* Extends Error for standard error handling compatibility
|
|
3894
3892
|
*/
|
|
3895
|
-
class
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
this.STORAGE_KEY_PREFIX = 'uipath_sdk_user_token-';
|
|
3907
|
-
this.refreshPromise = null;
|
|
3908
|
-
}
|
|
3909
|
-
/**
|
|
3910
|
-
* Checks if a token is expired
|
|
3911
|
-
* @param tokenInfo The token info to check
|
|
3912
|
-
* @returns true if the token is expired, false otherwise
|
|
3913
|
-
*/
|
|
3914
|
-
isTokenExpired(tokenInfo) {
|
|
3915
|
-
// If no token info or no expiration date, token is not expired
|
|
3916
|
-
if (!tokenInfo?.expiresAt) {
|
|
3917
|
-
return false;
|
|
3918
|
-
}
|
|
3919
|
-
return new Date() >= tokenInfo.expiresAt;
|
|
3920
|
-
}
|
|
3921
|
-
/**
|
|
3922
|
-
* Gets the storage key for this TokenManager instance
|
|
3923
|
-
*/
|
|
3924
|
-
_getStorageKey() {
|
|
3925
|
-
return `${this.STORAGE_KEY_PREFIX}${this.config.clientId}`;
|
|
3926
|
-
}
|
|
3927
|
-
/**
|
|
3928
|
-
* Loads token from session storage if available
|
|
3929
|
-
* @returns true if a valid token was loaded, false otherwise
|
|
3930
|
-
*/
|
|
3931
|
-
loadFromStorage() {
|
|
3932
|
-
// Only OAuth tokens are stored in session storage
|
|
3933
|
-
if (!isBrowser || !this.isOAuth) {
|
|
3934
|
-
return false;
|
|
3935
|
-
}
|
|
3936
|
-
try {
|
|
3937
|
-
const storedToken = sessionStorage.getItem(this._getStorageKey());
|
|
3938
|
-
if (!storedToken) {
|
|
3939
|
-
return false;
|
|
3940
|
-
}
|
|
3941
|
-
const tokenInfo = this._parseTokenInfo(storedToken);
|
|
3942
|
-
if (!tokenInfo) {
|
|
3943
|
-
// Invalid token format, clear it
|
|
3944
|
-
sessionStorage.removeItem(this._getStorageKey());
|
|
3945
|
-
return false;
|
|
3946
|
-
}
|
|
3947
|
-
// Check if token is expired
|
|
3948
|
-
if (this.isTokenExpired(tokenInfo)) {
|
|
3949
|
-
// Token expired, clear it
|
|
3950
|
-
sessionStorage.removeItem(this._getStorageKey());
|
|
3951
|
-
return false;
|
|
3952
|
-
}
|
|
3953
|
-
// Valid token found, use it
|
|
3954
|
-
this.currentToken = tokenInfo;
|
|
3955
|
-
this._updateExecutionContext(tokenInfo);
|
|
3956
|
-
return true;
|
|
3957
|
-
}
|
|
3958
|
-
catch (error) {
|
|
3959
|
-
console.warn('Failed to load token from session storage', error);
|
|
3960
|
-
return false;
|
|
3961
|
-
}
|
|
3962
|
-
}
|
|
3963
|
-
/**
|
|
3964
|
-
* Parse and validate token info from storage
|
|
3965
|
-
* @param storedToken JSON string from storage
|
|
3966
|
-
* @returns Valid TokenInfo or undefined if invalid
|
|
3967
|
-
*/
|
|
3968
|
-
_parseTokenInfo(storedToken) {
|
|
3969
|
-
try {
|
|
3970
|
-
const parsed = JSON.parse(storedToken);
|
|
3971
|
-
// Basic validation
|
|
3972
|
-
if (typeof parsed !== 'object' || !parsed) {
|
|
3973
|
-
return undefined;
|
|
3974
|
-
}
|
|
3975
|
-
if (typeof parsed.token !== 'string' || !parsed.token) {
|
|
3976
|
-
return undefined;
|
|
3977
|
-
}
|
|
3978
|
-
if (parsed.type !== 'secret' && parsed.type !== 'oauth') {
|
|
3979
|
-
return undefined;
|
|
3980
|
-
}
|
|
3981
|
-
const tokenInfo = parsed;
|
|
3982
|
-
// Convert string date back to Date object
|
|
3983
|
-
if (tokenInfo.expiresAt) {
|
|
3984
|
-
tokenInfo.expiresAt = new Date(tokenInfo.expiresAt);
|
|
3985
|
-
// Verify it's a valid date
|
|
3986
|
-
if (isNaN(tokenInfo.expiresAt.getTime())) {
|
|
3987
|
-
return undefined;
|
|
3988
|
-
}
|
|
3989
|
-
}
|
|
3990
|
-
return tokenInfo;
|
|
3991
|
-
}
|
|
3992
|
-
catch (error) {
|
|
3993
|
-
console.warn('Failed to parse token info', error);
|
|
3994
|
-
return undefined;
|
|
3995
|
-
}
|
|
3996
|
-
}
|
|
3997
|
-
/**
|
|
3998
|
-
* Sets a new token and updates all necessary contexts
|
|
3999
|
-
*/
|
|
4000
|
-
setToken(tokenInfo) {
|
|
4001
|
-
this.currentToken = tokenInfo;
|
|
4002
|
-
// Store token in execution context
|
|
4003
|
-
this._updateExecutionContext(tokenInfo);
|
|
4004
|
-
// Store in session storage if in browser and this is an OAuth token
|
|
4005
|
-
if (isBrowser && this.isOAuth) {
|
|
4006
|
-
try {
|
|
4007
|
-
sessionStorage.setItem(this._getStorageKey(), JSON.stringify(tokenInfo));
|
|
4008
|
-
}
|
|
4009
|
-
catch (error) {
|
|
4010
|
-
console.warn('Failed to store token in session storage', error);
|
|
4011
|
-
}
|
|
4012
|
-
}
|
|
4013
|
-
}
|
|
4014
|
-
/**
|
|
4015
|
-
* Gets the current token information
|
|
4016
|
-
*/
|
|
4017
|
-
getTokenInfo() {
|
|
4018
|
-
return this.currentToken;
|
|
4019
|
-
}
|
|
4020
|
-
/**
|
|
4021
|
-
* Gets just the token string
|
|
4022
|
-
*/
|
|
4023
|
-
getToken() {
|
|
4024
|
-
return this.currentToken?.token;
|
|
4025
|
-
}
|
|
4026
|
-
/**
|
|
4027
|
-
* Checks if we have a valid token
|
|
4028
|
-
*/
|
|
4029
|
-
hasValidToken() {
|
|
4030
|
-
if (!this.currentToken) {
|
|
4031
|
-
return false;
|
|
4032
|
-
}
|
|
4033
|
-
if (this.isTokenExpired(this.currentToken)) {
|
|
4034
|
-
return false;
|
|
4035
|
-
}
|
|
4036
|
-
return true;
|
|
4037
|
-
}
|
|
4038
|
-
/**
|
|
4039
|
-
* Clears the current token
|
|
4040
|
-
*/
|
|
4041
|
-
clearToken() {
|
|
4042
|
-
this.currentToken = undefined;
|
|
4043
|
-
this.executionContext.set('tokenInfo', undefined);
|
|
4044
|
-
const headers = this.executionContext.getHeaders();
|
|
4045
|
-
delete headers['Authorization'];
|
|
4046
|
-
this.executionContext.setHeaders(headers);
|
|
4047
|
-
// Remove from session storage if this is an OAuth token
|
|
4048
|
-
if (isBrowser && this.isOAuth) {
|
|
4049
|
-
try {
|
|
4050
|
-
sessionStorage.removeItem(this._getStorageKey());
|
|
4051
|
-
}
|
|
4052
|
-
catch (error) {
|
|
4053
|
-
console.warn('Failed to remove token from session storage', error);
|
|
4054
|
-
}
|
|
3893
|
+
class UiPathError extends Error {
|
|
3894
|
+
constructor(type, params) {
|
|
3895
|
+
super(params.message);
|
|
3896
|
+
this.name = type;
|
|
3897
|
+
this.type = type;
|
|
3898
|
+
this.statusCode = params.statusCode;
|
|
3899
|
+
this.requestId = params.requestId;
|
|
3900
|
+
this.timestamp = new Date();
|
|
3901
|
+
// Maintains proper stack trace for where our error was thrown
|
|
3902
|
+
if (Error.captureStackTrace) {
|
|
3903
|
+
Error.captureStackTrace(this, this.constructor);
|
|
4055
3904
|
}
|
|
4056
3905
|
}
|
|
4057
3906
|
/**
|
|
4058
|
-
*
|
|
4059
|
-
*/
|
|
4060
|
-
_updateExecutionContext(tokenInfo) {
|
|
4061
|
-
this.executionContext.set('tokenInfo', tokenInfo);
|
|
4062
|
-
// Update authorization header
|
|
4063
|
-
this.executionContext.setHeaders({
|
|
4064
|
-
'Authorization': `Bearer ${tokenInfo.token}`
|
|
4065
|
-
});
|
|
4066
|
-
}
|
|
4067
|
-
/**
|
|
4068
|
-
* Refreshes the access token using the stored refresh token.
|
|
4069
|
-
* This method only works for OAuth flow.
|
|
4070
|
-
* Uses a lock mechanism to prevent multiple simultaneous refreshes.
|
|
4071
|
-
* @returns A promise that resolves to the new AuthToken
|
|
4072
|
-
* @throws Error if not in OAuth flow, refresh token is missing, or the request fails
|
|
3907
|
+
* Returns a clean JSON representation of the error
|
|
4073
3908
|
*/
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
// Wait for refresh to complete
|
|
4083
|
-
const result = await this.refreshPromise;
|
|
4084
|
-
return result;
|
|
4085
|
-
}
|
|
4086
|
-
finally {
|
|
4087
|
-
// Clear the refresh promise when done (success or failure)
|
|
4088
|
-
this.refreshPromise = null;
|
|
4089
|
-
}
|
|
3909
|
+
toJSON() {
|
|
3910
|
+
return {
|
|
3911
|
+
type: this.type,
|
|
3912
|
+
message: this.message,
|
|
3913
|
+
statusCode: this.statusCode,
|
|
3914
|
+
requestId: this.requestId,
|
|
3915
|
+
timestamp: this.timestamp
|
|
3916
|
+
};
|
|
4090
3917
|
}
|
|
4091
3918
|
/**
|
|
4092
|
-
*
|
|
3919
|
+
* Returns detailed debug information including stack trace
|
|
4093
3920
|
*/
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
}
|
|
4099
|
-
// Get current token info from token manager
|
|
4100
|
-
const tokenInfo = this.getTokenInfo();
|
|
4101
|
-
if (!tokenInfo?.refreshToken) {
|
|
4102
|
-
throw new Error('No refresh token available. User may need to re-authenticate.');
|
|
4103
|
-
}
|
|
4104
|
-
const orgName = this.config.orgName;
|
|
4105
|
-
const body = new URLSearchParams({
|
|
4106
|
-
grant_type: 'refresh_token',
|
|
4107
|
-
client_id: this.config.clientId,
|
|
4108
|
-
refresh_token: tokenInfo.refreshToken
|
|
4109
|
-
});
|
|
4110
|
-
const response = await fetch(`${this.config.baseUrl}/${orgName}/identity_/connect/token`, {
|
|
4111
|
-
method: 'POST',
|
|
4112
|
-
headers: {
|
|
4113
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
4114
|
-
},
|
|
4115
|
-
body: body.toString()
|
|
4116
|
-
});
|
|
4117
|
-
if (!response.ok) {
|
|
4118
|
-
const errorData = await response.json().catch(() => ({ message: response.statusText }));
|
|
4119
|
-
console.error("Token refresh error:", errorData);
|
|
4120
|
-
// Clear the invalid token to prevent further failed requests
|
|
4121
|
-
this.clearToken();
|
|
4122
|
-
throw new Error(`Failed to refresh access token: ${JSON.stringify(errorData)}`);
|
|
4123
|
-
}
|
|
4124
|
-
const token = await response.json();
|
|
4125
|
-
this.setToken({
|
|
4126
|
-
token: token.access_token,
|
|
4127
|
-
type: 'oauth',
|
|
4128
|
-
expiresAt: new Date(Date.now() + token.expires_in * 1000),
|
|
4129
|
-
refreshToken: token.refresh_token
|
|
4130
|
-
});
|
|
4131
|
-
return token;
|
|
3921
|
+
getDebugInfo() {
|
|
3922
|
+
return {
|
|
3923
|
+
...this.toJSON(),
|
|
3924
|
+
stack: this.stack
|
|
3925
|
+
};
|
|
4132
3926
|
}
|
|
4133
3927
|
}
|
|
4134
3928
|
|
|
4135
3929
|
/**
|
|
4136
|
-
*
|
|
4137
|
-
* Centralized location for all API endpoints used throughout the SDK
|
|
3930
|
+
* HTTP status code constants for error handling
|
|
4138
3931
|
*/
|
|
3932
|
+
const HttpStatus = {
|
|
3933
|
+
// Client errors (4xx)
|
|
3934
|
+
BAD_REQUEST: 400,
|
|
3935
|
+
UNAUTHORIZED: 401,
|
|
3936
|
+
FORBIDDEN: 403,
|
|
3937
|
+
NOT_FOUND: 404,
|
|
3938
|
+
TOO_MANY_REQUESTS: 429,
|
|
3939
|
+
// Server errors (5xx)
|
|
3940
|
+
INTERNAL_SERVER_ERROR: 500,
|
|
3941
|
+
NOT_IMPLEMENTED: 501,
|
|
3942
|
+
BAD_GATEWAY: 502,
|
|
3943
|
+
SERVICE_UNAVAILABLE: 503,
|
|
3944
|
+
GATEWAY_TIMEOUT: 504
|
|
3945
|
+
};
|
|
4139
3946
|
/**
|
|
4140
|
-
*
|
|
3947
|
+
* Error type constants for consistent error identification
|
|
4141
3948
|
*/
|
|
4142
|
-
const
|
|
3949
|
+
const ErrorType = {
|
|
3950
|
+
AUTHENTICATION: 'AuthenticationError',
|
|
3951
|
+
AUTHORIZATION: 'AuthorizationError',
|
|
3952
|
+
VALIDATION: 'ValidationError',
|
|
3953
|
+
NOT_FOUND: 'NotFoundError',
|
|
3954
|
+
RATE_LIMIT: 'RateLimitError',
|
|
3955
|
+
SERVER: 'ServerError',
|
|
3956
|
+
NETWORK: 'NetworkError'
|
|
3957
|
+
};
|
|
4143
3958
|
/**
|
|
4144
|
-
*
|
|
3959
|
+
* Standard error message constants
|
|
4145
3960
|
*/
|
|
4146
|
-
const
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
3961
|
+
const ErrorMessages = {
|
|
3962
|
+
// Authentication errors
|
|
3963
|
+
AUTHENTICATION_FAILED: 'Authentication failed',
|
|
3964
|
+
// Authorization errors
|
|
3965
|
+
ACCESS_DENIED: 'Access denied',
|
|
3966
|
+
// Validation errors
|
|
3967
|
+
VALIDATION_FAILED: 'Validation failed',
|
|
3968
|
+
// Not found errors
|
|
3969
|
+
RESOURCE_NOT_FOUND: 'Resource not found',
|
|
3970
|
+
// Rate limit errors
|
|
3971
|
+
RATE_LIMIT_EXCEEDED: 'Rate limit exceeded',
|
|
3972
|
+
// Server errors
|
|
3973
|
+
INTERNAL_SERVER_ERROR: 'Internal Server error occurred',
|
|
3974
|
+
// Network errors
|
|
3975
|
+
NETWORK_ERROR: 'Network error occurred'};
|
|
4150
3976
|
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
*/
|
|
4166
|
-
static isInOAuthCallback() {
|
|
4167
|
-
if (!isBrowser)
|
|
4168
|
-
return false;
|
|
4169
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
4170
|
-
const code = urlParams.get('code');
|
|
4171
|
-
const hasCodeVerifier = sessionStorage.getItem('uipath_sdk_code_verifier');
|
|
4172
|
-
return !!(code && hasCodeVerifier);
|
|
4173
|
-
}
|
|
4174
|
-
/**
|
|
4175
|
-
* Get stored OAuth context
|
|
4176
|
-
*/
|
|
4177
|
-
static getStoredOAuthContext() {
|
|
4178
|
-
if (!isBrowser) {
|
|
4179
|
-
return null;
|
|
4180
|
-
}
|
|
4181
|
-
try {
|
|
4182
|
-
const stored = sessionStorage.getItem('uipath_sdk_oauth_context');
|
|
4183
|
-
if (!stored) {
|
|
4184
|
-
return null;
|
|
4185
|
-
}
|
|
4186
|
-
const context = JSON.parse(stored);
|
|
4187
|
-
// Validate required fields
|
|
4188
|
-
if (!context.codeVerifier || !context.clientId || !context.redirectUri ||
|
|
4189
|
-
!context.baseUrl || !context.orgName) {
|
|
4190
|
-
sessionStorage.removeItem('uipath_sdk_oauth_context');
|
|
4191
|
-
return null;
|
|
4192
|
-
}
|
|
4193
|
-
return context;
|
|
4194
|
-
}
|
|
4195
|
-
catch (error) {
|
|
4196
|
-
sessionStorage.removeItem('uipath_sdk_oauth_context');
|
|
4197
|
-
console.warn('Failed to parse stored OAuth context from session storage', error);
|
|
4198
|
-
return null;
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
/**
|
|
4202
|
-
* Merges provided config with stored OAuth context, prioritizing stored values
|
|
4203
|
-
*/
|
|
4204
|
-
static _mergeConfigWithContext(config, context) {
|
|
4205
|
-
return {
|
|
4206
|
-
...config,
|
|
4207
|
-
baseUrl: context.baseUrl,
|
|
4208
|
-
orgName: context.orgName,
|
|
4209
|
-
tenantName: context.tenantName,
|
|
4210
|
-
clientId: context.clientId,
|
|
4211
|
-
redirectUri: context.redirectUri,
|
|
4212
|
-
scope: context.scope
|
|
4213
|
-
};
|
|
4214
|
-
}
|
|
4215
|
-
/**
|
|
4216
|
-
* Get the token manager instance
|
|
4217
|
-
*/
|
|
4218
|
-
getTokenManager() {
|
|
4219
|
-
return this.tokenManager;
|
|
4220
|
-
}
|
|
4221
|
-
/**
|
|
4222
|
-
* Authenticates the user based on the provided SDK configuration.
|
|
4223
|
-
* This method handles OAuth 2.0 authentication flow only.
|
|
4224
|
-
* For secret-based authentication, see authenticateWithSecret().
|
|
4225
|
-
* @param config The SDK configuration object.
|
|
4226
|
-
* @returns A promise that resolves to true if authentication is successful, otherwise false.
|
|
4227
|
-
* In an OAuth flow, this method will trigger a page redirect and the promise will not resolve.
|
|
4228
|
-
*/
|
|
4229
|
-
async authenticate(config) {
|
|
4230
|
-
if (!isBrowser) {
|
|
4231
|
-
return false;
|
|
4232
|
-
}
|
|
4233
|
-
// First priority: Complete OAuth callback if we detect it
|
|
4234
|
-
if (AuthService.isInOAuthCallback()) {
|
|
4235
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
4236
|
-
const code = urlParams.get('code');
|
|
4237
|
-
if (!code) {
|
|
4238
|
-
throw new Error('Authorization code missing in OAuth callback');
|
|
4239
|
-
}
|
|
4240
|
-
// Check if token already exists (prevents duplicate processing)
|
|
4241
|
-
if (this.tokenManager.hasValidToken()) {
|
|
4242
|
-
return true;
|
|
4243
|
-
}
|
|
4244
|
-
// Ensure we have OAuth config for callback completion
|
|
4245
|
-
if (!hasOAuthConfig(config)) {
|
|
4246
|
-
throw new Error('OAuth configuration incomplete: clientId, redirectUri, and scope are required for OAuth callback');
|
|
4247
|
-
}
|
|
4248
|
-
const result = await this._authenticateWithOAuth(config.clientId, config.redirectUri, config.scope);
|
|
4249
|
-
return result;
|
|
4250
|
-
}
|
|
4251
|
-
// Secondly: Try to load existing valid token from storage
|
|
4252
|
-
const loadedFromStorage = this.tokenManager.loadFromStorage();
|
|
4253
|
-
// If we have a valid token from storage, return true
|
|
4254
|
-
if (loadedFromStorage && this.tokenManager.hasValidToken()) {
|
|
4255
|
-
return true;
|
|
4256
|
-
}
|
|
4257
|
-
// Start new OAuth flow if config has OAuth fields
|
|
4258
|
-
if (hasOAuthConfig(config)) {
|
|
4259
|
-
return await this._authenticateWithOAuth(config.clientId, config.redirectUri, config.scope);
|
|
4260
|
-
}
|
|
4261
|
-
return false;
|
|
4262
|
-
}
|
|
4263
|
-
/**
|
|
4264
|
-
* Authenticate using OAuth flow
|
|
4265
|
-
*/
|
|
4266
|
-
async _authenticateWithOAuth(clientId, redirectUri, scope) {
|
|
4267
|
-
if (!isBrowser) {
|
|
4268
|
-
throw new Error('OAuth flow is only supported in browser environments');
|
|
4269
|
-
}
|
|
4270
|
-
// Check if we have a stored code verifier indicating we're in an OAuth flow
|
|
4271
|
-
const codeVerifier = sessionStorage.getItem('uipath_sdk_code_verifier');
|
|
4272
|
-
const isInOAuthFlow = codeVerifier !== null;
|
|
4273
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
4274
|
-
const code = urlParams.get('code');
|
|
4275
|
-
// If we're in an OAuth flow (server-controlled code verifier exists), handle the callback
|
|
4276
|
-
if (isInOAuthFlow) {
|
|
4277
|
-
// We're expecting a callback - validate parameters
|
|
4278
|
-
if (!code) {
|
|
4279
|
-
// Clear stored state on error
|
|
4280
|
-
sessionStorage.removeItem('uipath_sdk_code_verifier');
|
|
4281
|
-
throw new Error('Authorization code missing in OAuth callback');
|
|
4282
|
-
}
|
|
4283
|
-
// Validate the authorization code format before using it
|
|
4284
|
-
// OAuth authorization codes should be alphanumeric with some special characters
|
|
4285
|
-
const codePattern = /^[A-Za-z0-9\-._~+/]+=*$/;
|
|
4286
|
-
if (!codePattern.test(code)) {
|
|
4287
|
-
// Clear stored state on error
|
|
4288
|
-
sessionStorage.removeItem('uipath_sdk_code_verifier');
|
|
4289
|
-
throw new Error('Invalid authorization code format');
|
|
4290
|
-
}
|
|
4291
|
-
// Authorization code is present and validated, so we can exchange it for a token.
|
|
4292
|
-
await this._handleOAuthCallback(code, clientId, redirectUri);
|
|
4293
|
-
return this.hasValidToken();
|
|
4294
|
-
}
|
|
4295
|
-
else {
|
|
4296
|
-
// Not in an OAuth flow - initiate one
|
|
4297
|
-
// Ignore any URL parameters that might be present
|
|
4298
|
-
await this._initiateOAuthFlow(clientId, redirectUri, scope);
|
|
4299
|
-
// This line is not expected to be reached due to redirect
|
|
4300
|
-
return false;
|
|
4301
|
-
}
|
|
4302
|
-
}
|
|
4303
|
-
/**
|
|
4304
|
-
* Authenticate using API secret
|
|
4305
|
-
*/
|
|
4306
|
-
authenticateWithSecret(secret) {
|
|
4307
|
-
try {
|
|
4308
|
-
this.updateToken({ token: secret, type: 'secret' });
|
|
4309
|
-
return true;
|
|
4310
|
-
}
|
|
4311
|
-
catch (error) {
|
|
4312
|
-
console.error('Failed to authenticate with secret', error);
|
|
4313
|
-
return false;
|
|
4314
|
-
}
|
|
4315
|
-
}
|
|
4316
|
-
/**
|
|
4317
|
-
* Updates the access token used for API requests
|
|
4318
|
-
* @param tokenInfo The token information containing the access token, type, expiration, and refresh token
|
|
4319
|
-
*/
|
|
4320
|
-
updateToken(tokenInfo) {
|
|
4321
|
-
this.tokenManager.setToken(tokenInfo);
|
|
4322
|
-
}
|
|
4323
|
-
/**
|
|
4324
|
-
* Checks if the current token is valid
|
|
4325
|
-
*/
|
|
4326
|
-
hasValidToken() {
|
|
4327
|
-
return this.tokenManager.hasValidToken();
|
|
4328
|
-
}
|
|
4329
|
-
/**
|
|
4330
|
-
* Get the current token
|
|
4331
|
-
*/
|
|
4332
|
-
getToken() {
|
|
4333
|
-
if (!this.tokenManager.hasValidToken()) {
|
|
4334
|
-
return undefined;
|
|
4335
|
-
}
|
|
4336
|
-
return this.tokenManager.getToken();
|
|
4337
|
-
}
|
|
4338
|
-
/**
|
|
4339
|
-
* Generates a random code verifier for PKCE
|
|
4340
|
-
*/
|
|
4341
|
-
generateCodeVerifier() {
|
|
4342
|
-
if (isBrowser) {
|
|
4343
|
-
const array = new Uint8Array(32);
|
|
4344
|
-
crypto.getRandomValues(array);
|
|
4345
|
-
return this._base64URLEncode(array);
|
|
4346
|
-
}
|
|
4347
|
-
else {
|
|
4348
|
-
// In Node.js environment
|
|
4349
|
-
try {
|
|
4350
|
-
const crypto = require('crypto');
|
|
4351
|
-
return crypto.randomBytes(32)
|
|
4352
|
-
.toString('base64')
|
|
4353
|
-
.replace(/\+/g, '-')
|
|
4354
|
-
.replace(/\//g, '_')
|
|
4355
|
-
.replace(/=/g, '');
|
|
4356
|
-
}
|
|
4357
|
-
catch {
|
|
4358
|
-
throw new Error("crypto not available in browser");
|
|
4359
|
-
}
|
|
4360
|
-
}
|
|
3977
|
+
/**
|
|
3978
|
+
* Error thrown when authentication fails (401 errors)
|
|
3979
|
+
* Common scenarios:
|
|
3980
|
+
* - Invalid credentials
|
|
3981
|
+
* - Expired token
|
|
3982
|
+
* - Missing authentication
|
|
3983
|
+
*/
|
|
3984
|
+
class AuthenticationError extends UiPathError {
|
|
3985
|
+
constructor(params = {}) {
|
|
3986
|
+
super(ErrorType.AUTHENTICATION, {
|
|
3987
|
+
message: params.message || ErrorMessages.AUTHENTICATION_FAILED,
|
|
3988
|
+
statusCode: params.statusCode ?? HttpStatus.UNAUTHORIZED,
|
|
3989
|
+
requestId: params.requestId
|
|
3990
|
+
});
|
|
4361
3991
|
}
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
.digest('base64')
|
|
4379
|
-
.replace(/\+/g, '-')
|
|
4380
|
-
.replace(/\//g, '_')
|
|
4381
|
-
.replace(/=/g, '');
|
|
4382
|
-
}
|
|
4383
|
-
catch {
|
|
4384
|
-
throw new Error("crypto not available in browser");
|
|
4385
|
-
}
|
|
4386
|
-
}
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3994
|
+
/**
|
|
3995
|
+
* Error thrown when authorization fails (403 errors)
|
|
3996
|
+
* Common scenarios:
|
|
3997
|
+
* - Insufficient permissions
|
|
3998
|
+
* - Access denied to resource
|
|
3999
|
+
* - Invalid scope
|
|
4000
|
+
*/
|
|
4001
|
+
class AuthorizationError extends UiPathError {
|
|
4002
|
+
constructor(params = {}) {
|
|
4003
|
+
super(ErrorType.AUTHORIZATION, {
|
|
4004
|
+
message: params.message || ErrorMessages.ACCESS_DENIED,
|
|
4005
|
+
statusCode: params.statusCode ?? HttpStatus.FORBIDDEN,
|
|
4006
|
+
requestId: params.requestId
|
|
4007
|
+
});
|
|
4387
4008
|
}
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4009
|
+
}
|
|
4010
|
+
|
|
4011
|
+
/**
|
|
4012
|
+
* Error thrown when validation fails (400 errors or client-side validation)
|
|
4013
|
+
* Common scenarios:
|
|
4014
|
+
* - Invalid input parameters
|
|
4015
|
+
* - Missing required fields
|
|
4016
|
+
* - Invalid data format
|
|
4017
|
+
*/
|
|
4018
|
+
class ValidationError extends UiPathError {
|
|
4019
|
+
constructor(params = {}) {
|
|
4020
|
+
super(ErrorType.VALIDATION, {
|
|
4021
|
+
message: params.message || ErrorMessages.VALIDATION_FAILED,
|
|
4022
|
+
statusCode: params.statusCode ?? HttpStatus.BAD_REQUEST,
|
|
4023
|
+
requestId: params.requestId
|
|
4401
4024
|
});
|
|
4402
|
-
return `${this.config.baseUrl}/${orgName}/${IDENTITY_ENDPOINTS.AUTHORIZE}?${queryParams.toString()}`;
|
|
4403
4025
|
}
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4026
|
+
}
|
|
4027
|
+
|
|
4028
|
+
/**
|
|
4029
|
+
* Error thrown when a resource is not found (404 errors)
|
|
4030
|
+
* Common scenarios:
|
|
4031
|
+
* - Resource doesn't exist
|
|
4032
|
+
* - Invalid ID provided
|
|
4033
|
+
* - Resource deleted
|
|
4034
|
+
*/
|
|
4035
|
+
class NotFoundError extends UiPathError {
|
|
4036
|
+
constructor(params = {}) {
|
|
4037
|
+
super(ErrorType.NOT_FOUND, {
|
|
4038
|
+
message: params.message || ErrorMessages.RESOURCE_NOT_FOUND,
|
|
4039
|
+
statusCode: params.statusCode ?? HttpStatus.NOT_FOUND,
|
|
4040
|
+
requestId: params.requestId
|
|
4415
4041
|
});
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4044
|
+
|
|
4045
|
+
/**
|
|
4046
|
+
* Error thrown when rate limit is exceeded (429 errors)
|
|
4047
|
+
* Common scenarios:
|
|
4048
|
+
* - Too many requests in a time window
|
|
4049
|
+
* - API throttling
|
|
4050
|
+
*/
|
|
4051
|
+
class RateLimitError extends UiPathError {
|
|
4052
|
+
constructor(params = {}) {
|
|
4053
|
+
super(ErrorType.RATE_LIMIT, {
|
|
4054
|
+
message: params.message || ErrorMessages.RATE_LIMIT_EXCEEDED,
|
|
4055
|
+
statusCode: params.statusCode ?? HttpStatus.TOO_MANY_REQUESTS,
|
|
4056
|
+
requestId: params.requestId
|
|
4422
4057
|
});
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
|
|
4061
|
+
/**
|
|
4062
|
+
* Error thrown when server encounters an error (5xx errors)
|
|
4063
|
+
* Common scenarios:
|
|
4064
|
+
* - Internal server error
|
|
4065
|
+
* - Service unavailable
|
|
4066
|
+
* - Gateway timeout
|
|
4067
|
+
*/
|
|
4068
|
+
class ServerError extends UiPathError {
|
|
4069
|
+
constructor(params = {}) {
|
|
4070
|
+
super(ErrorType.SERVER, {
|
|
4071
|
+
message: params.message || ErrorMessages.INTERNAL_SERVER_ERROR,
|
|
4072
|
+
statusCode: params.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR,
|
|
4073
|
+
requestId: params.requestId
|
|
4434
4074
|
});
|
|
4435
|
-
return token;
|
|
4436
4075
|
}
|
|
4437
4076
|
/**
|
|
4438
|
-
*
|
|
4077
|
+
* Checks if this is a temporary error that might succeed on retry
|
|
4439
4078
|
*/
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
.
|
|
4444
|
-
.replace(/\//g, '_')
|
|
4445
|
-
.replace(/=/g, '');
|
|
4446
|
-
}
|
|
4447
|
-
async _initiateOAuthFlow(clientId, redirectUri, scope) {
|
|
4448
|
-
const codeVerifier = this.generateCodeVerifier();
|
|
4449
|
-
const codeChallenge = await this.generateCodeChallenge(codeVerifier);
|
|
4450
|
-
// Store complete OAuth context for callback completion
|
|
4451
|
-
const oauthContext = {
|
|
4452
|
-
codeVerifier,
|
|
4453
|
-
clientId,
|
|
4454
|
-
redirectUri,
|
|
4455
|
-
baseUrl: this.config.baseUrl,
|
|
4456
|
-
orgName: this.config.orgName,
|
|
4457
|
-
tenantName: this.config.tenantName,
|
|
4458
|
-
scope
|
|
4459
|
-
};
|
|
4460
|
-
sessionStorage.setItem('uipath_sdk_oauth_context', JSON.stringify(oauthContext));
|
|
4461
|
-
sessionStorage.setItem('uipath_sdk_code_verifier', codeVerifier);
|
|
4462
|
-
const authUrl = this.getAuthorizationUrl({
|
|
4463
|
-
clientId,
|
|
4464
|
-
redirectUri,
|
|
4465
|
-
codeChallenge,
|
|
4466
|
-
scope
|
|
4467
|
-
});
|
|
4468
|
-
window.location.href = authUrl;
|
|
4079
|
+
get isRetryable() {
|
|
4080
|
+
return this.statusCode === HttpStatus.BAD_GATEWAY ||
|
|
4081
|
+
this.statusCode === HttpStatus.SERVICE_UNAVAILABLE ||
|
|
4082
|
+
this.statusCode === HttpStatus.GATEWAY_TIMEOUT;
|
|
4469
4083
|
}
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4084
|
+
}
|
|
4085
|
+
|
|
4086
|
+
/**
|
|
4087
|
+
* Error thrown when network/connection issues occur
|
|
4088
|
+
* Common scenarios:
|
|
4089
|
+
* - Connection timeout
|
|
4090
|
+
* - DNS resolution failure
|
|
4091
|
+
* - Network unreachable
|
|
4092
|
+
* - Request aborted
|
|
4093
|
+
*/
|
|
4094
|
+
class NetworkError extends UiPathError {
|
|
4095
|
+
constructor(params = {}) {
|
|
4096
|
+
super(ErrorType.NETWORK, {
|
|
4097
|
+
message: params.message || ErrorMessages.NETWORK_ERROR,
|
|
4098
|
+
statusCode: params.statusCode, // Network errors typically don't have HTTP status codes
|
|
4099
|
+
requestId: params.requestId
|
|
4480
4100
|
});
|
|
4481
|
-
// Clear OAuth context and code verifier after successful token exchange
|
|
4482
|
-
sessionStorage.removeItem('uipath_sdk_oauth_context');
|
|
4483
|
-
sessionStorage.removeItem('uipath_sdk_code_verifier');
|
|
4484
|
-
const url = new URL(window.location.href);
|
|
4485
|
-
url.searchParams.delete('code');
|
|
4486
|
-
url.searchParams.delete('state');
|
|
4487
|
-
window.history.replaceState({}, '', url.toString());
|
|
4488
4101
|
}
|
|
4489
4102
|
}
|
|
4490
4103
|
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4104
|
+
/**
|
|
4105
|
+
* Type guard to check if an error is a UiPathError
|
|
4106
|
+
*/
|
|
4107
|
+
function isUiPathError(error) {
|
|
4108
|
+
return error instanceof UiPathError;
|
|
4109
|
+
}
|
|
4110
|
+
/**
|
|
4111
|
+
* Type guard to check if an error is an AuthenticationError
|
|
4112
|
+
*/
|
|
4113
|
+
function isAuthenticationError(error) {
|
|
4114
|
+
return error instanceof AuthenticationError;
|
|
4115
|
+
}
|
|
4116
|
+
/**
|
|
4117
|
+
* Type guard to check if an error is an AuthorizationError
|
|
4118
|
+
*/
|
|
4119
|
+
function isAuthorizationError(error) {
|
|
4120
|
+
return error instanceof AuthorizationError;
|
|
4121
|
+
}
|
|
4122
|
+
/**
|
|
4123
|
+
* Type guard to check if an error is a ValidationError
|
|
4124
|
+
*/
|
|
4125
|
+
function isValidationError(error) {
|
|
4126
|
+
return error instanceof ValidationError;
|
|
4127
|
+
}
|
|
4128
|
+
/**
|
|
4129
|
+
* Type guard to check if an error is a NotFoundError
|
|
4130
|
+
*/
|
|
4131
|
+
function isNotFoundError(error) {
|
|
4132
|
+
return error instanceof NotFoundError;
|
|
4133
|
+
}
|
|
4134
|
+
/**
|
|
4135
|
+
* Type guard to check if an error is a RateLimitError
|
|
4136
|
+
*/
|
|
4137
|
+
function isRateLimitError(error) {
|
|
4138
|
+
return error instanceof RateLimitError;
|
|
4498
4139
|
}
|
|
4499
|
-
|
|
4500
|
-
|
|
4140
|
+
/**
|
|
4141
|
+
* Type guard to check if an error is a ServerError
|
|
4142
|
+
*/
|
|
4143
|
+
function isServerError(error) {
|
|
4144
|
+
return error instanceof ServerError;
|
|
4501
4145
|
}
|
|
4502
|
-
|
|
4503
4146
|
/**
|
|
4504
|
-
*
|
|
4147
|
+
* Type guard to check if an error is a NetworkError
|
|
4505
4148
|
*/
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4149
|
+
function isNetworkError(error) {
|
|
4150
|
+
return error instanceof NetworkError;
|
|
4151
|
+
}
|
|
4152
|
+
/**
|
|
4153
|
+
* Helper to get error details in a safe way
|
|
4154
|
+
*/
|
|
4155
|
+
function getErrorDetails(error) {
|
|
4156
|
+
if (isUiPathError(error)) {
|
|
4157
|
+
return {
|
|
4158
|
+
message: error.message,
|
|
4159
|
+
statusCode: error.statusCode
|
|
4160
|
+
};
|
|
4161
|
+
}
|
|
4162
|
+
if (error instanceof Error) {
|
|
4163
|
+
return {
|
|
4164
|
+
message: error.message
|
|
4165
|
+
};
|
|
4166
|
+
}
|
|
4167
|
+
return {
|
|
4168
|
+
message: String(error)
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
4526
4171
|
|
|
4527
4172
|
/**
|
|
4528
|
-
*
|
|
4173
|
+
* TokenManager is responsible for managing authentication tokens.
|
|
4174
|
+
* It provides token operations for a specific client ID.
|
|
4175
|
+
* - For OAuth tokens: Uses session storage with client ID-based keys
|
|
4176
|
+
* - For Secret tokens: Stores only in memory, allowing multiple instances
|
|
4529
4177
|
*/
|
|
4530
|
-
class
|
|
4531
|
-
|
|
4532
|
-
|
|
4178
|
+
class TokenManager {
|
|
4179
|
+
/**
|
|
4180
|
+
* Creates a new TokenManager instance
|
|
4181
|
+
* @param executionContext The execution context
|
|
4182
|
+
* @param config The SDK configuration
|
|
4183
|
+
* @param isOAuth Whether this is an OAuth-based authentication
|
|
4184
|
+
*/
|
|
4185
|
+
constructor(executionContext, config, isOAuth = false) {
|
|
4186
|
+
this.executionContext = executionContext;
|
|
4187
|
+
this.config = config;
|
|
4188
|
+
this.isOAuth = isOAuth;
|
|
4189
|
+
this.STORAGE_KEY_PREFIX = 'uipath_sdk_user_token-';
|
|
4190
|
+
this.refreshPromise = null;
|
|
4533
4191
|
}
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4192
|
+
/**
|
|
4193
|
+
* Checks if a token is expired
|
|
4194
|
+
* @param tokenInfo The token info to check
|
|
4195
|
+
* @returns true if the token is expired, false otherwise
|
|
4196
|
+
*/
|
|
4197
|
+
isTokenExpired(tokenInfo) {
|
|
4198
|
+
// If no token info or no expiration date, token is not expired
|
|
4199
|
+
if (!tokenInfo?.expiresAt) {
|
|
4200
|
+
return false;
|
|
4201
|
+
}
|
|
4202
|
+
return new Date() >= tokenInfo.expiresAt;
|
|
4203
|
+
}
|
|
4204
|
+
/**
|
|
4205
|
+
* Gets a valid authentication token, refreshing if necessary.
|
|
4206
|
+
* This is the single source of truth for token validation and refresh logic.
|
|
4207
|
+
*
|
|
4208
|
+
* @returns The valid token string
|
|
4209
|
+
* @throws AuthenticationError if no token available or refresh fails
|
|
4210
|
+
*/
|
|
4211
|
+
async getValidToken() {
|
|
4212
|
+
const tokenInfo = this.executionContext.get('tokenInfo');
|
|
4213
|
+
if (!tokenInfo) {
|
|
4214
|
+
throw new AuthenticationError({
|
|
4215
|
+
message: 'No authentication token available. Make sure to initialize the SDK first.'
|
|
4538
4216
|
});
|
|
4539
|
-
|
|
4217
|
+
}
|
|
4218
|
+
// For secret-based tokens, they never expire
|
|
4219
|
+
if (tokenInfo.type === 'secret') {
|
|
4220
|
+
return tokenInfo.token;
|
|
4221
|
+
}
|
|
4222
|
+
// If token is not expired, return it
|
|
4223
|
+
if (!this.isTokenExpired(tokenInfo)) {
|
|
4224
|
+
return tokenInfo.token;
|
|
4225
|
+
}
|
|
4226
|
+
// Token is expired, refresh it
|
|
4227
|
+
try {
|
|
4228
|
+
const newToken = await this.refreshAccessToken();
|
|
4229
|
+
return newToken.access_token;
|
|
4540
4230
|
}
|
|
4541
4231
|
catch (error) {
|
|
4542
|
-
|
|
4543
|
-
|
|
4232
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
4233
|
+
throw new AuthenticationError({
|
|
4234
|
+
message: `Token refresh failed: ${message}. Please re-authenticate.`,
|
|
4235
|
+
statusCode: HttpStatus.UNAUTHORIZED
|
|
4236
|
+
});
|
|
4544
4237
|
}
|
|
4545
4238
|
}
|
|
4546
|
-
|
|
4547
|
-
|
|
4239
|
+
/**
|
|
4240
|
+
* Gets the storage key for this TokenManager instance
|
|
4241
|
+
*/
|
|
4242
|
+
_getStorageKey() {
|
|
4243
|
+
return `${this.STORAGE_KEY_PREFIX}${this.config.clientId}`;
|
|
4548
4244
|
}
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
}
|
|
4563
|
-
},
|
|
4564
|
-
tags: {
|
|
4565
|
-
'ai.cloud.role': CLOUD_ROLE_NAME,
|
|
4566
|
-
'ai.cloud.roleInstance': SDK_VERSION
|
|
4245
|
+
/**
|
|
4246
|
+
* Loads token from session storage if available
|
|
4247
|
+
* @returns true if a valid token was loaded, false otherwise
|
|
4248
|
+
*/
|
|
4249
|
+
loadFromStorage() {
|
|
4250
|
+
// Only OAuth tokens are stored in session storage
|
|
4251
|
+
if (!isBrowser || !this.isOAuth) {
|
|
4252
|
+
return false;
|
|
4253
|
+
}
|
|
4254
|
+
try {
|
|
4255
|
+
const storedToken = sessionStorage.getItem(this._getStorageKey());
|
|
4256
|
+
if (!storedToken) {
|
|
4257
|
+
return false;
|
|
4567
4258
|
}
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4259
|
+
const tokenInfo = this._parseTokenInfo(storedToken);
|
|
4260
|
+
if (!tokenInfo) {
|
|
4261
|
+
// Invalid token format, clear it
|
|
4262
|
+
sessionStorage.removeItem(this._getStorageKey());
|
|
4263
|
+
return false;
|
|
4264
|
+
}
|
|
4265
|
+
// Check if token is expired
|
|
4266
|
+
if (this.isTokenExpired(tokenInfo)) {
|
|
4267
|
+
// Token expired, clear it
|
|
4268
|
+
sessionStorage.removeItem(this._getStorageKey());
|
|
4269
|
+
return false;
|
|
4270
|
+
}
|
|
4271
|
+
// Valid token found, use it
|
|
4272
|
+
this.currentToken = tokenInfo;
|
|
4273
|
+
this._updateExecutionContext(tokenInfo);
|
|
4274
|
+
return true;
|
|
4275
|
+
}
|
|
4276
|
+
catch (error) {
|
|
4277
|
+
console.warn('Failed to load token from session storage', error);
|
|
4278
|
+
return false;
|
|
4279
|
+
}
|
|
4581
4280
|
}
|
|
4582
|
-
|
|
4281
|
+
/**
|
|
4282
|
+
* Parse and validate token info from storage
|
|
4283
|
+
* @param storedToken JSON string from storage
|
|
4284
|
+
* @returns Valid TokenInfo or undefined if invalid
|
|
4285
|
+
*/
|
|
4286
|
+
_parseTokenInfo(storedToken) {
|
|
4583
4287
|
try {
|
|
4584
|
-
const
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
return;
|
|
4288
|
+
const parsed = JSON.parse(storedToken);
|
|
4289
|
+
// Basic validation
|
|
4290
|
+
if (typeof parsed !== 'object' || !parsed) {
|
|
4291
|
+
return undefined;
|
|
4588
4292
|
}
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
if (
|
|
4598
|
-
|
|
4293
|
+
if (typeof parsed.token !== 'string' || !parsed.token) {
|
|
4294
|
+
return undefined;
|
|
4295
|
+
}
|
|
4296
|
+
if (parsed.type !== 'secret' && parsed.type !== 'oauth') {
|
|
4297
|
+
return undefined;
|
|
4298
|
+
}
|
|
4299
|
+
const tokenInfo = parsed;
|
|
4300
|
+
// Convert string date back to Date object
|
|
4301
|
+
if (tokenInfo.expiresAt) {
|
|
4302
|
+
tokenInfo.expiresAt = new Date(tokenInfo.expiresAt);
|
|
4303
|
+
// Verify it's a valid date
|
|
4304
|
+
if (isNaN(tokenInfo.expiresAt.getTime())) {
|
|
4305
|
+
return undefined;
|
|
4306
|
+
}
|
|
4599
4307
|
}
|
|
4308
|
+
return tokenInfo;
|
|
4600
4309
|
}
|
|
4601
4310
|
catch (error) {
|
|
4602
|
-
console.
|
|
4311
|
+
console.warn('Failed to parse token info', error);
|
|
4312
|
+
return undefined;
|
|
4603
4313
|
}
|
|
4604
4314
|
}
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4315
|
+
/**
|
|
4316
|
+
* Sets a new token and updates all necessary contexts
|
|
4317
|
+
*/
|
|
4318
|
+
setToken(tokenInfo) {
|
|
4319
|
+
this.currentToken = tokenInfo;
|
|
4320
|
+
// Store token in execution context
|
|
4321
|
+
this._updateExecutionContext(tokenInfo);
|
|
4322
|
+
// Store in session storage if in browser and this is an OAuth token
|
|
4323
|
+
if (isBrowser && this.isOAuth) {
|
|
4324
|
+
try {
|
|
4325
|
+
sessionStorage.setItem(this._getStorageKey(), JSON.stringify(tokenInfo));
|
|
4326
|
+
}
|
|
4327
|
+
catch (error) {
|
|
4328
|
+
console.warn('Failed to store token in session storage', error);
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4608
4331
|
}
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
constructor() {
|
|
4615
|
-
this.isInitialized = false;
|
|
4332
|
+
/**
|
|
4333
|
+
* Gets the current token information
|
|
4334
|
+
*/
|
|
4335
|
+
getTokenInfo() {
|
|
4336
|
+
return this.currentToken;
|
|
4616
4337
|
}
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
return
|
|
4338
|
+
/**
|
|
4339
|
+
* Gets just the token string
|
|
4340
|
+
*/
|
|
4341
|
+
getToken() {
|
|
4342
|
+
return this.currentToken?.token;
|
|
4622
4343
|
}
|
|
4623
4344
|
/**
|
|
4624
|
-
*
|
|
4345
|
+
* Checks if we have a valid token
|
|
4625
4346
|
*/
|
|
4626
|
-
|
|
4627
|
-
if (this.
|
|
4628
|
-
return;
|
|
4347
|
+
hasValidToken() {
|
|
4348
|
+
if (!this.currentToken) {
|
|
4349
|
+
return false;
|
|
4629
4350
|
}
|
|
4630
|
-
this.
|
|
4631
|
-
|
|
4632
|
-
this.telemetryContext = config;
|
|
4351
|
+
if (this.isTokenExpired(this.currentToken)) {
|
|
4352
|
+
return false;
|
|
4633
4353
|
}
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4354
|
+
return true;
|
|
4355
|
+
}
|
|
4356
|
+
/**
|
|
4357
|
+
* Clears the current token
|
|
4358
|
+
*/
|
|
4359
|
+
clearToken() {
|
|
4360
|
+
this.currentToken = undefined;
|
|
4361
|
+
this.executionContext.set('tokenInfo', undefined);
|
|
4362
|
+
const headers = this.executionContext.getHeaders();
|
|
4363
|
+
delete headers['Authorization'];
|
|
4364
|
+
this.executionContext.setHeaders(headers);
|
|
4365
|
+
// Remove from session storage if this is an OAuth token
|
|
4366
|
+
if (isBrowser && this.isOAuth) {
|
|
4367
|
+
try {
|
|
4368
|
+
sessionStorage.removeItem(this._getStorageKey());
|
|
4369
|
+
}
|
|
4370
|
+
catch (error) {
|
|
4371
|
+
console.warn('Failed to remove token from session storage', error);
|
|
4638
4372
|
}
|
|
4639
|
-
this.setupTelemetryProvider(connectionString);
|
|
4640
|
-
}
|
|
4641
|
-
catch (error) {
|
|
4642
|
-
// Silent failure - telemetry errors shouldn't break functionality
|
|
4643
|
-
console.debug('Failed to initialize OpenTelemetry:', error);
|
|
4644
4373
|
}
|
|
4645
4374
|
}
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
processors: [processor]
|
|
4375
|
+
/**
|
|
4376
|
+
* Updates execution context with token information
|
|
4377
|
+
*/
|
|
4378
|
+
_updateExecutionContext(tokenInfo) {
|
|
4379
|
+
this.executionContext.set('tokenInfo', tokenInfo);
|
|
4380
|
+
// Update authorization header
|
|
4381
|
+
this.executionContext.setHeaders({
|
|
4382
|
+
'Authorization': `Bearer ${tokenInfo.token}`
|
|
4655
4383
|
});
|
|
4656
|
-
this.logger = this.logProvider.getLogger(SDK_LOGGER_NAME);
|
|
4657
4384
|
}
|
|
4658
4385
|
/**
|
|
4659
|
-
*
|
|
4386
|
+
* Refreshes the access token using the stored refresh token.
|
|
4387
|
+
* This method only works for OAuth flow.
|
|
4388
|
+
* Uses a lock mechanism to prevent multiple simultaneous refreshes.
|
|
4389
|
+
* @returns A promise that resolves to the new AuthToken
|
|
4390
|
+
* @throws Error if not in OAuth flow, refresh token is missing, or the request fails
|
|
4660
4391
|
*/
|
|
4661
|
-
|
|
4392
|
+
async refreshAccessToken() {
|
|
4393
|
+
// If there's already a refresh in progress, return that promise
|
|
4394
|
+
if (this.refreshPromise) {
|
|
4395
|
+
return this.refreshPromise;
|
|
4396
|
+
}
|
|
4662
4397
|
try {
|
|
4663
|
-
//
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
const attributes = this.getEnrichedAttributes(extraAttributes, eventName);
|
|
4669
|
-
// Emit as log
|
|
4670
|
-
this.logger.emit({
|
|
4671
|
-
body: finalDisplayName,
|
|
4672
|
-
attributes: attributes,
|
|
4673
|
-
timestamp: Date.now(),
|
|
4674
|
-
});
|
|
4398
|
+
// Create new refresh promise
|
|
4399
|
+
this.refreshPromise = this._doRefreshToken();
|
|
4400
|
+
// Wait for refresh to complete
|
|
4401
|
+
const result = await this.refreshPromise;
|
|
4402
|
+
return result;
|
|
4675
4403
|
}
|
|
4676
|
-
|
|
4677
|
-
//
|
|
4678
|
-
|
|
4404
|
+
finally {
|
|
4405
|
+
// Clear the refresh promise when done (success or failure)
|
|
4406
|
+
this.refreshPromise = null;
|
|
4679
4407
|
}
|
|
4680
4408
|
}
|
|
4681
4409
|
/**
|
|
4682
|
-
*
|
|
4683
|
-
*/
|
|
4684
|
-
getEnrichedAttributes(extraAttributes, eventName) {
|
|
4685
|
-
const attributes = {
|
|
4686
|
-
...extraAttributes,
|
|
4687
|
-
[APP_NAME]: SDK_SERVICE_NAME,
|
|
4688
|
-
[VERSION]: SDK_VERSION,
|
|
4689
|
-
[SERVICE]: eventName,
|
|
4690
|
-
[CLOUD_URL]: this.createCloudUrl(),
|
|
4691
|
-
[CLOUD_ORGANIZATION_NAME]: this.telemetryContext?.orgName || UNKNOWN,
|
|
4692
|
-
[CLOUD_TENANT_NAME]: this.telemetryContext?.tenantName || UNKNOWN,
|
|
4693
|
-
[CLOUD_REDIRECT_URI]: this.telemetryContext?.redirectUri || UNKNOWN,
|
|
4694
|
-
[CLOUD_CLIENT_ID]: this.telemetryContext?.clientId || UNKNOWN,
|
|
4695
|
-
};
|
|
4696
|
-
return attributes;
|
|
4697
|
-
}
|
|
4698
|
-
/**
|
|
4699
|
-
* Create cloud URL from base URL, organization ID, and tenant ID
|
|
4410
|
+
* Internal method to perform the actual token refresh
|
|
4700
4411
|
*/
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
if (!baseUrl || !orgId || !tenantId) {
|
|
4706
|
-
return UNKNOWN;
|
|
4412
|
+
async _doRefreshToken() {
|
|
4413
|
+
// Check if we're in OAuth flow
|
|
4414
|
+
if (!hasOAuthConfig(this.config)) {
|
|
4415
|
+
throw new Error('refreshAccessToken is only available in OAuth flow');
|
|
4707
4416
|
}
|
|
4708
|
-
|
|
4417
|
+
// Get current token info from token manager
|
|
4418
|
+
const tokenInfo = this.getTokenInfo();
|
|
4419
|
+
if (!tokenInfo?.refreshToken) {
|
|
4420
|
+
throw new Error('No refresh token available. User may need to re-authenticate.');
|
|
4421
|
+
}
|
|
4422
|
+
const orgName = this.config.orgName;
|
|
4423
|
+
const body = new URLSearchParams({
|
|
4424
|
+
grant_type: 'refresh_token',
|
|
4425
|
+
client_id: this.config.clientId,
|
|
4426
|
+
refresh_token: tokenInfo.refreshToken
|
|
4427
|
+
});
|
|
4428
|
+
const response = await fetch(`${this.config.baseUrl}/${orgName}/identity_/connect/token`, {
|
|
4429
|
+
method: 'POST',
|
|
4430
|
+
headers: {
|
|
4431
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
4432
|
+
},
|
|
4433
|
+
body: body.toString()
|
|
4434
|
+
});
|
|
4435
|
+
if (!response.ok) {
|
|
4436
|
+
const errorData = await response.json().catch(() => ({ message: response.statusText }));
|
|
4437
|
+
console.error("Token refresh error:", errorData);
|
|
4438
|
+
// Clear the invalid token to prevent further failed requests
|
|
4439
|
+
this.clearToken();
|
|
4440
|
+
throw new Error(`Failed to refresh access token: ${JSON.stringify(errorData)}`);
|
|
4441
|
+
}
|
|
4442
|
+
const token = await response.json();
|
|
4443
|
+
this.setToken({
|
|
4444
|
+
token: token.access_token,
|
|
4445
|
+
type: 'oauth',
|
|
4446
|
+
expiresAt: new Date(Date.now() + token.expires_in * 1000),
|
|
4447
|
+
refreshToken: token.refresh_token
|
|
4448
|
+
});
|
|
4449
|
+
return token;
|
|
4709
4450
|
}
|
|
4710
4451
|
}
|
|
4711
|
-
// Export singleton instance
|
|
4712
|
-
const telemetryClient = TelemetryClient.getInstance();
|
|
4713
4452
|
|
|
4714
4453
|
/**
|
|
4715
|
-
*
|
|
4716
|
-
*/
|
|
4717
|
-
/**
|
|
4718
|
-
* Direct tracking function
|
|
4454
|
+
* Base path constants for different services
|
|
4719
4455
|
*/
|
|
4720
|
-
|
|
4721
|
-
telemetryClient.track(eventName, name, attributes);
|
|
4722
|
-
}
|
|
4456
|
+
const IDENTITY_BASE = 'identity_';
|
|
4723
4457
|
|
|
4724
4458
|
/**
|
|
4725
|
-
*
|
|
4726
|
-
*
|
|
4727
|
-
* This class is NOT exported in the public API.
|
|
4728
|
-
* It provides a secure way to share SDK internals between
|
|
4729
|
-
* the UiPath class and service classes without exposing them publicly.
|
|
4730
|
-
*
|
|
4731
|
-
* @internal
|
|
4459
|
+
* Identity/Authentication Endpoints
|
|
4732
4460
|
*/
|
|
4733
|
-
// Global symbol key to ensure WeakMap is shared across module instances
|
|
4734
|
-
// This prevents issues when core and service modules are bundled separately
|
|
4735
|
-
const REGISTRY_KEY = Symbol.for('@uipath/sdk-internals-registry');
|
|
4736
|
-
// Get or create the global WeakMap store
|
|
4737
|
-
const getGlobalStore = () => {
|
|
4738
|
-
const globalObj = globalThis;
|
|
4739
|
-
if (!globalObj[REGISTRY_KEY]) {
|
|
4740
|
-
globalObj[REGISTRY_KEY] = new WeakMap();
|
|
4741
|
-
}
|
|
4742
|
-
return globalObj[REGISTRY_KEY];
|
|
4743
|
-
};
|
|
4744
4461
|
/**
|
|
4745
|
-
*
|
|
4746
|
-
* Uses WeakMap to prevent memory leaks - entries are automatically
|
|
4747
|
-
* garbage collected when the SDK instance is no longer referenced.
|
|
4748
|
-
*
|
|
4749
|
-
* Uses a global singleton pattern to ensure the same WeakMap is shared
|
|
4750
|
-
* across separately bundled modules (core, entities, tasks, etc.).
|
|
4751
|
-
*
|
|
4752
|
-
* @internal - Not exported in public API
|
|
4462
|
+
* Identity Service Endpoints
|
|
4753
4463
|
*/
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4464
|
+
const IDENTITY_ENDPOINTS = {
|
|
4465
|
+
TOKEN: `${IDENTITY_BASE}/connect/token`,
|
|
4466
|
+
AUTHORIZE: `${IDENTITY_BASE}/connect/authorize`,
|
|
4467
|
+
};
|
|
4468
|
+
|
|
4469
|
+
class AuthService {
|
|
4470
|
+
constructor(config, executionContext) {
|
|
4471
|
+
// Check if we should use stored OAuth context instead of provided config
|
|
4472
|
+
const storedContext = AuthService.getStoredOAuthContext();
|
|
4473
|
+
const effectiveConfig = storedContext ? AuthService._mergeConfigWithContext(config, storedContext) : config;
|
|
4474
|
+
this.config = effectiveConfig;
|
|
4475
|
+
const isOAuth = hasOAuthConfig(effectiveConfig);
|
|
4476
|
+
this.tokenManager = new TokenManager(executionContext, effectiveConfig, isOAuth);
|
|
4477
|
+
// Auto-load token from storage on initialization
|
|
4478
|
+
// This ensures isAuthenticated() returns true after page refresh if a valid token exists
|
|
4479
|
+
this.tokenManager.loadFromStorage();
|
|
4480
|
+
}
|
|
4481
|
+
/**
|
|
4482
|
+
* Check if we're in an OAuth callback state
|
|
4483
|
+
*/
|
|
4484
|
+
static isInOAuthCallback() {
|
|
4485
|
+
if (!isBrowser)
|
|
4486
|
+
return false;
|
|
4487
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
4488
|
+
const code = urlParams.get('code');
|
|
4489
|
+
const hasCodeVerifier = sessionStorage.getItem('uipath_sdk_code_verifier');
|
|
4490
|
+
return !!(code && hasCodeVerifier);
|
|
4491
|
+
}
|
|
4492
|
+
/**
|
|
4493
|
+
* Get stored OAuth context
|
|
4494
|
+
*/
|
|
4495
|
+
static getStoredOAuthContext() {
|
|
4496
|
+
if (!isBrowser) {
|
|
4497
|
+
return null;
|
|
4498
|
+
}
|
|
4499
|
+
try {
|
|
4500
|
+
const stored = sessionStorage.getItem('uipath_sdk_oauth_context');
|
|
4501
|
+
if (!stored) {
|
|
4502
|
+
return null;
|
|
4503
|
+
}
|
|
4504
|
+
const context = JSON.parse(stored);
|
|
4505
|
+
// Validate required fields
|
|
4506
|
+
if (!context.codeVerifier || !context.clientId || !context.redirectUri ||
|
|
4507
|
+
!context.baseUrl || !context.orgName) {
|
|
4508
|
+
sessionStorage.removeItem('uipath_sdk_oauth_context');
|
|
4509
|
+
return null;
|
|
4510
|
+
}
|
|
4511
|
+
return context;
|
|
4512
|
+
}
|
|
4513
|
+
catch (error) {
|
|
4514
|
+
sessionStorage.removeItem('uipath_sdk_oauth_context');
|
|
4515
|
+
console.warn('Failed to parse stored OAuth context from session storage', error);
|
|
4516
|
+
return null;
|
|
4517
|
+
}
|
|
4518
|
+
}
|
|
4519
|
+
/**
|
|
4520
|
+
* Merges provided config with stored OAuth context, prioritizing stored values
|
|
4521
|
+
*/
|
|
4522
|
+
static _mergeConfigWithContext(config, context) {
|
|
4523
|
+
return {
|
|
4524
|
+
...config,
|
|
4525
|
+
baseUrl: context.baseUrl,
|
|
4526
|
+
orgName: context.orgName,
|
|
4527
|
+
tenantName: context.tenantName,
|
|
4528
|
+
clientId: context.clientId,
|
|
4529
|
+
redirectUri: context.redirectUri,
|
|
4530
|
+
scope: context.scope
|
|
4531
|
+
};
|
|
4758
4532
|
}
|
|
4759
4533
|
/**
|
|
4760
|
-
*
|
|
4761
|
-
* Called by UiPath constructor
|
|
4534
|
+
* Get the token manager instance
|
|
4762
4535
|
*/
|
|
4763
|
-
|
|
4764
|
-
this.
|
|
4536
|
+
getTokenManager() {
|
|
4537
|
+
return this.tokenManager;
|
|
4765
4538
|
}
|
|
4766
4539
|
/**
|
|
4767
|
-
*
|
|
4768
|
-
*
|
|
4540
|
+
* Authenticates the user based on the provided SDK configuration.
|
|
4541
|
+
* This method handles OAuth 2.0 authentication flow only.
|
|
4542
|
+
* For secret-based authentication, see authenticateWithSecret().
|
|
4543
|
+
* @param config The SDK configuration object.
|
|
4544
|
+
* @returns A promise that resolves to true if authentication is successful, otherwise false.
|
|
4545
|
+
* In an OAuth flow, this method will trigger a page redirect and the promise will not resolve.
|
|
4769
4546
|
*/
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
throw new Error('Invalid SDK instance. Make sure to pass a valid UiPath instance to the service constructor.');
|
|
4547
|
+
async authenticate(config) {
|
|
4548
|
+
if (!isBrowser) {
|
|
4549
|
+
return false;
|
|
4774
4550
|
}
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
*
|
|
4803
|
-
* const entitiesService = new Entities(sdk);
|
|
4804
|
-
* const allEntities = await entitiesService.getAll();
|
|
4805
|
-
* ```
|
|
4806
|
-
*/
|
|
4807
|
-
class UiPath {
|
|
4808
|
-
constructor(config) {
|
|
4809
|
-
// Private fields - true runtime privacy, not visible via Object.keys()
|
|
4810
|
-
_UiPath_config.set(this, void 0);
|
|
4811
|
-
_UiPath_authService.set(this, void 0);
|
|
4812
|
-
_UiPath_initialized.set(this, false);
|
|
4813
|
-
// Validate and normalize the configuration
|
|
4814
|
-
validateConfig(config);
|
|
4815
|
-
const hasSecretAuth = hasSecretConfig(config);
|
|
4816
|
-
const hasOAuthAuth = hasOAuthConfig(config);
|
|
4817
|
-
// Initialize core components
|
|
4818
|
-
const internalConfig = new UiPathConfig({
|
|
4819
|
-
baseUrl: normalizeBaseUrl(config.baseUrl),
|
|
4820
|
-
orgName: config.orgName,
|
|
4821
|
-
tenantName: config.tenantName,
|
|
4822
|
-
secret: hasSecretAuth ? config.secret : undefined,
|
|
4823
|
-
clientId: hasOAuthAuth ? config.clientId : undefined,
|
|
4824
|
-
redirectUri: hasOAuthAuth ? config.redirectUri : undefined,
|
|
4825
|
-
scope: hasOAuthAuth ? config.scope : undefined
|
|
4826
|
-
});
|
|
4827
|
-
const executionContext = new ExecutionContext();
|
|
4828
|
-
__classPrivateFieldSet(this, _UiPath_authService, new AuthService(internalConfig, executionContext), "f");
|
|
4829
|
-
__classPrivateFieldSet(this, _UiPath_config, internalConfig, "f");
|
|
4830
|
-
// Store internals in SDKInternalsRegistry (not visible on instance)
|
|
4831
|
-
SDKInternalsRegistry.set(this, {
|
|
4832
|
-
config: internalConfig,
|
|
4833
|
-
context: executionContext,
|
|
4834
|
-
tokenManager: __classPrivateFieldGet(this, _UiPath_authService, "f").getTokenManager()
|
|
4835
|
-
});
|
|
4836
|
-
// Expose read-only config for user convenience
|
|
4837
|
-
this.config = {
|
|
4838
|
-
baseUrl: internalConfig.baseUrl,
|
|
4839
|
-
orgName: internalConfig.orgName,
|
|
4840
|
-
tenantName: internalConfig.tenantName
|
|
4841
|
-
};
|
|
4842
|
-
// Initialize telemetry with SDK configuration
|
|
4843
|
-
telemetryClient.initialize({
|
|
4844
|
-
baseUrl: config.baseUrl,
|
|
4845
|
-
orgName: config.orgName,
|
|
4846
|
-
tenantName: config.tenantName,
|
|
4847
|
-
clientId: hasOAuthAuth ? config.clientId : undefined,
|
|
4848
|
-
redirectUri: hasOAuthAuth ? config.redirectUri : undefined
|
|
4849
|
-
});
|
|
4850
|
-
// Track SDK initialization
|
|
4851
|
-
trackEvent('Sdk.Auth');
|
|
4852
|
-
// Auto-initialize for secret-based auth
|
|
4853
|
-
if (hasSecretAuth) {
|
|
4854
|
-
__classPrivateFieldGet(this, _UiPath_authService, "f").authenticateWithSecret(config.secret);
|
|
4855
|
-
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
4551
|
+
// First priority: Complete OAuth callback if we detect it
|
|
4552
|
+
if (AuthService.isInOAuthCallback()) {
|
|
4553
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
4554
|
+
const code = urlParams.get('code');
|
|
4555
|
+
if (!code) {
|
|
4556
|
+
throw new Error('Authorization code missing in OAuth callback');
|
|
4557
|
+
}
|
|
4558
|
+
// Check if token already exists (prevents duplicate processing)
|
|
4559
|
+
if (this.tokenManager.hasValidToken()) {
|
|
4560
|
+
return true;
|
|
4561
|
+
}
|
|
4562
|
+
// Ensure we have OAuth config for callback completion
|
|
4563
|
+
if (!hasOAuthConfig(config)) {
|
|
4564
|
+
throw new Error('OAuth configuration incomplete: clientId, redirectUri, and scope are required for OAuth callback');
|
|
4565
|
+
}
|
|
4566
|
+
const result = await this._authenticateWithOAuth(config.clientId, config.redirectUri, config.scope);
|
|
4567
|
+
return result;
|
|
4568
|
+
}
|
|
4569
|
+
// Secondly: Try to load existing valid token from storage
|
|
4570
|
+
const loadedFromStorage = this.tokenManager.loadFromStorage();
|
|
4571
|
+
// If we have a valid token from storage, return true
|
|
4572
|
+
if (loadedFromStorage && this.tokenManager.hasValidToken()) {
|
|
4573
|
+
return true;
|
|
4574
|
+
}
|
|
4575
|
+
// Start new OAuth flow if config has OAuth fields
|
|
4576
|
+
if (hasOAuthConfig(config)) {
|
|
4577
|
+
return await this._authenticateWithOAuth(config.clientId, config.redirectUri, config.scope);
|
|
4856
4578
|
}
|
|
4579
|
+
return false;
|
|
4857
4580
|
}
|
|
4858
4581
|
/**
|
|
4859
|
-
*
|
|
4860
|
-
* This method handles both OAuth flow initiation and completion automatically.
|
|
4861
|
-
* For secret-based authentication, initialization is automatic and this returns immediately.
|
|
4582
|
+
* Authenticate using OAuth flow
|
|
4862
4583
|
*/
|
|
4863
|
-
async
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
return;
|
|
4584
|
+
async _authenticateWithOAuth(clientId, redirectUri, scope) {
|
|
4585
|
+
if (!isBrowser) {
|
|
4586
|
+
throw new Error('OAuth flow is only supported in browser environments');
|
|
4867
4587
|
}
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
//
|
|
4876
|
-
if (
|
|
4877
|
-
|
|
4878
|
-
|
|
4588
|
+
// Check if we have a stored code verifier indicating we're in an OAuth flow
|
|
4589
|
+
const codeVerifier = sessionStorage.getItem('uipath_sdk_code_verifier');
|
|
4590
|
+
const isInOAuthFlow = codeVerifier !== null;
|
|
4591
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
4592
|
+
const code = urlParams.get('code');
|
|
4593
|
+
// If we're in an OAuth flow (server-controlled code verifier exists), handle the callback
|
|
4594
|
+
if (isInOAuthFlow) {
|
|
4595
|
+
// We're expecting a callback - validate parameters
|
|
4596
|
+
if (!code) {
|
|
4597
|
+
// Clear stored state on error
|
|
4598
|
+
sessionStorage.removeItem('uipath_sdk_code_verifier');
|
|
4599
|
+
throw new Error('Authorization code missing in OAuth callback');
|
|
4879
4600
|
}
|
|
4880
|
-
//
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4601
|
+
// Validate the authorization code format before using it
|
|
4602
|
+
// OAuth authorization codes should be alphanumeric with some special characters
|
|
4603
|
+
const codePattern = /^[A-Za-z0-9\-._~+/]+=*$/;
|
|
4604
|
+
if (!codePattern.test(code)) {
|
|
4605
|
+
// Clear stored state on error
|
|
4606
|
+
sessionStorage.removeItem('uipath_sdk_code_verifier');
|
|
4607
|
+
throw new Error('Invalid authorization code format');
|
|
4884
4608
|
}
|
|
4609
|
+
// Authorization code is present and validated, so we can exchange it for a token.
|
|
4610
|
+
await this._handleOAuthCallback(code, clientId, redirectUri);
|
|
4611
|
+
return this.hasValidToken();
|
|
4612
|
+
}
|
|
4613
|
+
else {
|
|
4614
|
+
// Not in an OAuth flow - initiate one
|
|
4615
|
+
// Ignore any URL parameters that might be present
|
|
4616
|
+
await this._initiateOAuthFlow(clientId, redirectUri, scope);
|
|
4617
|
+
// This line is not expected to be reached due to redirect
|
|
4618
|
+
return false;
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
/**
|
|
4622
|
+
* Authenticate using API secret
|
|
4623
|
+
*/
|
|
4624
|
+
authenticateWithSecret(secret) {
|
|
4625
|
+
try {
|
|
4626
|
+
this.updateToken({ token: secret, type: 'secret' });
|
|
4627
|
+
return true;
|
|
4885
4628
|
}
|
|
4886
4629
|
catch (error) {
|
|
4887
|
-
|
|
4888
|
-
|
|
4630
|
+
console.error('Failed to authenticate with secret', error);
|
|
4631
|
+
return false;
|
|
4889
4632
|
}
|
|
4890
4633
|
}
|
|
4891
4634
|
/**
|
|
4892
|
-
*
|
|
4635
|
+
* Updates the access token used for API requests
|
|
4636
|
+
* @param tokenInfo The token information containing the access token, type, expiration, and refresh token
|
|
4893
4637
|
*/
|
|
4894
|
-
|
|
4895
|
-
|
|
4638
|
+
updateToken(tokenInfo) {
|
|
4639
|
+
this.tokenManager.setToken(tokenInfo);
|
|
4896
4640
|
}
|
|
4897
4641
|
/**
|
|
4898
|
-
*
|
|
4642
|
+
* Checks if the current token is valid
|
|
4899
4643
|
*/
|
|
4900
|
-
|
|
4901
|
-
return
|
|
4644
|
+
hasValidToken() {
|
|
4645
|
+
return this.tokenManager.hasValidToken();
|
|
4646
|
+
}
|
|
4647
|
+
/**
|
|
4648
|
+
* Get the current token
|
|
4649
|
+
*/
|
|
4650
|
+
getToken() {
|
|
4651
|
+
if (!this.tokenManager.hasValidToken()) {
|
|
4652
|
+
return undefined;
|
|
4653
|
+
}
|
|
4654
|
+
return this.tokenManager.getToken();
|
|
4655
|
+
}
|
|
4656
|
+
/**
|
|
4657
|
+
* Generates a random code verifier for PKCE
|
|
4658
|
+
*/
|
|
4659
|
+
generateCodeVerifier() {
|
|
4660
|
+
if (isBrowser) {
|
|
4661
|
+
const array = new Uint8Array(32);
|
|
4662
|
+
crypto.getRandomValues(array);
|
|
4663
|
+
return this._base64URLEncode(array);
|
|
4664
|
+
}
|
|
4665
|
+
else {
|
|
4666
|
+
// In Node.js environment
|
|
4667
|
+
try {
|
|
4668
|
+
const crypto = require('crypto');
|
|
4669
|
+
return crypto.randomBytes(32)
|
|
4670
|
+
.toString('base64')
|
|
4671
|
+
.replace(/\+/g, '-')
|
|
4672
|
+
.replace(/\//g, '_')
|
|
4673
|
+
.replace(/=/g, '');
|
|
4674
|
+
}
|
|
4675
|
+
catch {
|
|
4676
|
+
throw new Error("crypto not available in browser");
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4902
4679
|
}
|
|
4903
4680
|
/**
|
|
4904
|
-
*
|
|
4681
|
+
* Generates a code challenge from the code verifier
|
|
4905
4682
|
*/
|
|
4906
|
-
async
|
|
4907
|
-
if (
|
|
4908
|
-
|
|
4683
|
+
async generateCodeChallenge(codeVerifier) {
|
|
4684
|
+
if (isBrowser) {
|
|
4685
|
+
const encoder = new TextEncoder();
|
|
4686
|
+
const data = encoder.encode(codeVerifier);
|
|
4687
|
+
const hash = await crypto.subtle.digest('SHA-256', data);
|
|
4688
|
+
return this._base64URLEncode(new Uint8Array(hash));
|
|
4909
4689
|
}
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
return
|
|
4690
|
+
else {
|
|
4691
|
+
// In Node.js environment
|
|
4692
|
+
try {
|
|
4693
|
+
const crypto = require('crypto');
|
|
4694
|
+
return crypto.createHash('sha256')
|
|
4695
|
+
.update(codeVerifier)
|
|
4696
|
+
.digest('base64')
|
|
4697
|
+
.replace(/\+/g, '-')
|
|
4698
|
+
.replace(/\//g, '_')
|
|
4699
|
+
.replace(/=/g, '');
|
|
4700
|
+
}
|
|
4701
|
+
catch {
|
|
4702
|
+
throw new Error("crypto not available in browser");
|
|
4915
4703
|
}
|
|
4916
|
-
return false;
|
|
4917
|
-
}
|
|
4918
|
-
catch (error) {
|
|
4919
|
-
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
|
|
4920
|
-
throw new Error(`Failed to complete OAuth: ${errorMessage}`);
|
|
4921
4704
|
}
|
|
4922
4705
|
}
|
|
4923
4706
|
/**
|
|
4924
|
-
*
|
|
4707
|
+
* Gets the authorization URL for the OAuth flow
|
|
4925
4708
|
*/
|
|
4926
|
-
|
|
4927
|
-
|
|
4709
|
+
getAuthorizationUrl(params) {
|
|
4710
|
+
const orgName = this.config.orgName;
|
|
4711
|
+
const queryParams = new URLSearchParams({
|
|
4712
|
+
response_type: 'code',
|
|
4713
|
+
client_id: params.clientId,
|
|
4714
|
+
redirect_uri: params.redirectUri,
|
|
4715
|
+
code_challenge: params.codeChallenge,
|
|
4716
|
+
code_challenge_method: 'S256',
|
|
4717
|
+
scope: params.scope + ' offline_access',
|
|
4718
|
+
state: params.state || this.generateCodeVerifier().slice(0, 16)
|
|
4719
|
+
});
|
|
4720
|
+
return `${this.config.baseUrl}/${orgName}/${IDENTITY_ENDPOINTS.AUTHORIZE}?${queryParams.toString()}`;
|
|
4928
4721
|
}
|
|
4929
4722
|
/**
|
|
4930
|
-
*
|
|
4723
|
+
* Exchanges the authorization code for an access token and automatically updates the current token
|
|
4931
4724
|
*/
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4725
|
+
async _getAccessToken(params) {
|
|
4726
|
+
const orgName = this.config.orgName;
|
|
4727
|
+
const body = new URLSearchParams({
|
|
4728
|
+
grant_type: 'authorization_code',
|
|
4729
|
+
client_id: params.clientId,
|
|
4730
|
+
code: params.code,
|
|
4731
|
+
redirect_uri: params.redirectUri,
|
|
4732
|
+
code_verifier: params.codeVerifier
|
|
4733
|
+
});
|
|
4734
|
+
const response = await fetch(`${this.config.baseUrl}/${orgName}/${IDENTITY_ENDPOINTS.TOKEN}`, {
|
|
4735
|
+
method: 'POST',
|
|
4736
|
+
headers: {
|
|
4737
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
4738
|
+
},
|
|
4739
|
+
body: body.toString()
|
|
4740
|
+
});
|
|
4741
|
+
if (!response.ok) {
|
|
4742
|
+
const errorData = await response.json().catch(() => ({ message: response.statusText }));
|
|
4743
|
+
console.error("OAuth error:", errorData);
|
|
4744
|
+
throw new Error(`Failed to get access token: ${JSON.stringify(errorData)}`);
|
|
4745
|
+
}
|
|
4746
|
+
const token = await response.json();
|
|
4747
|
+
this.updateToken({
|
|
4748
|
+
token: token.access_token,
|
|
4749
|
+
type: 'oauth',
|
|
4750
|
+
expiresAt: token.expires_in ? new Date(Date.now() + token.expires_in * 1000) : undefined,
|
|
4751
|
+
refreshToken: token.refresh_token
|
|
4752
|
+
});
|
|
4753
|
+
return token;
|
|
4951
4754
|
}
|
|
4952
4755
|
/**
|
|
4953
|
-
*
|
|
4756
|
+
* Base64URL encodes an array buffer
|
|
4954
4757
|
*/
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
timestamp: this.timestamp
|
|
4962
|
-
};
|
|
4758
|
+
_base64URLEncode(buffer) {
|
|
4759
|
+
const base64 = btoa(String.fromCharCode(...buffer));
|
|
4760
|
+
return base64
|
|
4761
|
+
.replace(/\+/g, '-')
|
|
4762
|
+
.replace(/\//g, '_')
|
|
4763
|
+
.replace(/=/g, '');
|
|
4963
4764
|
}
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4765
|
+
async _initiateOAuthFlow(clientId, redirectUri, scope) {
|
|
4766
|
+
const codeVerifier = this.generateCodeVerifier();
|
|
4767
|
+
const codeChallenge = await this.generateCodeChallenge(codeVerifier);
|
|
4768
|
+
// Store complete OAuth context for callback completion
|
|
4769
|
+
const oauthContext = {
|
|
4770
|
+
codeVerifier,
|
|
4771
|
+
clientId,
|
|
4772
|
+
redirectUri,
|
|
4773
|
+
baseUrl: this.config.baseUrl,
|
|
4774
|
+
orgName: this.config.orgName,
|
|
4775
|
+
tenantName: this.config.tenantName,
|
|
4776
|
+
scope
|
|
4971
4777
|
};
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
// Client errors (4xx)
|
|
4980
|
-
BAD_REQUEST: 400,
|
|
4981
|
-
UNAUTHORIZED: 401,
|
|
4982
|
-
FORBIDDEN: 403,
|
|
4983
|
-
NOT_FOUND: 404,
|
|
4984
|
-
TOO_MANY_REQUESTS: 429,
|
|
4985
|
-
// Server errors (5xx)
|
|
4986
|
-
INTERNAL_SERVER_ERROR: 500,
|
|
4987
|
-
NOT_IMPLEMENTED: 501,
|
|
4988
|
-
BAD_GATEWAY: 502,
|
|
4989
|
-
SERVICE_UNAVAILABLE: 503,
|
|
4990
|
-
GATEWAY_TIMEOUT: 504
|
|
4991
|
-
};
|
|
4992
|
-
/**
|
|
4993
|
-
* Error type constants for consistent error identification
|
|
4994
|
-
*/
|
|
4995
|
-
const ErrorType = {
|
|
4996
|
-
AUTHENTICATION: 'AuthenticationError',
|
|
4997
|
-
AUTHORIZATION: 'AuthorizationError',
|
|
4998
|
-
VALIDATION: 'ValidationError',
|
|
4999
|
-
NOT_FOUND: 'NotFoundError',
|
|
5000
|
-
RATE_LIMIT: 'RateLimitError',
|
|
5001
|
-
SERVER: 'ServerError',
|
|
5002
|
-
NETWORK: 'NetworkError'
|
|
5003
|
-
};
|
|
5004
|
-
/**
|
|
5005
|
-
* Standard error message constants
|
|
5006
|
-
*/
|
|
5007
|
-
const ErrorMessages = {
|
|
5008
|
-
// Authentication errors
|
|
5009
|
-
AUTHENTICATION_FAILED: 'Authentication failed',
|
|
5010
|
-
// Authorization errors
|
|
5011
|
-
ACCESS_DENIED: 'Access denied',
|
|
5012
|
-
// Validation errors
|
|
5013
|
-
VALIDATION_FAILED: 'Validation failed',
|
|
5014
|
-
// Not found errors
|
|
5015
|
-
RESOURCE_NOT_FOUND: 'Resource not found',
|
|
5016
|
-
// Rate limit errors
|
|
5017
|
-
RATE_LIMIT_EXCEEDED: 'Rate limit exceeded',
|
|
5018
|
-
// Server errors
|
|
5019
|
-
INTERNAL_SERVER_ERROR: 'Internal Server error occurred',
|
|
5020
|
-
// Network errors
|
|
5021
|
-
NETWORK_ERROR: 'Network error occurred'};
|
|
5022
|
-
|
|
5023
|
-
/**
|
|
5024
|
-
* Error thrown when authentication fails (401 errors)
|
|
5025
|
-
* Common scenarios:
|
|
5026
|
-
* - Invalid credentials
|
|
5027
|
-
* - Expired token
|
|
5028
|
-
* - Missing authentication
|
|
5029
|
-
*/
|
|
5030
|
-
class AuthenticationError extends UiPathError {
|
|
5031
|
-
constructor(params = {}) {
|
|
5032
|
-
super(ErrorType.AUTHENTICATION, {
|
|
5033
|
-
message: params.message || ErrorMessages.AUTHENTICATION_FAILED,
|
|
5034
|
-
statusCode: params.statusCode ?? HttpStatus.UNAUTHORIZED,
|
|
5035
|
-
requestId: params.requestId
|
|
4778
|
+
sessionStorage.setItem('uipath_sdk_oauth_context', JSON.stringify(oauthContext));
|
|
4779
|
+
sessionStorage.setItem('uipath_sdk_code_verifier', codeVerifier);
|
|
4780
|
+
const authUrl = this.getAuthorizationUrl({
|
|
4781
|
+
clientId,
|
|
4782
|
+
redirectUri,
|
|
4783
|
+
codeChallenge,
|
|
4784
|
+
scope
|
|
5036
4785
|
});
|
|
4786
|
+
window.location.href = authUrl;
|
|
5037
4787
|
}
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
constructor(params = {}) {
|
|
5049
|
-
super(ErrorType.AUTHORIZATION, {
|
|
5050
|
-
message: params.message || ErrorMessages.ACCESS_DENIED,
|
|
5051
|
-
statusCode: params.statusCode ?? HttpStatus.FORBIDDEN,
|
|
5052
|
-
requestId: params.requestId
|
|
4788
|
+
async _handleOAuthCallback(code, clientId, redirectUri) {
|
|
4789
|
+
const codeVerifier = sessionStorage.getItem('uipath_sdk_code_verifier');
|
|
4790
|
+
if (!codeVerifier) {
|
|
4791
|
+
throw new Error('Code verifier not found in session storage. Authentication may have been interrupted.');
|
|
4792
|
+
}
|
|
4793
|
+
await this._getAccessToken({
|
|
4794
|
+
clientId,
|
|
4795
|
+
redirectUri,
|
|
4796
|
+
code,
|
|
4797
|
+
codeVerifier
|
|
5053
4798
|
});
|
|
4799
|
+
// Clear OAuth context and code verifier after successful token exchange
|
|
4800
|
+
sessionStorage.removeItem('uipath_sdk_oauth_context');
|
|
4801
|
+
sessionStorage.removeItem('uipath_sdk_code_verifier');
|
|
4802
|
+
const url = new URL(window.location.href);
|
|
4803
|
+
url.searchParams.delete('code');
|
|
4804
|
+
url.searchParams.delete('state');
|
|
4805
|
+
window.history.replaceState({}, '', url.toString());
|
|
5054
4806
|
}
|
|
5055
4807
|
}
|
|
5056
4808
|
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
* - Invalid input parameters
|
|
5061
|
-
* - Missing required fields
|
|
5062
|
-
* - Invalid data format
|
|
5063
|
-
*/
|
|
5064
|
-
class ValidationError extends UiPathError {
|
|
5065
|
-
constructor(params = {}) {
|
|
5066
|
-
super(ErrorType.VALIDATION, {
|
|
5067
|
-
message: params.message || ErrorMessages.VALIDATION_FAILED,
|
|
5068
|
-
statusCode: params.statusCode ?? HttpStatus.BAD_REQUEST,
|
|
5069
|
-
requestId: params.requestId
|
|
5070
|
-
});
|
|
4809
|
+
function validateConfig(config) {
|
|
4810
|
+
if (!config.baseUrl || !config.orgName || !config.tenantName) {
|
|
4811
|
+
throw new Error('Missing required configuration: baseUrl, orgName, and tenantName are required');
|
|
5071
4812
|
}
|
|
4813
|
+
if (!hasSecretConfig(config) && !hasOAuthConfig(config)) {
|
|
4814
|
+
throw new Error('Invalid configuration: must provide either secret or (clientId, redirectUri, and scope)');
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4817
|
+
function normalizeBaseUrl(url) {
|
|
4818
|
+
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
5072
4819
|
}
|
|
5073
4820
|
|
|
5074
4821
|
/**
|
|
5075
|
-
*
|
|
5076
|
-
* Common scenarios:
|
|
5077
|
-
* - Resource doesn't exist
|
|
5078
|
-
* - Invalid ID provided
|
|
5079
|
-
* - Resource deleted
|
|
4822
|
+
* SDK Telemetry constants
|
|
5080
4823
|
*/
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
4824
|
+
// Connection string placeholder that will be replaced during build
|
|
4825
|
+
const CONNECTION_STRING = "InstrumentationKey=a6efa11d-1feb-4508-9738-e13e12dcae5e;IngestionEndpoint=https://westeurope-5.in.applicationinsights.azure.com/;LiveEndpoint=https://westeurope.livediagnostics.monitor.azure.com/;ApplicationId=7c58eb1c-9581-4ba6-839e-11725848a037";
|
|
4826
|
+
// SDK Version placeholder
|
|
4827
|
+
const SDK_VERSION = "1.1.0";
|
|
4828
|
+
const VERSION = "Version";
|
|
4829
|
+
const SERVICE = "Service";
|
|
4830
|
+
const CLOUD_ORGANIZATION_NAME = "CloudOrganizationName";
|
|
4831
|
+
const CLOUD_TENANT_NAME = "CloudTenantName";
|
|
4832
|
+
const CLOUD_URL = "CloudUrl";
|
|
4833
|
+
const CLOUD_CLIENT_ID = "CloudClientId";
|
|
4834
|
+
const CLOUD_REDIRECT_URI = "CloudRedirectUri";
|
|
4835
|
+
const APP_NAME = "ApplicationName";
|
|
4836
|
+
const CLOUD_ROLE_NAME = "uipath-ts-sdk";
|
|
4837
|
+
// Service and logger names
|
|
4838
|
+
const SDK_SERVICE_NAME = "UiPath.TypeScript.Sdk";
|
|
4839
|
+
const SDK_LOGGER_NAME = "uipath-ts-sdk-telemetry";
|
|
4840
|
+
// Event names
|
|
4841
|
+
const SDK_RUN_EVENT = "Sdk.Run";
|
|
4842
|
+
// Default value for unknown/empty attributes
|
|
4843
|
+
const UNKNOWN = "";
|
|
5090
4844
|
|
|
5091
4845
|
/**
|
|
5092
|
-
*
|
|
5093
|
-
* Common scenarios:
|
|
5094
|
-
* - Too many requests in a time window
|
|
5095
|
-
* - API throttling
|
|
4846
|
+
* Log exporter that sends ALL logs as Application Insights custom events
|
|
5096
4847
|
*/
|
|
5097
|
-
class
|
|
5098
|
-
constructor(
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
4848
|
+
class ApplicationInsightsEventExporter {
|
|
4849
|
+
constructor(connectionString) {
|
|
4850
|
+
this.connectionString = connectionString;
|
|
4851
|
+
}
|
|
4852
|
+
export(logs, resultCallback) {
|
|
4853
|
+
try {
|
|
4854
|
+
logs.forEach(logRecord => {
|
|
4855
|
+
this.sendAsCustomEvent(logRecord);
|
|
4856
|
+
});
|
|
4857
|
+
resultCallback({ code: 0 });
|
|
4858
|
+
}
|
|
4859
|
+
catch (error) {
|
|
4860
|
+
console.debug('Failed to export logs to Application Insights:', error);
|
|
4861
|
+
resultCallback({ code: 2, error });
|
|
4862
|
+
}
|
|
4863
|
+
}
|
|
4864
|
+
shutdown() {
|
|
4865
|
+
return Promise.resolve();
|
|
4866
|
+
}
|
|
4867
|
+
sendAsCustomEvent(logRecord) {
|
|
4868
|
+
// Get event name from body or attributes
|
|
4869
|
+
const eventName = logRecord.body || SDK_RUN_EVENT;
|
|
4870
|
+
const payload = {
|
|
4871
|
+
name: 'Microsoft.ApplicationInsights.Event',
|
|
4872
|
+
time: new Date().toISOString(),
|
|
4873
|
+
iKey: this.extractInstrumentationKey(),
|
|
4874
|
+
data: {
|
|
4875
|
+
baseType: 'EventData',
|
|
4876
|
+
baseData: {
|
|
4877
|
+
ver: 2,
|
|
4878
|
+
name: eventName,
|
|
4879
|
+
properties: this.convertAttributesToProperties(logRecord.attributes || {})
|
|
4880
|
+
}
|
|
4881
|
+
},
|
|
4882
|
+
tags: {
|
|
4883
|
+
'ai.cloud.role': CLOUD_ROLE_NAME,
|
|
4884
|
+
'ai.cloud.roleInstance': SDK_VERSION
|
|
4885
|
+
}
|
|
4886
|
+
};
|
|
4887
|
+
this.sendToApplicationInsights(payload);
|
|
4888
|
+
}
|
|
4889
|
+
extractInstrumentationKey() {
|
|
4890
|
+
const match = this.connectionString.match(/InstrumentationKey=([^;]+)/);
|
|
4891
|
+
return match ? match[1] : '';
|
|
4892
|
+
}
|
|
4893
|
+
convertAttributesToProperties(attributes) {
|
|
4894
|
+
const properties = {};
|
|
4895
|
+
Object.entries(attributes || {}).forEach(([key, value]) => {
|
|
4896
|
+
properties[key] = String(value);
|
|
5103
4897
|
});
|
|
4898
|
+
return properties;
|
|
4899
|
+
}
|
|
4900
|
+
async sendToApplicationInsights(payload) {
|
|
4901
|
+
try {
|
|
4902
|
+
const ingestionEndpoint = this.extractIngestionEndpoint();
|
|
4903
|
+
if (!ingestionEndpoint) {
|
|
4904
|
+
console.debug('No ingestion endpoint found in connection string');
|
|
4905
|
+
return;
|
|
4906
|
+
}
|
|
4907
|
+
const url = `${ingestionEndpoint}/v2/track`;
|
|
4908
|
+
const response = await fetch(url, {
|
|
4909
|
+
method: 'POST',
|
|
4910
|
+
headers: {
|
|
4911
|
+
'Content-Type': 'application/json',
|
|
4912
|
+
},
|
|
4913
|
+
body: JSON.stringify(payload)
|
|
4914
|
+
});
|
|
4915
|
+
if (!response.ok) {
|
|
4916
|
+
console.debug(`Failed to send event telemetry: ${response.status} ${response.statusText}`);
|
|
4917
|
+
}
|
|
4918
|
+
}
|
|
4919
|
+
catch (error) {
|
|
4920
|
+
console.debug('Error sending event telemetry to Application Insights:', error);
|
|
4921
|
+
}
|
|
4922
|
+
}
|
|
4923
|
+
extractIngestionEndpoint() {
|
|
4924
|
+
const match = this.connectionString.match(/IngestionEndpoint=([^;]+)/);
|
|
4925
|
+
return match ? match[1] : '';
|
|
5104
4926
|
}
|
|
5105
4927
|
}
|
|
5106
|
-
|
|
5107
4928
|
/**
|
|
5108
|
-
*
|
|
5109
|
-
* Common scenarios:
|
|
5110
|
-
* - Internal server error
|
|
5111
|
-
* - Service unavailable
|
|
5112
|
-
* - Gateway timeout
|
|
4929
|
+
* Singleton telemetry client
|
|
5113
4930
|
*/
|
|
5114
|
-
class
|
|
5115
|
-
constructor(
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
4931
|
+
class TelemetryClient {
|
|
4932
|
+
constructor() {
|
|
4933
|
+
this.isInitialized = false;
|
|
4934
|
+
}
|
|
4935
|
+
static getInstance() {
|
|
4936
|
+
if (!TelemetryClient.instance) {
|
|
4937
|
+
TelemetryClient.instance = new TelemetryClient();
|
|
4938
|
+
}
|
|
4939
|
+
return TelemetryClient.instance;
|
|
5121
4940
|
}
|
|
5122
4941
|
/**
|
|
5123
|
-
*
|
|
4942
|
+
* Initialize telemetry
|
|
5124
4943
|
*/
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
4944
|
+
initialize(config) {
|
|
4945
|
+
if (this.isInitialized) {
|
|
4946
|
+
return;
|
|
4947
|
+
}
|
|
4948
|
+
this.isInitialized = true;
|
|
4949
|
+
if (config) {
|
|
4950
|
+
this.telemetryContext = config;
|
|
4951
|
+
}
|
|
4952
|
+
try {
|
|
4953
|
+
const connectionString = this.getConnectionString();
|
|
4954
|
+
if (!connectionString) {
|
|
4955
|
+
return;
|
|
4956
|
+
}
|
|
4957
|
+
this.setupTelemetryProvider(connectionString);
|
|
4958
|
+
}
|
|
4959
|
+
catch (error) {
|
|
4960
|
+
// Silent failure - telemetry errors shouldn't break functionality
|
|
4961
|
+
console.debug('Failed to initialize OpenTelemetry:', error);
|
|
4962
|
+
}
|
|
5129
4963
|
}
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
*/
|
|
5140
|
-
class NetworkError extends UiPathError {
|
|
5141
|
-
constructor(params = {}) {
|
|
5142
|
-
super(ErrorType.NETWORK, {
|
|
5143
|
-
message: params.message || ErrorMessages.NETWORK_ERROR,
|
|
5144
|
-
statusCode: params.statusCode, // Network errors typically don't have HTTP status codes
|
|
5145
|
-
requestId: params.requestId
|
|
4964
|
+
getConnectionString() {
|
|
4965
|
+
const connectionString = CONNECTION_STRING;
|
|
4966
|
+
return connectionString;
|
|
4967
|
+
}
|
|
4968
|
+
setupTelemetryProvider(connectionString) {
|
|
4969
|
+
const exporter = new ApplicationInsightsEventExporter(connectionString);
|
|
4970
|
+
const processor = new BatchLogRecordProcessor(exporter);
|
|
4971
|
+
this.logProvider = new LoggerProvider({
|
|
4972
|
+
processors: [processor]
|
|
5146
4973
|
});
|
|
4974
|
+
this.logger = this.logProvider.getLogger(SDK_LOGGER_NAME);
|
|
4975
|
+
}
|
|
4976
|
+
/**
|
|
4977
|
+
* Track a telemetry event
|
|
4978
|
+
*/
|
|
4979
|
+
track(eventName, name, extraAttributes = {}) {
|
|
4980
|
+
try {
|
|
4981
|
+
// Skip if logger not initialized
|
|
4982
|
+
if (!this.logger) {
|
|
4983
|
+
return;
|
|
4984
|
+
}
|
|
4985
|
+
const finalDisplayName = name || eventName;
|
|
4986
|
+
const attributes = this.getEnrichedAttributes(extraAttributes, eventName);
|
|
4987
|
+
// Emit as log
|
|
4988
|
+
this.logger.emit({
|
|
4989
|
+
body: finalDisplayName,
|
|
4990
|
+
attributes: attributes,
|
|
4991
|
+
timestamp: Date.now(),
|
|
4992
|
+
});
|
|
4993
|
+
}
|
|
4994
|
+
catch (error) {
|
|
4995
|
+
// Silent failure
|
|
4996
|
+
console.debug('Failed to track telemetry event:', error);
|
|
4997
|
+
}
|
|
4998
|
+
}
|
|
4999
|
+
/**
|
|
5000
|
+
* Get enriched attributes for telemetry events
|
|
5001
|
+
*/
|
|
5002
|
+
getEnrichedAttributes(extraAttributes, eventName) {
|
|
5003
|
+
const attributes = {
|
|
5004
|
+
[APP_NAME]: SDK_SERVICE_NAME,
|
|
5005
|
+
[VERSION]: SDK_VERSION,
|
|
5006
|
+
[SERVICE]: eventName,
|
|
5007
|
+
[CLOUD_URL]: this.createCloudUrl(),
|
|
5008
|
+
[CLOUD_ORGANIZATION_NAME]: this.telemetryContext?.orgName || UNKNOWN,
|
|
5009
|
+
[CLOUD_TENANT_NAME]: this.telemetryContext?.tenantName || UNKNOWN,
|
|
5010
|
+
[CLOUD_REDIRECT_URI]: this.telemetryContext?.redirectUri || UNKNOWN,
|
|
5011
|
+
[CLOUD_CLIENT_ID]: this.telemetryContext?.clientId || UNKNOWN,
|
|
5012
|
+
...extraAttributes,
|
|
5013
|
+
};
|
|
5014
|
+
return attributes;
|
|
5015
|
+
}
|
|
5016
|
+
/**
|
|
5017
|
+
* Create cloud URL from base URL, organization ID, and tenant ID
|
|
5018
|
+
*/
|
|
5019
|
+
createCloudUrl() {
|
|
5020
|
+
const baseUrl = this.telemetryContext?.baseUrl;
|
|
5021
|
+
const orgId = this.telemetryContext?.orgName;
|
|
5022
|
+
const tenantId = this.telemetryContext?.tenantName;
|
|
5023
|
+
if (!baseUrl || !orgId || !tenantId) {
|
|
5024
|
+
return UNKNOWN;
|
|
5025
|
+
}
|
|
5026
|
+
return `${baseUrl}/${orgId}/${tenantId}`;
|
|
5147
5027
|
}
|
|
5148
5028
|
}
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
*/
|
|
5153
|
-
function isUiPathError(error) {
|
|
5154
|
-
return error instanceof UiPathError;
|
|
5155
|
-
}
|
|
5156
|
-
/**
|
|
5157
|
-
* Type guard to check if an error is an AuthenticationError
|
|
5158
|
-
*/
|
|
5159
|
-
function isAuthenticationError(error) {
|
|
5160
|
-
return error instanceof AuthenticationError;
|
|
5161
|
-
}
|
|
5162
|
-
/**
|
|
5163
|
-
* Type guard to check if an error is an AuthorizationError
|
|
5164
|
-
*/
|
|
5165
|
-
function isAuthorizationError(error) {
|
|
5166
|
-
return error instanceof AuthorizationError;
|
|
5167
|
-
}
|
|
5168
|
-
/**
|
|
5169
|
-
* Type guard to check if an error is a ValidationError
|
|
5170
|
-
*/
|
|
5171
|
-
function isValidationError(error) {
|
|
5172
|
-
return error instanceof ValidationError;
|
|
5173
|
-
}
|
|
5029
|
+
// Export singleton instance
|
|
5030
|
+
const telemetryClient = TelemetryClient.getInstance();
|
|
5031
|
+
|
|
5174
5032
|
/**
|
|
5175
|
-
*
|
|
5033
|
+
* SDK Track decorator and function for telemetry
|
|
5176
5034
|
*/
|
|
5177
|
-
function isNotFoundError(error) {
|
|
5178
|
-
return error instanceof NotFoundError;
|
|
5179
|
-
}
|
|
5180
5035
|
/**
|
|
5181
|
-
*
|
|
5036
|
+
* Direct tracking function
|
|
5182
5037
|
*/
|
|
5183
|
-
function
|
|
5184
|
-
|
|
5038
|
+
function trackEvent(eventName, name, attributes) {
|
|
5039
|
+
telemetryClient.track(eventName, name, attributes);
|
|
5185
5040
|
}
|
|
5041
|
+
|
|
5186
5042
|
/**
|
|
5187
|
-
*
|
|
5043
|
+
* SDK Internals Registry - Internal registry for SDK instances
|
|
5044
|
+
*
|
|
5045
|
+
* This class is NOT exported in the public API.
|
|
5046
|
+
* It provides a secure way to share SDK internals between
|
|
5047
|
+
* the UiPath class and service classes without exposing them publicly.
|
|
5048
|
+
*
|
|
5049
|
+
* @internal
|
|
5188
5050
|
*/
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5051
|
+
// Global symbol key to ensure WeakMap is shared across module instances
|
|
5052
|
+
// This prevents issues when core and service modules are bundled separately
|
|
5053
|
+
const REGISTRY_KEY = Symbol.for('@uipath/sdk-internals-registry');
|
|
5054
|
+
// Get or create the global WeakMap store
|
|
5055
|
+
const getGlobalStore = () => {
|
|
5056
|
+
const globalObj = globalThis;
|
|
5057
|
+
if (!globalObj[REGISTRY_KEY]) {
|
|
5058
|
+
globalObj[REGISTRY_KEY] = new WeakMap();
|
|
5059
|
+
}
|
|
5060
|
+
return globalObj[REGISTRY_KEY];
|
|
5061
|
+
};
|
|
5192
5062
|
/**
|
|
5193
|
-
*
|
|
5063
|
+
* Internal registry for SDK private components.
|
|
5064
|
+
* Uses WeakMap to prevent memory leaks - entries are automatically
|
|
5065
|
+
* garbage collected when the SDK instance is no longer referenced.
|
|
5066
|
+
*
|
|
5067
|
+
* Uses a global singleton pattern to ensure the same WeakMap is shared
|
|
5068
|
+
* across separately bundled modules (core, entities, tasks, etc.).
|
|
5069
|
+
*
|
|
5070
|
+
* @internal - Not exported in public API
|
|
5194
5071
|
*/
|
|
5195
|
-
|
|
5196
|
-
|
|
5072
|
+
class SDKInternalsRegistry {
|
|
5073
|
+
// Use global store to ensure sharing across module bundles
|
|
5074
|
+
static get store() {
|
|
5075
|
+
return getGlobalStore();
|
|
5076
|
+
}
|
|
5077
|
+
/**
|
|
5078
|
+
* Register SDK instance internals
|
|
5079
|
+
* Called by UiPath constructor
|
|
5080
|
+
*/
|
|
5081
|
+
static set(instance, internals) {
|
|
5082
|
+
this.store.set(instance, internals);
|
|
5083
|
+
}
|
|
5084
|
+
/**
|
|
5085
|
+
* Retrieve SDK instance internals
|
|
5086
|
+
* Called by BaseService constructor
|
|
5087
|
+
*/
|
|
5088
|
+
static get(instance) {
|
|
5089
|
+
const internals = this.store.get(instance);
|
|
5090
|
+
if (!internals) {
|
|
5091
|
+
throw new Error('Invalid SDK instance. Make sure to pass a valid UiPath instance to the service constructor.');
|
|
5092
|
+
}
|
|
5093
|
+
return internals;
|
|
5094
|
+
}
|
|
5197
5095
|
}
|
|
5096
|
+
|
|
5097
|
+
var _UiPath_config, _UiPath_authService, _UiPath_initialized;
|
|
5198
5098
|
/**
|
|
5199
|
-
*
|
|
5099
|
+
* UiPath - Core SDK class for authentication and configuration management.
|
|
5100
|
+
*
|
|
5101
|
+
* Handles authentication, configuration, and provides access to SDK internals
|
|
5102
|
+
* for service instantiation in the modular pattern.
|
|
5103
|
+
*
|
|
5104
|
+
* @example
|
|
5105
|
+
* ```typescript
|
|
5106
|
+
* // Modular pattern
|
|
5107
|
+
* import { UiPath } from '@uipath/uipath-typescript/core';
|
|
5108
|
+
* import { Entities } from '@uipath/uipath-typescript/entities';
|
|
5109
|
+
*
|
|
5110
|
+
* const sdk = new UiPath({
|
|
5111
|
+
* baseUrl: 'https://cloud.uipath.com',
|
|
5112
|
+
* orgName: 'myorg',
|
|
5113
|
+
* tenantName: 'mytenant',
|
|
5114
|
+
* clientId: 'xxx',
|
|
5115
|
+
* redirectUri: 'http://localhost:3000/callback',
|
|
5116
|
+
* scope: 'OR.Users OR.Robots'
|
|
5117
|
+
* });
|
|
5118
|
+
*
|
|
5119
|
+
* await sdk.initialize();
|
|
5120
|
+
*
|
|
5121
|
+
* const entitiesService = new Entities(sdk);
|
|
5122
|
+
* const allEntities = await entitiesService.getAll();
|
|
5123
|
+
* ```
|
|
5200
5124
|
*/
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5125
|
+
class UiPath {
|
|
5126
|
+
constructor(config) {
|
|
5127
|
+
// Private fields - true runtime privacy, not visible via Object.keys()
|
|
5128
|
+
_UiPath_config.set(this, void 0);
|
|
5129
|
+
_UiPath_authService.set(this, void 0);
|
|
5130
|
+
_UiPath_initialized.set(this, false);
|
|
5131
|
+
// Validate and normalize the configuration
|
|
5132
|
+
validateConfig(config);
|
|
5133
|
+
const hasSecretAuth = hasSecretConfig(config);
|
|
5134
|
+
const hasOAuthAuth = hasOAuthConfig(config);
|
|
5135
|
+
// Initialize core components
|
|
5136
|
+
const internalConfig = new UiPathConfig({
|
|
5137
|
+
baseUrl: normalizeBaseUrl(config.baseUrl),
|
|
5138
|
+
orgName: config.orgName,
|
|
5139
|
+
tenantName: config.tenantName,
|
|
5140
|
+
secret: hasSecretAuth ? config.secret : undefined,
|
|
5141
|
+
clientId: hasOAuthAuth ? config.clientId : undefined,
|
|
5142
|
+
redirectUri: hasOAuthAuth ? config.redirectUri : undefined,
|
|
5143
|
+
scope: hasOAuthAuth ? config.scope : undefined
|
|
5144
|
+
});
|
|
5145
|
+
const executionContext = new ExecutionContext();
|
|
5146
|
+
__classPrivateFieldSet(this, _UiPath_authService, new AuthService(internalConfig, executionContext), "f");
|
|
5147
|
+
__classPrivateFieldSet(this, _UiPath_config, internalConfig, "f");
|
|
5148
|
+
// Store internals in SDKInternalsRegistry (not visible on instance)
|
|
5149
|
+
SDKInternalsRegistry.set(this, {
|
|
5150
|
+
config: internalConfig,
|
|
5151
|
+
context: executionContext,
|
|
5152
|
+
tokenManager: __classPrivateFieldGet(this, _UiPath_authService, "f").getTokenManager()
|
|
5153
|
+
});
|
|
5154
|
+
// Expose read-only config for user convenience
|
|
5155
|
+
this.config = {
|
|
5156
|
+
baseUrl: internalConfig.baseUrl,
|
|
5157
|
+
orgName: internalConfig.orgName,
|
|
5158
|
+
tenantName: internalConfig.tenantName
|
|
5206
5159
|
};
|
|
5160
|
+
// Initialize telemetry with SDK configuration
|
|
5161
|
+
telemetryClient.initialize({
|
|
5162
|
+
baseUrl: config.baseUrl,
|
|
5163
|
+
orgName: config.orgName,
|
|
5164
|
+
tenantName: config.tenantName,
|
|
5165
|
+
clientId: hasOAuthAuth ? config.clientId : undefined,
|
|
5166
|
+
redirectUri: hasOAuthAuth ? config.redirectUri : undefined
|
|
5167
|
+
});
|
|
5168
|
+
// Track SDK initialization
|
|
5169
|
+
trackEvent('Sdk.Auth');
|
|
5170
|
+
// Auto-initialize for secret-based auth
|
|
5171
|
+
if (hasSecretAuth) {
|
|
5172
|
+
__classPrivateFieldGet(this, _UiPath_authService, "f").authenticateWithSecret(config.secret);
|
|
5173
|
+
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
5174
|
+
}
|
|
5207
5175
|
}
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5176
|
+
/**
|
|
5177
|
+
* Initialize the SDK based on the provided configuration.
|
|
5178
|
+
* This method handles both OAuth flow initiation and completion automatically.
|
|
5179
|
+
* For secret-based authentication, initialization is automatic and this returns immediately.
|
|
5180
|
+
*/
|
|
5181
|
+
async initialize() {
|
|
5182
|
+
// For secret-based auth, it's already initialized in constructor
|
|
5183
|
+
if (hasSecretConfig(__classPrivateFieldGet(this, _UiPath_config, "f"))) {
|
|
5184
|
+
return;
|
|
5185
|
+
}
|
|
5186
|
+
try {
|
|
5187
|
+
// Check for OAuth callback first
|
|
5188
|
+
if (AuthService.isInOAuthCallback()) {
|
|
5189
|
+
if (await this.completeOAuth()) {
|
|
5190
|
+
return;
|
|
5191
|
+
}
|
|
5192
|
+
}
|
|
5193
|
+
// Check if already authenticated
|
|
5194
|
+
if (this.isAuthenticated()) {
|
|
5195
|
+
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
5196
|
+
return;
|
|
5197
|
+
}
|
|
5198
|
+
// Start new OAuth flow
|
|
5199
|
+
await __classPrivateFieldGet(this, _UiPath_authService, "f").authenticate(__classPrivateFieldGet(this, _UiPath_config, "f"));
|
|
5200
|
+
if (this.isAuthenticated()) {
|
|
5201
|
+
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
catch (error) {
|
|
5205
|
+
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
|
|
5206
|
+
throw new Error(`Failed to initialize UiPath SDK: ${errorMessage}`);
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
5209
|
+
/**
|
|
5210
|
+
* Check if the SDK has been initialized
|
|
5211
|
+
*/
|
|
5212
|
+
isInitialized() {
|
|
5213
|
+
return __classPrivateFieldGet(this, _UiPath_initialized, "f");
|
|
5214
|
+
}
|
|
5215
|
+
/**
|
|
5216
|
+
* Check if we're in an OAuth callback state
|
|
5217
|
+
*/
|
|
5218
|
+
isInOAuthCallback() {
|
|
5219
|
+
return AuthService.isInOAuthCallback();
|
|
5220
|
+
}
|
|
5221
|
+
/**
|
|
5222
|
+
* Complete OAuth authentication flow (only call if isInOAuthCallback() is true)
|
|
5223
|
+
*/
|
|
5224
|
+
async completeOAuth() {
|
|
5225
|
+
if (!AuthService.isInOAuthCallback()) {
|
|
5226
|
+
throw new Error('Not in OAuth callback state. Call initialize() first to start OAuth flow.');
|
|
5227
|
+
}
|
|
5228
|
+
try {
|
|
5229
|
+
const success = await __classPrivateFieldGet(this, _UiPath_authService, "f").authenticate(__classPrivateFieldGet(this, _UiPath_config, "f"));
|
|
5230
|
+
if (success && this.isAuthenticated()) {
|
|
5231
|
+
__classPrivateFieldSet(this, _UiPath_initialized, true, "f");
|
|
5232
|
+
return true;
|
|
5233
|
+
}
|
|
5234
|
+
return false;
|
|
5235
|
+
}
|
|
5236
|
+
catch (error) {
|
|
5237
|
+
const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
|
|
5238
|
+
throw new Error(`Failed to complete OAuth: ${errorMessage}`);
|
|
5239
|
+
}
|
|
5240
|
+
}
|
|
5241
|
+
/**
|
|
5242
|
+
* Check if the user is authenticated (has valid token)
|
|
5243
|
+
*/
|
|
5244
|
+
isAuthenticated() {
|
|
5245
|
+
return __classPrivateFieldGet(this, _UiPath_authService, "f").hasValidToken();
|
|
5246
|
+
}
|
|
5247
|
+
/**
|
|
5248
|
+
* Get the current authentication token
|
|
5249
|
+
*/
|
|
5250
|
+
getToken() {
|
|
5251
|
+
return __classPrivateFieldGet(this, _UiPath_authService, "f").getToken();
|
|
5212
5252
|
}
|
|
5213
|
-
return {
|
|
5214
|
-
message: String(error)
|
|
5215
|
-
};
|
|
5216
5253
|
}
|
|
5254
|
+
_UiPath_config = new WeakMap(), _UiPath_authService = new WeakMap(), _UiPath_initialized = new WeakMap();
|
|
5217
5255
|
|
|
5218
5256
|
/**
|
|
5219
5257
|
* Constants used throughout the pagination system
|