Skip to content

Commit 00b78fa

Browse files
[AXON-46] chore: add unit tests for auth strategies
(to avoid creating problems when changing this code)
1 parent 7ecc8da commit 00b78fa

File tree

3 files changed

+128
-19
lines changed

3 files changed

+128
-19
lines changed

src/atlclients/strategy.test.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// mock crypto.randomBytes
2+
jest.mock('./strategyCrypto', () => {
3+
return {
4+
createVerifier: jest.fn(() => 'verifier'),
5+
base64URLEncode: jest.fn(() => 'base64URLEncode'),
6+
sha256: jest.fn(() => 'sha256'),
7+
basicAuth: jest.fn(() => 'basicAuth'),
8+
};
9+
});
10+
11+
import { OAuthProvider } from './authInfo';
12+
import { strategyForProvider } from './strategy';
13+
14+
const expectedData = {
15+
bbcloud: {
16+
provider: 'bbcloud',
17+
authorizeUrl:
18+
'https://bitbucket.org/site/oauth2/authorize?client_id=3hasX42a7Ugka2FJja&response_type=code&state=state',
19+
accessibleResourcesUrl: '',
20+
tokenAuthorizationData: 'grant_type=authorization_code&code=code',
21+
tokenUrl: 'https://bitbucket.org/site/oauth2/access_token',
22+
apiUrl: 'https://bitbucket.org',
23+
refreshHeaders: {
24+
'Content-Type': 'application/x-www-form-urlencoded',
25+
Authorization: 'basicAuth',
26+
},
27+
tokenRefreshData: 'grant_type=refresh_token&refresh_token=refreshToken',
28+
profileUrl: 'https://api.bitbucket.org/2.0/user',
29+
emailsUrl: 'https://api.bitbucket.org/2.0/user/emails',
30+
},
31+
bbcloudstaging: {
32+
provider: 'bbcloudstaging',
33+
authorizeUrl:
34+
'https://staging.bb-inf.net/site/oauth2/authorize?client_id=7jspxC7fgemuUbnWQL&response_type=code&state=state',
35+
accessibleResourcesUrl: '',
36+
tokenAuthorizationData: 'grant_type=authorization_code&code=code',
37+
tokenUrl: 'https://staging.bb-inf.net/site/oauth2/access_token',
38+
apiUrl: 'https://staging.bb-inf.net',
39+
refreshHeaders: {
40+
'Content-Type': 'application/x-www-form-urlencoded',
41+
Authorization: 'basicAuth',
42+
},
43+
tokenRefreshData: 'grant_type=refresh_token&refresh_token=refreshToken',
44+
profileUrl: 'https://api-staging.bb-inf.net/2.0/user',
45+
emailsUrl: 'https://api-staging.bb-inf.net/2.0/user/emails',
46+
},
47+
jiracloud: {
48+
provider: 'jiracloud',
49+
authorizeUrl:
50+
'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',
51+
accessibleResourcesUrl: 'https://api.atlassian.com/oauth/token/accessible-resources',
52+
tokenAuthorizationData:
53+
'{"grant_type":"authorization_code","code":"code","redirect_uri":"http://127.0.0.1:31415/jiracloud","client_id":"bJChVgBQd0aNUPuFZ8YzYBVZz3X4QTe2","code_verifier":"verifier"}',
54+
tokenUrl: 'https://auth.atlassian.com/oauth/token',
55+
apiUrl: 'api.atlassian.com',
56+
refreshHeaders: {
57+
'Content-Type': 'application/json',
58+
},
59+
tokenRefreshData:
60+
'{"grant_type":"refresh_token","client_id":"bJChVgBQd0aNUPuFZ8YzYBVZz3X4QTe2","refresh_token":"refreshToken"}',
61+
profileUrl: '',
62+
emailsUrl: '',
63+
},
64+
jiracloudstaging: {
65+
provider: 'jiracloudstaging',
66+
authorizeUrl:
67+
'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',
68+
accessibleResourcesUrl: 'https://api.stg.atlassian.com/oauth/token/accessible-resources',
69+
tokenAuthorizationData:
70+
'{"grant_type":"authorization_code","code":"code","redirect_uri":"http://127.0.0.1:31415/jiracloudstaging","client_id":"pmzXmUav3Rr5XEL0Sie7Biec0WGU8BKg","code_verifier":"verifier"}',
71+
tokenUrl: 'https://auth.stg.atlassian.com/oauth/token',
72+
apiUrl: 'api.stg.atlassian.com',
73+
refreshHeaders: {
74+
'Content-Type': 'application/json',
75+
},
76+
tokenRefreshData:
77+
'{"grant_type":"refresh_token","client_id":"pmzXmUav3Rr5XEL0Sie7Biec0WGU8BKg","refresh_token":"refreshToken"}',
78+
profileUrl: '',
79+
emailsUrl: '',
80+
},
81+
};
82+
83+
describe('Authentication strategies', () => {
84+
it.each([
85+
[OAuthProvider.BitbucketCloud],
86+
[OAuthProvider.BitbucketCloudStaging],
87+
[OAuthProvider.JiraCloud],
88+
[OAuthProvider.JiraCloudStaging],
89+
])('Strategy for provider %s yields expected results', (provider: OAuthProvider) => {
90+
const expected = expectedData[provider] as any;
91+
const strategy = strategyForProvider(provider);
92+
expect(strategy.provider()).toBe(expected.provider);
93+
expect(strategy.authorizeUrl('state')).toBe(expected.authorizeUrl);
94+
expect(strategy.accessibleResourcesUrl()).toBe(expected.accessibleResourcesUrl);
95+
expect(strategy.tokenAuthorizationData('code')).toBe(expected.tokenAuthorizationData);
96+
expect(strategy.tokenUrl()).toBe(expected.tokenUrl);
97+
expect(strategy.apiUrl()).toBe(expected.apiUrl);
98+
expect(strategy.refreshHeaders()).toStrictEqual(expected.refreshHeaders);
99+
expect(strategy.tokenRefreshData('refreshToken')).toBe(expected.tokenRefreshData);
100+
expect(strategy.profileUrl()).toBe(expected.profileUrl);
101+
expect(strategy.emailsUrl()).toBe(expected.emailsUrl);
102+
});
103+
});

