1+ import errorFormatter from './contentstackError' ;
2+
13/**
24 * @description OAuthHandler class to handle OAuth authorization and token management
35 * @class OAuthHandler
@@ -61,8 +63,8 @@ export default class OAuthHandler {
6163 const base64String = btoa ( String . fromCharCode ( ...hashArray ) ) ;
6264 return base64String . replace ( / \+ / g, '-' ) . replace ( / \/ / g, '_' ) . replace ( / = + $ / , '' ) ; // URL-safe Base64
6365 } else {
64- // In Node.js: Use the native `crypto` module for hashing
65- const crypto = require ( 'crypto' ) ;
66+ // In Node.js: Use the native `crypto` module for hashing
67+ const crypto = require ( 'crypto' ) ;
6668
6769 const hash = crypto . createHash ( 'sha256' ) ;
6870 hash . update ( codeVerifier ) ;
@@ -71,8 +73,8 @@ export default class OAuthHandler {
7173 // Convert to Base64 and URL-safe Base64
7274 let base64String = hashBuffer . toString ( 'base64' ) ;
7375 return base64String . replace ( / \+ / g, '-' ) . replace ( / \/ / g, '_' ) . replace ( / = + $ / , '' ) ; // URL-safe Base64
74- }
75- }
76+ }
77+ }
7678
7779 /**
7880 * @description Authorize the user by redirecting to the OAuth provider's authorization page
@@ -86,18 +88,22 @@ export default class OAuthHandler {
8688 * const authUrl = await oauthHandler.authorize();
8789 */
8890 async authorize ( ) {
89- const baseUrl = `${ this . OAuthBaseURL } /#!/apps/${ this . appId } /authorize` ;
90- const authUrl = new URL ( baseUrl ) ;
91- authUrl . searchParams . set ( 'response_type' , 'code' ) ; // Using set() to avoid duplicate parameters
92- authUrl . searchParams . set ( 'client_id' , this . clientId ) ;
93- if ( this . clientSecret ) {
94- return authUrl . toString ( ) ;
95- } else {
96- // PKCE flow: add code_challenge to the authorization URL
97- this . codeChallenge = await this . generateCodeChallenge ( this . codeVerifier ) ;
98- authUrl . searchParams . set ( 'code_challenge' , this . codeChallenge ) ;
99- authUrl . searchParams . set ( 'code_challenge_method' , 'S256' ) ;
100- return authUrl . toString ( ) ;
91+ try {
92+ const baseUrl = `${ this . OAuthBaseURL } /#!/apps/${ this . appId } /authorize` ;
93+ const authUrl = new URL ( baseUrl ) ;
94+ authUrl . searchParams . set ( 'response_type' , 'code' ) ; // Using set() to avoid duplicate parameters
95+ authUrl . searchParams . set ( 'client_id' , this . clientId ) ;
96+ if ( this . clientSecret ) {
97+ return authUrl . toString ( ) ;
98+ } else {
99+ // PKCE flow: add code_challenge to the authorization URL
100+ this . codeChallenge = await this . generateCodeChallenge ( this . codeVerifier ) ;
101+ authUrl . searchParams . set ( 'code_challenge' , this . codeChallenge ) ;
102+ authUrl . searchParams . set ( 'code_challenge_method' , 'S256' ) ;
103+ return authUrl . toString ( ) ;
104+ }
105+ } catch ( error ) {
106+ errorFormatter ( error ) ;
101107 }
102108 }
103109
@@ -125,14 +131,11 @@ export default class OAuthHandler {
125131 this . axiosInstance . defaults . headers = this . _getHeaders ( ) ;
126132 try {
127133 const response = await this . axiosInstance . post ( `${ this . OAuthBaseURL } /apps-api/apps/token` , body ) ;
128- if ( response . status !== 200 ) {
129- throw new Error ( 'Failed to exchange authorization code for token' ) ;
130- }
131134
132135 this . _saveTokens ( response . data ) ;
133136 return response . data ;
134137 } catch ( error ) {
135- throw new Error ( 'Failed to exchange authorization code for token:' , error ) ;
138+ errorFormatter ( error ) ;
136139 }
137140 }
138141
@@ -176,17 +179,13 @@ export default class OAuthHandler {
176179 try {
177180 const response = await this . axiosInstance . post ( `${ this . developerHubBaseUrl } /apps/token` , body ) ;
178181
179- if ( response . status !== 200 ) {
180- throw new Error ( `Failed to refresh access token. Status: ${ response . status } ` ) ;
181- }
182-
183182 const data = response . data ;
184183 this . axiosInstance . oauth . accessToken = data . access_token ;
185184 this . axiosInstance . oauth . refreshToken = data . refresh_token || this . axiosInstance . oauth . refreshToken ; // Optionally update refresh token
186185 this . axiosInstance . oauth . tokenExpiryTime = Date . now ( ) + ( data . expires_in - 60 ) * 1000 ; // Update expiry time
187186 return data ;
188187 } catch ( error ) {
189- throw new Error ( 'Failed to refresh access token:' , error ) ;
188+ errorFormatter ( error ) ;
190189 }
191190 }
192191
@@ -206,13 +205,10 @@ export default class OAuthHandler {
206205 try {
207206 const authorizationId = await this . getOauthAppAuthorization ( ) ;
208207 await this . revokeOauthAppAuthorization ( authorizationId ) ;
209- this . axiosInstance . oauth . accessToken = null ;
210- this . axiosInstance . oauth . refreshToken = null ;
211- this . axiosInstance . oauth . tokenExpiryTime = null ;
212- delete this . axiosInstance . oauth ;
208+ this . axiosInstance . oauth = { } ; // Clear OAuth data
213209 return 'Logged out successfully' ;
214210 } catch ( error ) {
215- throw new Error ( 'Failed to log out:' , error ) ;
211+ errorFormatter ( error ) ;
216212 }
217213 }
218214
@@ -231,6 +227,156 @@ export default class OAuthHandler {
231227 return this . axiosInstance . oauth . accessToken ;
232228 }
233229
230+ /**
231+ * @description Get the refresh token from the axios instance
232+ * @memberof OAuthHandler
233+ * @func getRefreshToken
234+ * @returns {string|null }
235+ * @example
236+ * import * as contentstack from '@contentstack/management'
237+ * const client = contentstack.client();
238+ * const oauthHandler = client.oauth({ appId: 'appId', clientId: 'clientId', redirectUri: 'http://localhost:8184' });
239+ * const refreshToken = oauthHandler.getRefreshToken();
240+ */
241+ getRefreshToken ( ) {
242+ return this . axiosInstance . oauth . refreshToken || null ;
243+ }
244+
245+ /**
246+ * @description Get the organization UID from the axios instance
247+ * @memberof OAuthHandler
248+ * @func getOrganizationUID
249+ * @returns {string|null }
250+ * @example
251+ * import * as contentstack from '@contentstack/management'
252+ * const client = contentstack.client();
253+ * const oauthHandler = client.oauth({ appId: 'appId', clientId: 'clientId', redirectUri: 'http://localhost:8184' });
254+ * const orgUID = oauthHandler.getOrganizationUID();
255+ */
256+ getOrganizationUID ( ) {
257+ return this . axiosInstance . oauth . organizationUID || null ;
258+ }
259+
260+ /**
261+ * @description Get the user UID from the axios instance
262+ * @memberof OAuthHandler
263+ * @func getUserUID
264+ * @returns {string|null }
265+ * @example
266+ * import * as contentstack from '@contentstack/management'
267+ * const client = contentstack.client();
268+ * const oauthHandler = client.oauth({ appId: 'appId', clientId: 'clientId', redirectUri: 'http://localhost:8184' });
269+ * const userId = oauthHandler.getUserUID();
270+ */
271+ getUserUID ( ) {
272+ return this . axiosInstance . oauth . userUID || null ;
273+ }
274+
275+ /**
276+ * @description Get the token expiry time from the axios instance
277+ * @memberof OAuthHandler
278+ * @func getTokenExpiryTime
279+ * @returns {number|null }
280+ * @example
281+ * import * as contentstack from '@contentstack/management'
282+ * const client = contentstack.client();
283+ * const oauthHandler = client.oauth({ appId: 'appId', clientId: 'clientId', redirectUri: 'http://localhost:8184' });
284+ * const expiryTime = oauthHandler.getTokenExpiryTime();
285+ */
286+ getTokenExpiryTime ( ) {
287+ return this . axiosInstance . oauth . tokenExpiryTime || null ;
288+ }
289+
290+ /**
291+ * @description Set the access token in the axios instance
292+ * @memberof OAuthHandler
293+ * @func setAccessToken
294+ * @param {* } token
295+ * @example
296+ * import * as contentstack from '@contentstack/management'
297+ * const client = contentstack.client();
298+ * const oauthHandler = client.oauth({ appId: 'appId', clientId: 'clientId', redirectUri: 'http://localhost:8184' });
299+ * oauthHandler.setAccessToken('accessToken');
300+ */
301+ setAccessToken ( token ) {
302+ if ( ! token ) {
303+ throw new Error ( 'Access token is required' ) ;
304+ }
305+ this . axiosInstance . oauth . accessToken = token ;
306+ }
307+
308+ /**
309+ * @description Set the refresh token in the axios instance
310+ * @memberof OAuthHandler
311+ * @func setRefreshToken
312+ * @param {* } token
313+ * @example
314+ * import * as contentstack from '@contentstack/management'
315+ * const client = contentstack.client();
316+ * const oauthHandler = client.oauth({ appId: 'appId', clientId: 'clientId', redirectUri: 'http://localhost:8184' });
317+ * oauthHandler.setRefreshToken('refreshToken');
318+ */
319+ setRefreshToken ( token ) {
320+ if ( ! token ) {
321+ throw new Error ( 'Refresh token is required' ) ;
322+ }
323+ this . axiosInstance . oauth . refreshToken = token ;
324+ }
325+
326+ /**
327+ * @description Set the organization UID in the axios instance
328+ * @memberof OAuthHandler
329+ * @func setOrganizationUID
330+ * @param {* } organizationUID
331+ * @example
332+ * import * as contentstack from '@contentstack/management'
333+ * const client = contentstack.client();
334+ * const oauthHandler = client.oauth({ appId: 'appId', clientId: 'clientId', redirectUri: 'http://localhost:8184' });
335+ * oauthHandler.setOrganizationUID('organizationUID');
336+ */
337+ setOrganizationUID ( organizationUID ) {
338+ if ( ! organizationUID ) {
339+ throw new Error ( 'Organization UID is required' ) ;
340+ }
341+ this . axiosInstance . oauth . organizationUID = organizationUID ;
342+ }
343+
344+ /**
345+ * @description Set the user UID in the axios instance
346+ * @memberof OAuthHandler
347+ * @func setUserUID
348+ * @param {* } userUID
349+ * @example
350+ * import * as contentstack from '@contentstack/management'
351+ * const client = contentstack.client();
352+ * const oauthHandler = client.oauth({ appId: 'appId', clientId: 'clientId', redirectUri: 'http://localhost:8184' });
353+ * oauthHandler.setUserUID('userID');
354+ */
355+ setUserUID ( userUID ) {
356+ if ( ! userUID ) {
357+ throw new Error ( 'User UID is required' ) ;
358+ }
359+ this . axiosInstance . oauth . userUID = userUID ;
360+ }
361+
362+ /**
363+ * @description Set the token expiry time in the axios instance
364+ * @memberof OAuthHandler
365+ * @func setTokenExpiryTime
366+ * @param {* } expiryTime
367+ * @example
368+ * import * as contentstack from '@contentstack/management'
369+ * const client = contentstack.client();
370+ * const oauthHandler = client.oauth({ appId: 'appId', clientId: 'clientId', redirectUri: 'http://localhost:8184' });
371+ * oauthHandler.setTokenExpiryTime('expiryTime'); // date format
372+ */
373+ setTokenExpiryTime ( expiryTime ) {
374+ if ( ! expiryTime ) {
375+ throw new Error ( 'Token expiry time is required' ) ;
376+ }
377+ this . axiosInstance . oauth . tokenExpiryTime = expiryTime ;
378+ }
379+
234380 /**
235381 * @description Handles the redirect URL after OAuth authorization
236382 * @memberof OAuthHandler
@@ -253,7 +399,7 @@ export default class OAuthHandler {
253399 try {
254400 await this . exchangeCodeForToken ( code ) ;
255401 } catch ( error ) {
256- throw new Error ( 'OAuth Authentication failed:' , error ) ;
402+ errorFormatter ( error ) ;
257403 }
258404 } else {
259405 throw new Error ( 'Authorization code not found in redirect URL.' ) ;
@@ -291,7 +437,7 @@ export default class OAuthHandler {
291437 throw new Error ( 'No authorizations found for the app!' ) ;
292438 }
293439 } catch ( error ) {
294- throw new Error ( 'Failed to get authorizations:' , error ) ;
440+ errorFormatter ( error ) ;
295441 }
296442 }
297443
@@ -318,7 +464,7 @@ export default class OAuthHandler {
318464
319465 return res . data ;
320466 } catch ( error ) {
321- throw new Error ( 'Failed to revoke authorization:' , error ) ;
467+ errorFormatter ( error ) ;
322468 }
323469 }
324470 }
0 commit comments