@nooks-ai/nooks-zoominfo-api-auth-client 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/test.yml +19 -0
- package/index.test.js +99 -0
- package/package.json +7 -5
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
node-version: [20, 22, 24]
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: ${{ matrix.node-version }}
|
|
18
|
+
- run: npm i
|
|
19
|
+
- run: npm test
|
package/index.test.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const rs = require('jsrsasign');
|
|
3
|
+
|
|
4
|
+
jest.mock('axios');
|
|
5
|
+
jest.mock('jsrsasign');
|
|
6
|
+
|
|
7
|
+
const { getAccessTokenViaPKI, getAccessTokenViaBasicAuth } = require('./index');
|
|
8
|
+
|
|
9
|
+
const AUTH_URL = 'https://api.zoominfo.com/authenticate';
|
|
10
|
+
|
|
11
|
+
describe('getAccessTokenViaBasicAuth', () => {
|
|
12
|
+
afterEach(() => jest.resetAllMocks());
|
|
13
|
+
|
|
14
|
+
it('sends username and password and returns the JWT', async () => {
|
|
15
|
+
axios.post.mockResolvedValue({ data: { jwt: 'test-token-123' } });
|
|
16
|
+
|
|
17
|
+
const token = await getAccessTokenViaBasicAuth('user@example.com', 'pass123');
|
|
18
|
+
|
|
19
|
+
expect(axios.post).toHaveBeenCalledWith(AUTH_URL, {
|
|
20
|
+
username: 'user@example.com',
|
|
21
|
+
password: 'pass123',
|
|
22
|
+
});
|
|
23
|
+
expect(token).toBe('test-token-123');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('returns the error when the request fails', async () => {
|
|
27
|
+
const err = new Error('Network error');
|
|
28
|
+
axios.post.mockRejectedValue(err);
|
|
29
|
+
|
|
30
|
+
const result = await getAccessTokenViaBasicAuth('user@example.com', 'pass123');
|
|
31
|
+
|
|
32
|
+
expect(result).toBe(err);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('getAccessTokenViaPKI', () => {
|
|
37
|
+
afterEach(() => jest.resetAllMocks());
|
|
38
|
+
|
|
39
|
+
it('signs a JWT and sends it as a Bearer token', async () => {
|
|
40
|
+
rs.jws = { JWS: { sign: jest.fn().mockReturnValue('signed-jwt') } };
|
|
41
|
+
axios.post.mockResolvedValue({ data: { jwt: 'access-token-456' } });
|
|
42
|
+
|
|
43
|
+
const token = await getAccessTokenViaPKI('user@example.com', 'client-id', 'private-key');
|
|
44
|
+
|
|
45
|
+
// Verify JWS.sign was called with RS256
|
|
46
|
+
expect(rs.jws.JWS.sign).toHaveBeenCalledWith(
|
|
47
|
+
'RS256',
|
|
48
|
+
expect.any(String),
|
|
49
|
+
expect.any(String),
|
|
50
|
+
'private-key'
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Verify the JWT payload contains expected fields
|
|
54
|
+
const payloadArg = JSON.parse(rs.jws.JWS.sign.mock.calls[0][2]);
|
|
55
|
+
expect(payloadArg).toMatchObject({
|
|
56
|
+
aud: 'enterprise_api',
|
|
57
|
+
iss: 'zoominfo-api-auth-client-nodejs',
|
|
58
|
+
username: 'user@example.com',
|
|
59
|
+
client_id: 'client-id',
|
|
60
|
+
});
|
|
61
|
+
expect(payloadArg.iat).toBeDefined();
|
|
62
|
+
expect(payloadArg.exp).toBeDefined();
|
|
63
|
+
expect(payloadArg.exp).toBeGreaterThan(payloadArg.iat);
|
|
64
|
+
|
|
65
|
+
// Verify axios was called with the signed JWT
|
|
66
|
+
expect(axios.post).toHaveBeenCalledWith(
|
|
67
|
+
AUTH_URL,
|
|
68
|
+
{},
|
|
69
|
+
{ headers: { Authorization: 'Bearer signed-jwt' } }
|
|
70
|
+
);
|
|
71
|
+
expect(token).toBe('access-token-456');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('returns the error when the request fails', async () => {
|
|
75
|
+
rs.jws = { JWS: { sign: jest.fn().mockReturnValue('signed-jwt') } };
|
|
76
|
+
const err = new Error('Auth failed');
|
|
77
|
+
axios.post.mockRejectedValue(err);
|
|
78
|
+
|
|
79
|
+
const result = await getAccessTokenViaPKI('user@example.com', 'client-id', 'private-key');
|
|
80
|
+
|
|
81
|
+
expect(result).toBe(err);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('sets iat to ~60 seconds before now and exp to ~4 minutes after iat', async () => {
|
|
85
|
+
rs.jws = { JWS: { sign: jest.fn().mockReturnValue('signed-jwt') } };
|
|
86
|
+
axios.post.mockResolvedValue({ data: { jwt: 'token' } });
|
|
87
|
+
|
|
88
|
+
const before = Math.floor(Date.now() / 1000);
|
|
89
|
+
await getAccessTokenViaPKI('user@example.com', 'client-id', 'private-key');
|
|
90
|
+
const after = Math.floor(Date.now() / 1000);
|
|
91
|
+
|
|
92
|
+
const payload = JSON.parse(rs.jws.JWS.sign.mock.calls[0][2]);
|
|
93
|
+
// iat should be roughly now - 60
|
|
94
|
+
expect(payload.iat).toBeGreaterThanOrEqual(before - 61);
|
|
95
|
+
expect(payload.iat).toBeLessThanOrEqual(after - 59);
|
|
96
|
+
// exp should be iat + 5 minutes (300s)
|
|
97
|
+
expect(payload.exp - payload.iat).toBe(300);
|
|
98
|
+
});
|
|
99
|
+
});
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nooks-ai/nooks-zoominfo-api-auth-client",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Zoominfo API Authentication Client (Nooks fork)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
7
|
+
"test": "jest"
|
|
8
8
|
},
|
|
9
9
|
"author": "Nooks",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"axios": "^
|
|
13
|
-
"jsrsasign": "^
|
|
12
|
+
"axios": "^1.12.2",
|
|
13
|
+
"jsrsasign": "^11.1.1"
|
|
14
14
|
},
|
|
15
|
-
"devDependencies": {
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"jest": "^30.3.0"
|
|
17
|
+
}
|
|
16
18
|
}
|