Skip to content

Commit aa892ba

Browse files
committed
feat: added getter & setter methods for access token, refresh
1 parent fb32783 commit aa892ba

File tree

1 file changed

+179
-33
lines changed

1 file changed

+179
-33
lines changed

lib/core/oauthHandler.js

Lines changed: 179 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
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

Comments
 (0)