src/atlclients/strategy.ts

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { OAuthProvider } from './authInfo';
2-
import crypto from 'crypto';
2+
import { createVerifier, base64URLEncode, sha256, basicAuth } from './strategyCrypto';
33

44
const JiraProdStrategyData = {
55
clientID: 'bJChVgBQd0aNUPuFZ8YzYBVZz3X4QTe2',
@@ -90,7 +90,7 @@ class PKCEJiraProdStrategy extends Strategy {
9090

9191
public constructor() {
9292
super();
93-
this.verifier = base64URLEncode(crypto.randomBytes(32));
93+
this.verifier = createVerifier();
9494
}
9595

9696
public provider(): OAuthProvider {
@@ -151,20 +151,12 @@ class PKCEJiraProdStrategy extends Strategy {
151151
}
152152
}
153153

154-
function base64URLEncode(str: Buffer): string {
155-
return str.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
156-
}
157-
158-
function sha256(buffer: any) {
159-
return crypto.createHash('sha256').update(buffer).digest();
160-
}
161-
162154
class PKCEJiraStagingStrategy extends Strategy {
163155
private verifier: string;
164156

165157
public constructor() {
166158
super();
167-
this.verifier = base64URLEncode(crypto.randomBytes(32));
159+
this.verifier = createVerifier();
168160
}
169161

170162
public provider(): OAuthProvider {
@@ -258,12 +250,9 @@ class BitbucketProdStrategy extends Strategy {
258250

259251
// We kinda abuse refreshHeaders for bitbucket. Maybe have a authorizationHeaders as well? Just rename?
260252
public refreshHeaders() {
261-
const basicAuth = Buffer.from(
262-
`${BitbucketProdStrategyData.clientID}:${BitbucketProdStrategyData.clientSecret}`,
263-
).toString('base64');
264253
return {
265254
'Content-Type': 'application/x-www-form-urlencoded',
266-
Authorization: `Basic ${basicAuth}`,
255+
Authorization: basicAuth(BitbucketProdStrategyData.clientID, BitbucketProdStrategyData.clientSecret),
267256
};
268257
}
269258

@@ -311,12 +300,9 @@ class BitbucketStagingStrategy extends Strategy {
311300
}
312301

313302
public refreshHeaders() {
314-
const basicAuth = Buffer.from(
315-
`${BitbucketStagingStrategyData.clientID}:${BitbucketStagingStrategyData.clientSecret}`,
316-
).toString('base64');
317303
return {
318304
'Content-Type': 'application/x-www-form-urlencoded',
319-
Authorization: `Basic ${basicAuth}`,
305+
Authorization: basicAuth(BitbucketStagingStrategyData.clientID, BitbucketStagingStrategyData.clientSecret),
320306
};
321307
}
322308

src/atlclients/strategyCrypto.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import crypto from 'crypto';
2+
3+
// for some reason jest doesn't play nice with crypto,
4+
// so these are now in a separate file for easy mocking
5+
6+
export function createVerifier() {
7+
return base64URLEncode(crypto.randomBytes(32));
8+
}
9+
10+
export function base64URLEncode(str: Buffer): string {
11+
return str.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
12+
}
13+
14+
export function sha256(buffer: any) {
15+
return crypto.createHash('sha256').update(buffer).digest();
16+
}
17+
18+
export function basicAuth(username: string, password: string) {
19+
return 'Basic ' + Buffer.from(username + ':' + password).toString('base64');
20+
}

0 commit comments

Comments
 (0)