Skip to content

Commit 297bb95

Browse files
committed
feat(helper): implement getAuthenticationParameters method and test cases
1 parent b1594d8 commit 297bb95

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

src/resources/helper.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {
1414
} from './shared';
1515
import transformationUtils, { safeBtoa } from '../lib/transformation-utils';
1616
import { createHmacSha1 } from '../lib/crypto-utils';
17+
import { uuid4 } from '../internal/utils/uuid';
1718

1819
const TRANSFORMATION_PARAMETER = 'tr';
1920
const SIGNATURE_PARAMETER = 'ik-s';
@@ -132,8 +133,52 @@ export class Helper extends APIResource {
132133
buildTransformationString(transformation: Transformation[] | undefined): string {
133134
return buildTransformationString(transformation);
134135
}
136+
137+
/**
138+
* Generates authentication parameters for client-side file uploads using ImageKit's Upload API V1.
139+
*
140+
* This method creates the required authentication signature that allows secure file uploads
141+
* directly from the browser or mobile applications without exposing your private API key.
142+
* The generated parameters include a unique token, expiration timestamp, and HMAC signature.
143+
*
144+
* @param token - Custom token for the upload session. If not provided, a UUID v4 will be generated automatically.
145+
* @param expire - Expiration time in seconds from now. If not provided, defaults to 1800 seconds (30 minutes).
146+
* @returns Authentication parameters object containing:
147+
* - `token`: Unique identifier for this upload session
148+
* - `expire`: Unix timestamp when these parameters expire
149+
* - `signature`: HMAC-SHA1 signature for authenticating the upload
150+
*
151+
* @throws {Error} If the private API key is not configured (should not happen in normal usage)
152+
*/
153+
getAuthenticationParameters(token?: string, expire?: number) {
154+
if (!this._client.privateAPIKey) {
155+
throw new Error('Private API key is required for authentication parameters generation');
156+
}
157+
158+
const DEFAULT_TIME_DIFF = 60 * 30;
159+
const defaultExpire = Math.floor(Date.now() / 1000) + DEFAULT_TIME_DIFF;
160+
161+
const finalToken = token || uuid4();
162+
const finalExpire = expire || defaultExpire;
163+
164+
return getAuthenticationParameters(finalToken, finalExpire, this._client.privateAPIKey);
165+
}
135166
}
136167

168+
const getAuthenticationParameters = function (token: string, expire: number, privateKey: string) {
169+
var authParameters = {
170+
token: token,
171+
expire: expire,
172+
signature: '',
173+
};
174+
175+
var signature = createHmacSha1(privateKey, token + expire);
176+
177+
authParameters.signature = signature;
178+
179+
return authParameters;
180+
};
181+
137182
function removeTrailingSlash(str: string): string {
138183
if (typeof str == 'string' && str[str.length - 1] == '/') {
139184
str = str.substring(0, str.length - 1);

tests/helper-authentication.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import ImageKit from '@imagekit/nodejs';
2+
3+
const client = new ImageKit({
4+
privateAPIKey: 'private_key_test',
5+
password: 'My Password',
6+
baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
7+
});
8+
9+
describe('Helper Authentication Parameters', function () {
10+
it('should return correct authentication parameters with provided token and expire', function () {
11+
const authenticationParameters = client.helper.getAuthenticationParameters('your_token', 1582269249);
12+
13+
expect(authenticationParameters).toEqual({
14+
token: 'your_token',
15+
expire: 1582269249,
16+
signature: 'e71bcd6031016b060d349d212e23e85c791decdd',
17+
});
18+
});
19+
20+
it('should return authentication parameters with required properties when no params provided', function () {
21+
const authenticationParameters = client.helper.getAuthenticationParameters();
22+
23+
expect(authenticationParameters).toHaveProperty('token');
24+
expect(authenticationParameters).toHaveProperty('expire');
25+
expect(authenticationParameters).toHaveProperty('signature');
26+
27+
// Token should be a UUID (36 characters with dashes)
28+
expect(authenticationParameters.token).toMatch(
29+
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
30+
);
31+
32+
// Expire should be a number greater than current time
33+
expect(typeof authenticationParameters.expire).toBe('number');
34+
expect(authenticationParameters.expire).toBeGreaterThan(Math.floor(Date.now() / 1000));
35+
36+
// Signature should be a hex string (40 characters for HMAC-SHA1)
37+
expect(authenticationParameters.signature).toMatch(/^[a-f0-9]{40}$/);
38+
});
39+
40+
it('should handle edge case with expire time 0', function () {
41+
// When expire is 0, it's falsy, so the method uses default expire time
42+
const authenticationParameters = client.helper.getAuthenticationParameters('test-token', 0);
43+
44+
expect(authenticationParameters.token).toBe('test-token');
45+
// Since 0 is falsy, it should use the default expire (30 minutes from now)
46+
const expectedExpire = Math.floor(Date.now() / 1000) + 60 * 30;
47+
expect(authenticationParameters.expire).toBeCloseTo(expectedExpire, -1);
48+
expect(authenticationParameters.signature).toMatch(/^[a-f0-9]{40}$/);
49+
});
50+
51+
it('should handle empty string token', function () {
52+
// When token is empty string, it's falsy, so the method generates a UUID
53+
const authenticationParameters = client.helper.getAuthenticationParameters('', 1582269249);
54+
55+
// Since '' is falsy, it should generate a UUID
56+
expect(authenticationParameters.token).toMatch(
57+
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
58+
);
59+
expect(authenticationParameters.expire).toBe(1582269249);
60+
expect(authenticationParameters.signature).toMatch(/^[a-f0-9]{40}$/);
61+
});
62+
});

0 commit comments

Comments
 (0)