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