Skip to content

Commit

Permalink
Merge pull request #87 from snyk-labs/feat/add-oauth-support
Browse files Browse the repository at this point in the history
Feat/add oauth support
  • Loading branch information
Robthreefold authored Dec 13, 2024
2 parents 93e7ed8 + 3858dd5 commit eba2a2d
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 16 deletions.
21 changes: 18 additions & 3 deletions src/lib/request/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const getTopParentModuleName = (parent: NodeModule | null): string => {
const makeSnykRequest = async (
request: SnykRequest,
snykToken = '',
oauthBearerToken = '',
apiUrl = DEFAULT_API,
apiUrlREST = DEFAULT_REST_API,
userAgentPrefix = '',
Expand All @@ -57,20 +58,31 @@ const makeSnykRequest = async (
userAgentPrefix != '' && !userAgentPrefix.endsWith('/')
? userAgentPrefix + '/'
: userAgentPrefix;

// prioritize snykToken but use oauthBearerToken if snykToken isn't provided
const authorizationToken = snykToken
? `token ${snykToken}`
: oauthBearerToken
? `Bearer ${oauthBearerToken}`
: '';

const requestHeaders: Record<string, any> = {

Check warning on line 69 in src/lib/request/request.ts

View workflow job for this annotation

GitHub Actions / build-test

Unexpected any. Specify a different type

Check warning on line 69 in src/lib/request/request.ts

View workflow job for this annotation

GitHub Actions / build-test

Unexpected any. Specify a different type
'Content-Type':
request.useRESTApi && request.body
? 'application/vnd.api+json'
: 'application/json',
Authorization: 'token ' + snykToken,
Authorization: authorizationToken,
'User-Agent': `${topParentModuleName}${userAgentPrefixChecked}tech-services/snyk-request-manager/1.0`,
};
let apiClient;
if (proxyUri) {
apiClient = axios.create({
baseURL: request.useRESTApi ? apiUrlREST : apiUrl,
responseType: 'json',
headers: { ...requestHeaders, ...request.headers },
headers: {
...requestHeaders,
...request.headers,
},
transitional: {
clarifyTimeoutError: true,
},
Expand All @@ -81,7 +93,10 @@ const makeSnykRequest = async (
apiClient = axios.create({
baseURL: request.useRESTApi ? apiUrlREST : apiUrl,
responseType: 'json',
headers: { ...requestHeaders, ...request.headers },
headers: {
...requestHeaders,
...request.headers,
},
transitional: {
clarifyTimeoutError: true,
},
Expand Down
55 changes: 42 additions & 13 deletions src/lib/request/requestManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ function getRESTAPI(endpoint: string): string {
return new URL(`${apiData.protocol}//${apiData.host}/rest`).toString();
}

function getOauthToken(): string {
const oauthToken: string = process.env.SNYK_OAUTH_TOKEN || '';
return oauthToken;
}

const getConfig = (): { endpoint: string; token: string } => {
const snykApiEndpoint: string =
process.env.SNYK_API ||
Expand All @@ -69,6 +74,7 @@ class RequestsManager {
_retryCounter: Map<string, number>;
_MAX_RETRY_COUNT: number;
_snykToken: string;
_oauthBearerToken?: string; // Optional OAuth token
_userAgentPrefix?: string;

//snykToken = '', burstSize = 10, period = 500, maxRetryCount = 5
Expand All @@ -82,6 +88,7 @@ class RequestsManager {
this._events = {};
this._retryCounter = new Map();
this._MAX_RETRY_COUNT = params?.maxRetryCount || 5;
this._oauthBearerToken = getOauthToken();
this._snykToken = params?.snykToken ?? this._userConfig.token;
this._apiUrl = this._userConfig.endpoint;
this._apiUrlREST = getRESTAPI(this._userConfig.endpoint);
Expand All @@ -101,19 +108,41 @@ class RequestsManager {
_makeRequest = async (request: QueuedRequest): Promise<void> => {
const requestId = request.id;
try {
const response = await makeSnykRequest(
request.snykRequest,
this._snykToken,
this._apiUrl,
this._apiUrlREST,
this._userAgentPrefix,
);
this._emit({
eventType: eventType.data,
channel: request.channel,
requestId,
data: response,
});
// Pass oauthBearerToken if available
if (
this._oauthBearerToken != null &&
this._oauthBearerToken.trim() != ''
) {
const response = await makeSnykRequest(
request.snykRequest,
'',
this._oauthBearerToken,
this._apiUrl,
this._apiUrlREST,
this._userAgentPrefix,
);
this._emit({
eventType: eventType.data,
channel: request.channel,
requestId,
data: response,
});
} else {
const response = await makeSnykRequest(
request.snykRequest,
this._snykToken,
'',
this._apiUrl,
this._apiUrlREST,
this._userAgentPrefix,
);
this._emit({
eventType: eventType.data,
channel: request.channel,
requestId,
data: response,
});
}
} catch (err) {
const overloadedError = requestsManagerError.requestsManagerErrorOverload(
err,
Expand Down
82 changes: 82 additions & 0 deletions test/lib/request/request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,85 @@ describe('Test Snyk Utils error handling/classification', () => {
}
});
});

describe('Test makeSnykRequest with oauthBearerToken', () => {
beforeEach(() => {
nock.cleanAll();
});

afterEach(() => {
nock.cleanAll();
});

it('should set Bearer token in Authorization header when oauthBearerToken is provided for DEFAULT_API', async () => {
const testToken = 'test-oauth-token';
const request = {
verb: 'GET',
url: '/test-endpoint',
};

const scope = nock('https://api.snyk.io/v1')
.get('/test-endpoint')
.matchHeader('Authorization', `Bearer ${testToken}`)
.reply(200, { success: true });

await makeSnykRequest(request, '', testToken);

expect(scope.isDone()).toBe(true);
});

it('should set Bearer token in Authorization header when oauthBearerToken is provided for DEFAULT_REST_API', async () => {
const testToken = 'test-oauth-token';
const request = {
verb: 'GET',
url: '/test-endpoint',
useRESTApi: true,
};

const scope = nock('https://api.snyk.io/rest/')
.get('/test-endpoint')
.matchHeader('Authorization', `Bearer ${testToken}`)
.reply(200, { success: true });

await makeSnykRequest(request, '', testToken);

expect(scope.isDone()).toBe(true);
});

it('should prioritize snykToken over oauthBearerToken when both are provided for DEFAULT_API', async () => {
const snykToken = 'test-snyk-token';
const oauthToken = 'test-oauth-token';
const request = {
verb: 'GET',
url: '/test-endpoint',
};

const scope = nock('https://api.snyk.io/v1')
.get('/test-endpoint')
.matchHeader('Authorization', `token ${snykToken}`)
.reply(200, { success: true });

await makeSnykRequest(request, snykToken, oauthToken);

expect(scope.isDone()).toBe(true);
});

it('should prioritize snykToken over oauthBearerToken when both are provided for DEFAULT_REST_API', async () => {
const snykToken = 'test-snyk-token';
const oauthToken = 'test-oauth-token';
const request = {
verb: 'GET',
url: '/test-endpoint',
useRESTApi: true,
};

const scope = nock('https://api.snyk.io/rest/')
.get('/test-endpoint')
.matchHeader('Authorization', `token ${snykToken}`)
.reply(200, { success: true });

await makeSnykRequest(request, snykToken, oauthToken);

expect(scope.isDone()).toBe(true);
});
});

0 comments on commit eba2a2d

Please sign in to comment.