Skip to content

json stringify and check for both response.json or response.data #189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,7 @@
"strict": ["error", "safe"],
"prefer-object-spread": "off",
"no-param-reassign": "off",
"comma-dangle": [
"error",
{
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "never"
}
],
"comma-dangle": ["error", "always-multiline"],
"no-underscore-dangle": "off",
"no-unused-expressions": "off"
},
Expand Down
28 changes: 23 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
# gitignore

# OS generated files
.DS_Store
.idea
.idea/
.nyc_output
.vscode/

# Build and test outputs
.nyc_output
coverage
bower_components
npm-debug.*
node_modules
index.js
src/index.js
sample/node_modules

# Debug logs
npm-debug.*
*.log
logs/
src/logs/
sample/logs/
**/logs/

# Environment and config files
sample/.env
oauth-jsclient.iml

# Package files
package-lock.json
yarn.lock
src/logs/*

# Source files
index.js
src/index.js

# Other
.qodo
191 changes: 191 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,197 @@
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=shield)](https://github.com/prettier/prettier)
[![Known Vulnerabilities](https://snyk.io/test/github/intuit/oauth-jsclient/badge.svg)](https://snyk.io/test/github/intuit/oauth-jsclient)

# OAuth Client for Intuit

A Node.js client for Intuit's OAuth 2.0 implementation.

## Features

- OAuth 2.0 authentication flow
- Token management and refresh
- API request handling
- Error handling with custom error types
- Automatic retry for transient errors
- Structured logging
- Response validation

## Installation

```bash
npm install oauth-jsclient
```

## Usage

```javascript
const OAuthClient = require('oauth-jsclient');

const oauthClient = new OAuthClient({
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
environment: 'sandbox', // or 'production'
redirectUri: 'http://localhost:8000/callback',
logging: true // Enable logging
});
```

## Error Handling

The client provides several custom error types for better error handling:

- `OAuthError`: Base error class for all OAuth related errors
- `NetworkError`: For network related errors
- `ValidationError`: For validation related errors
- `TokenError`: For token related errors

Example error handling:

```javascript
try {
await oauthClient.makeApiCall({ url: 'https://api.example.com' });
} catch (error) {
if (error instanceof TokenError) {
// Handle token errors
console.error('Token error:', error.code, error.description);
} else if (error instanceof NetworkError) {
// Handle network errors
console.error('Network error:', error.message);
} else if (error instanceof ValidationError) {
// Handle validation errors
console.error('Validation error:', error.message);
} else {
// Handle other errors
console.error('Unexpected error:', error);
}
}
```

## Retry Logic

The client includes automatic retry logic for transient errors:

- Maximum 3 retries
- Exponential backoff (1s, 2s, 4s)
- Retries on specific status codes (408, 429, 500, 502, 503, 504)
- Retries on network errors (ECONNRESET, ETIMEDOUT, ECONNREFUSED)

You can configure retry behavior:

```javascript
OAuthClient.retryConfig = {
maxRetries: 3,
retryDelay: 1000,
retryableStatusCodes: [408, 429, 500, 502, 503, 504],
retryableErrors: ['ECONNRESET', 'ETIMEDOUT', 'ECONNREFUSED']
};
```

## Logging

The client provides structured logging when enabled:

```javascript
const oauthClient = new OAuthClient({
// ... other config
logging: true
});
```

Log entries include:
- Timestamp
- Log level
- Message
- Request context (URL, method, headers)
- Error details (for error logs)
- Environment information
- Client ID

Example log entry:
```json
{
"timestamp": "2024-03-14T12:00:00.000Z",
"level": "error",
"message": "API call failed",
"data": {
"error": {
"name": "TokenError",
"code": "UNAUTHORIZED",
"message": "Invalid or expired access token",
"stack": "...",
"intuit_tid": "1234-1234-1234-123"
}
},
"environment": "sandbox",
"clientId": "your_client_id",
"request": {
"url": "https://api.example.com",
"method": "GET",
"headers": {
"Authorization": "Bearer ...",
"Accept": "application/json"
}
}
}
```

## Response Validation

The client validates responses and throws appropriate errors for common scenarios:

- 401 Unauthorized: Invalid or expired access token
- 403 Forbidden: Insufficient permissions
- 429 Too Many Requests: Rate limit exceeded
- Missing or invalid response data
- Invalid content types

## API Reference

### OAuthClient

#### constructor(config)
Creates a new OAuthClient instance.

```javascript
const oauthClient = new OAuthClient({
clientId: 'your_client_id',
clientSecret: 'your_client_secret',
environment: 'sandbox',
redirectUri: 'http://localhost:8000/callback',
logging: true
});
```

#### makeApiCall(params)
Makes an API call with automatic retry and error handling.

```javascript
const response = await oauthClient.makeApiCall({
url: 'https://api.example.com',
method: 'GET',
headers: {
'Custom-Header': 'value'
},
body: {
key: 'value'
}
});
```

#### validateResponse(response)
Validates an API response and throws appropriate errors.

```javascript
try {
oauthClient.validateResponse(response);
} catch (error) {
// Handle validation errors
}
```

## License

Apache License 2.0

# Intuit OAuth2.0 NodeJS Library

The OAuth2 Nodejs Client library is meant to work with Intuit's
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "intuit-oauth",
"version": "4.2.0",
"version": "4.2.1",
"description": "Intuit Node.js client for OAuth2.0 and OpenIDConnect",
"main": "./src/OAuthClient.js",
"scripts": {
Expand Down Expand Up @@ -68,14 +68,15 @@
"homepage": "https://github.com/intuit/oauth-jsclient",
"dependencies": {
"atob": "2.1.2",
"axios": "^1.5.1",
"axios": "^1.9.0",
"csrf": "^3.0.4",
"jsonwebtoken": "^9.0.2",
"query-string": "^6.12.1",
"rsa-pem-from-mod-exp": "^0.8.4",
"winston": "^3.1.0"
},
"devDependencies": {
"@snyk/protect": "^1.657.0",
"btoa": "^1.2.1",
"chai": "^4.1.2",
"chai-as-promised": "^7.1.1",
Expand All @@ -87,8 +88,7 @@
"nock": "^9.2.3",
"nyc": "^15.0.1",
"prettier": "^2.0.5",
"sinon": "^9.0.2",
"@snyk/protect": "^1.657.0"
"sinon": "^9.0.2"
},
"snyk": true
}
1 change: 1 addition & 0 deletions sample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.log
65 changes: 62 additions & 3 deletions sample/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,70 @@ app.get('/getCompanyInfo', function (req, res) {
oauthClient
.makeApiCall({ url: `${url}v3/company/${companyID}/companyinfo/${companyID}` })
.then(function (authResponse) {
console.log(`\n The response for API call is :${JSON.stringify(authResponse.json)}`);
res.send(authResponse.json);
const resp = authResponse.json ? authResponse.json : authResponse.data;
console.log(`\n The response for API call is :${JSON.stringify(resp)}`);
res.send(resp);
})
.catch(function (e) {
console.error(e);
// Detailed error analysis
const errorAnalysis = {
// Basic error properties
basic: {
name: e.name,
message: e.message,
stack: e.stack,
code: e.code
},
// Response analysis
response: e.response ? {
status: e.response.status,
statusText: e.response.statusText,
headers: JSON.stringify(e.response.headers),
// Deep analysis of response data
data: JSON.stringify(e.response.data),
// Specific Fault object analysis
fault: JSON.stringify(e.response.data && e.response.data.Fault ? {
type: e.response.data.Fault.type,
error: e.response.data.Fault.Error ? e.response.data.Fault.Error.map(err => ({
message: err.Message,
detail: err.Detail,
code: err.code,
element: err.element,
additionalInfo: err.additionalInfo
})) : null,
timestamp: e.response.data.time
} : null),
// OAuth error fields
oauth: {
error:e.response.data && e.response.data.error,
error_description: e.response.data && e.response.data.error_description
}
} : null,
// Request analysis
request: e.request ? {
method: e.request.method,
path: e.request.path,
headers: e.request.headers
} : null
};

// Log the detailed error analysis
console.error('Exception Analysis:', {
hasFaultObject: !!(e.response && e.response.data && e.response.data.Fault),
faultType: e.response && e.response.data && e.response.data.Fault && e.response.data.Fault.type,
faultErrors: e.response && e.response.data && e.response.data.Fault && e.response.data.Fault.Error,
fullAnalysis: errorAnalysis
});

// Send error response to client
res.status(e.response ? e.response.status : 500).json({
error: true,
message: e.message,
fault: e.response && e.response.data && e.response.data.Fault ? {
type: e.response.data.Fault.type,
errors: e.response.data.Fault.Error
} : null
});
});
});

Expand Down
Loading