Skip to content

Commit

Permalink
[AXON-46] chore: add unit tests for auth strategies
Browse files Browse the repository at this point in the history
(to avoid creating problems when changing this code)
  • Loading branch information
sdzh-atlassian committed Feb 13, 2025
1 parent 7ecc8da commit 00b78fa
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 19 deletions.
103 changes: 103 additions & 0 deletions src/atlclients/strategy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// mock crypto.randomBytes
jest.mock('./strategyCrypto', () => {
return {
createVerifier: jest.fn(() => 'verifier'),
base64URLEncode: jest.fn(() => 'base64URLEncode'),
sha256: jest.fn(() => 'sha256'),
basicAuth: jest.fn(() => 'basicAuth'),
};
});

import { OAuthProvider } from './authInfo';
import { strategyForProvider } from './strategy';

const expectedData = {
bbcloud: {
provider: 'bbcloud',
authorizeUrl:
'https://bitbucket.org/site/oauth2/authorize?client_id=3hasX42a7Ugka2FJja&response_type=code&state=state',
accessibleResourcesUrl: '',
tokenAuthorizationData: 'grant_type=authorization_code&code=code',
tokenUrl: 'https://bitbucket.org/site/oauth2/access_token',
apiUrl: 'https://bitbucket.org',
refreshHeaders: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: 'basicAuth',
},
tokenRefreshData: 'grant_type=refresh_token&refresh_token=refreshToken',
profileUrl: 'https://api.bitbucket.org/2.0/user',
emailsUrl: 'https://api.bitbucket.org/2.0/user/emails',
},
bbcloudstaging: {
provider: 'bbcloudstaging',
authorizeUrl:
'https://staging.bb-inf.net/site/oauth2/authorize?client_id=7jspxC7fgemuUbnWQL&response_type=code&state=state',
accessibleResourcesUrl: '',
tokenAuthorizationData: 'grant_type=authorization_code&code=code',
tokenUrl: 'https://staging.bb-inf.net/site/oauth2/access_token',
apiUrl: 'https://staging.bb-inf.net',
refreshHeaders: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: 'basicAuth',
},
tokenRefreshData: 'grant_type=refresh_token&refresh_token=refreshToken',
profileUrl: 'https://api-staging.bb-inf.net/2.0/user',
emailsUrl: 'https://api-staging.bb-inf.net/2.0/user/emails',
},
jiracloud: {
provider: 'jiracloud',
authorizeUrl:
'https://auth.atlassian.com/authorize?client_id=bJChVgBQd0aNUPuFZ8YzYBVZz3X4QTe2&redirect_uri=http%3A%2F%2F127.0.0.1%3A31415%2Fjiracloud&response_type=code&scope=read%3Ajira-user+read%3Ajira-work+write%3Ajira-work+offline_access+manage%3Ajira-project&audience=api.atlassian.com&prompt=consent&state=state&code_challenge=base64URLEncode&code_challenge_method=S256',
accessibleResourcesUrl: 'https://api.atlassian.com/oauth/token/accessible-resources',
tokenAuthorizationData:
'{"grant_type":"authorization_code","code":"code","redirect_uri":"http://127.0.0.1:31415/jiracloud","client_id":"bJChVgBQd0aNUPuFZ8YzYBVZz3X4QTe2","code_verifier":"verifier"}',
tokenUrl: 'https://auth.atlassian.com/oauth/token',
apiUrl: 'api.atlassian.com',
refreshHeaders: {
'Content-Type': 'application/json',
},
tokenRefreshData:
'{"grant_type":"refresh_token","client_id":"bJChVgBQd0aNUPuFZ8YzYBVZz3X4QTe2","refresh_token":"refreshToken"}',
profileUrl: '',
emailsUrl: '',
},
jiracloudstaging: {
provider: 'jiracloudstaging',
authorizeUrl:
'https://auth.stg.atlassian.com/authorize?client_id=pmzXmUav3Rr5XEL0Sie7Biec0WGU8BKg&redirect_uri=http%3A%2F%2F127.0.0.1%3A31415%2Fjiracloudstaging&response_type=code&scope=read%3Ajira-user+read%3Ajira-work+write%3Ajira-work+offline_access+manage%3Ajira-project&audience=api.stg.atlassian.com&prompt=consent&state=state&code_challenge=base64URLEncode&code_challenge_method=S256',
accessibleResourcesUrl: 'https://api.stg.atlassian.com/oauth/token/accessible-resources',
tokenAuthorizationData:
'{"grant_type":"authorization_code","code":"code","redirect_uri":"http://127.0.0.1:31415/jiracloudstaging","client_id":"pmzXmUav3Rr5XEL0Sie7Biec0WGU8BKg","code_verifier":"verifier"}',
tokenUrl: 'https://auth.stg.atlassian.com/oauth/token',
apiUrl: 'api.stg.atlassian.com',
refreshHeaders: {
'Content-Type': 'application/json',
},
tokenRefreshData:
'{"grant_type":"refresh_token","client_id":"pmzXmUav3Rr5XEL0Sie7Biec0WGU8BKg","refresh_token":"refreshToken"}',
profileUrl: '',
emailsUrl: '',
},
};

describe('Authentication strategies', () => {
it.each([
[OAuthProvider.BitbucketCloud],
[OAuthProvider.BitbucketCloudStaging],
[OAuthProvider.JiraCloud],
[OAuthProvider.JiraCloudStaging],
])('Strategy for provider %s yields expected results', (provider: OAuthProvider) => {
const expected = expectedData[provider] as any;
const strategy = strategyForProvider(provider);
expect(strategy.provider()).toBe(expected.provider);
expect(strategy.authorizeUrl('state')).toBe(expected.authorizeUrl);
expect(strategy.accessibleResourcesUrl()).toBe(expected.accessibleResourcesUrl);
expect(strategy.tokenAuthorizationData('code')).toBe(expected.tokenAuthorizationData);
expect(strategy.tokenUrl()).toBe(expected.tokenUrl);
expect(strategy.apiUrl()).toBe(expected.apiUrl);
expect(strategy.refreshHeaders()).toStrictEqual(expected.refreshHeaders);
expect(strategy.tokenRefreshData('refreshToken')).toBe(expected.tokenRefreshData);
expect(strategy.profileUrl()).toBe(expected.profileUrl);
expect(strategy.emailsUrl()).toBe(expected.emailsUrl);
});
});
24 changes: 5 additions & 19 deletions src/atlclients/strategy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { OAuthProvider } from './authInfo';
import crypto from 'crypto';
import { createVerifier, base64URLEncode, sha256, basicAuth } from './strategyCrypto';

const JiraProdStrategyData = {
clientID: 'bJChVgBQd0aNUPuFZ8YzYBVZz3X4QTe2',
Expand Down Expand Up @@ -90,7 +90,7 @@ class PKCEJiraProdStrategy extends Strategy {

public constructor() {
super();
this.verifier = base64URLEncode(crypto.randomBytes(32));
this.verifier = createVerifier();
}

public provider(): OAuthProvider {
Expand Down Expand Up @@ -151,20 +151,12 @@ class PKCEJiraProdStrategy extends Strategy {
}
}

function base64URLEncode(str: Buffer): string {
return str.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}

function sha256(buffer: any) {
return crypto.createHash('sha256').update(buffer).digest();
}

class PKCEJiraStagingStrategy extends Strategy {
private verifier: string;

public constructor() {
super();
this.verifier = base64URLEncode(crypto.randomBytes(32));
this.verifier = createVerifier();
}

public provider(): OAuthProvider {
Expand Down Expand Up @@ -258,12 +250,9 @@ class BitbucketProdStrategy extends Strategy {

// We kinda abuse refreshHeaders for bitbucket. Maybe have a authorizationHeaders as well? Just rename?
public refreshHeaders() {
const basicAuth = Buffer.from(
`${BitbucketProdStrategyData.clientID}:${BitbucketProdStrategyData.clientSecret}`,
).toString('base64');
return {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${basicAuth}`,
Authorization: basicAuth(BitbucketProdStrategyData.clientID, BitbucketProdStrategyData.clientSecret),
};
}

Expand Down Expand Up @@ -311,12 +300,9 @@ class BitbucketStagingStrategy extends Strategy {
}

public refreshHeaders() {
const basicAuth = Buffer.from(
`${BitbucketStagingStrategyData.clientID}:${BitbucketStagingStrategyData.clientSecret}`,
).toString('base64');
return {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${basicAuth}`,
Authorization: basicAuth(BitbucketStagingStrategyData.clientID, BitbucketStagingStrategyData.clientSecret),
};
}

Expand Down
20 changes: 20 additions & 0 deletions src/atlclients/strategyCrypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import crypto from 'crypto';

// for some reason jest doesn't play nice with crypto,
// so these are now in a separate file for easy mocking

export function createVerifier() {
return base64URLEncode(crypto.randomBytes(32));
}

export function base64URLEncode(str: Buffer): string {
return str.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}

export function sha256(buffer: any) {
return crypto.createHash('sha256').update(buffer).digest();
}

export function basicAuth(username: string, password: string) {
return 'Basic ' + Buffer.from(username + ':' + password).toString('base64');
}

0 comments on commit 00b78fa

Please sign in to comment.