Skip to content

Commit f09297b

Browse files
authored
Merge pull request #468 from uploadcare/feat/secure-signature-generator-api
feat(signed-uploads): update `generateSecureSignature` signature - BREAKING CHANGE without major bump
2 parents 65a00c6 + 97f7ebb commit f09297b

File tree

3 files changed

+55
-23
lines changed

3 files changed

+55
-23
lines changed

Diff for: packages/signed-uploads/README.md

+12-4
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,23 @@ npm install @uploadcare/signed-uploads
3939
import { generateSecureSignature } from '@uploadcare/signed-uploads'
4040

4141
// by the expiration timestamp in milliseconds since the epoch
42-
const signature = generateSecureSignature('YOUR_SECRET_KEY', {
43-
expire: Date.now() + 60 * 30 * 1000 // 30 minutes
42+
const { secureSignature, secureExpire } = generateSecureSignature('YOUR_SECRET_KEY', {
43+
expire: Date.now() + 60 * 30 * 1000 // expire in 30 minutes
44+
})
45+
46+
// by the expiration date
47+
const { secureSignature, secureExpire } = generateSecureSignature('YOUR_SECRET_KEY', {
48+
expire: new Date("2099-01-01") // expire on 2099-01-01
4449
})
4550

4651
// by the lifetime in milliseconds
47-
const signature = generateSecureSignature('YOUR_SECRET_KEY', {
48-
lifetime: 60 * 30 * 1000 // 30 minutes
52+
const { secureSignature, secureExpire } = generateSecureSignature('YOUR_SECRET_KEY', {
53+
lifetime: 60 * 30 * 1000 // expire in 30 minutes
4954
})
5055
```
5156

57+
A pair of `secureSignature` and `secureExpire` (string with a unixtime in seconds) can be passed directly to the [corresponding options][upload-client-secure-options] of `@uploadcare/upload-client`.
58+
5259
## Security issues
5360

5461
If you think you ran into something in Uploadcare libraries that might have
@@ -74,3 +81,4 @@ request at [[email protected]][uc-email-hello].
7481
[badge-build]: https://github.com/uploadcare/uploadcare-js-api-clients/actions/workflows/checks.yml/badge.svg
7582
[build-url]: https://github.com/uploadcare/uploadcare-js-api-clients/actions/workflows/checks.yml
7683
[uc-docs-signed-uploads]: https://uploadcare.com/docs/security/secure-uploads/#signed-uploads?utm_source=github&utm_campaign=uploadcare-js-api-clients
84+
[upload-client-secure-options]: https://github.com/uploadcare/uploadcare-js-api-clients/blob/master/packages/upload-client/README.md#securesignature-string

Diff for: packages/signed-uploads/src/generateSecureSignature.test.ts

+29-9
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,43 @@ const FIXTURE_DATE = new Date(1678359840000)
66
const FIXTURE_LIFETIME = 60 * 30 * 1000 // 30 minutes
77
const EXPECTED_SIGNATURE =
88
'93b69d086a487fbdfc36172b96a5d7f5afa7cb209e43e5f25890bc037e638584'
9+
const EXPECTED_EXPIRE = '1678361640'
910

1011
describe('generateSecureSignature', () => {
1112
beforeAll(() => {
1213
jest.useFakeTimers().setSystemTime(FIXTURE_DATE)
1314
})
1415

15-
it('should return signature by `expire` as number', () => {
16-
const signature = generateSecureSignature(FIXTURE_SECRET, {
17-
expire: Date.now() + FIXTURE_LIFETIME
18-
})
19-
expect(signature).toBe(EXPECTED_SIGNATURE)
16+
it('should return signature and expire by `expire` as number', () => {
17+
const { secureSignature, secureExpire } = generateSecureSignature(
18+
FIXTURE_SECRET,
19+
{
20+
expire: Date.now() + FIXTURE_LIFETIME
21+
}
22+
)
23+
expect(secureSignature).toBe(EXPECTED_SIGNATURE)
24+
expect(secureExpire).toBe(EXPECTED_EXPIRE)
25+
})
26+
27+
it('should return signature and expire by `expire` as Date', () => {
28+
const { secureSignature, secureExpire } = generateSecureSignature(
29+
FIXTURE_SECRET,
30+
{
31+
expire: new Date(FIXTURE_DATE.getTime() + FIXTURE_LIFETIME)
32+
}
33+
)
34+
expect(secureSignature).toBe(EXPECTED_SIGNATURE)
35+
expect(secureExpire).toBe(EXPECTED_EXPIRE)
2036
})
2137

2238
it('should return signature by `lifetime`', () => {
23-
const signature = generateSecureSignature(FIXTURE_SECRET, {
24-
lifetime: FIXTURE_LIFETIME
25-
})
26-
expect(signature).toBe(EXPECTED_SIGNATURE)
39+
const { secureSignature, secureExpire } = generateSecureSignature(
40+
FIXTURE_SECRET,
41+
{
42+
lifetime: FIXTURE_LIFETIME
43+
}
44+
)
45+
expect(secureSignature).toBe(EXPECTED_SIGNATURE)
46+
expect(secureExpire).toBe(EXPECTED_EXPIRE)
2747
})
2848
})

Diff for: packages/signed-uploads/src/generateSecureSignature.ts

+14-10
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,23 @@ export type GenerateSecureSignatureOptions =
44
| {
55
/**
66
* The expiration timestamp of the signature in milliseconds since the
7-
* epoch
7+
* epoch or just Date object.
88
*/
9-
expire: number
9+
expire: number | Date
1010
}
1111
| {
1212
/** The lifetime of the signature in milliseconds */
1313
lifetime: number
1414
}
1515

16-
const msToUnixTimestamp = (ms: number) => Math.floor(ms / 1000)
16+
const msToUnixTimestamp = (ms: number) => Math.floor(ms / 1000).toString()
17+
const getSecureExpire = (options: GenerateSecureSignatureOptions) => {
18+
if ('expire' in options) {
19+
return msToUnixTimestamp(new Date(options.expire).getTime())
20+
}
21+
22+
return msToUnixTimestamp(Date.now() + options.lifetime)
23+
}
1724

1825
/**
1926
* Generate a secure signature for signing the upload request to Uploadcare.
@@ -26,12 +33,9 @@ export const generateSecureSignature = (
2633
secret: string,
2734
options: GenerateSecureSignatureOptions
2835
) => {
29-
const expire =
30-
'expire' in options
31-
? msToUnixTimestamp(new Date(options.expire).getTime())
32-
: msToUnixTimestamp(Date.now() + options.lifetime)
33-
3436
const hmac = createHmac('sha256', secret)
35-
hmac.update(expire.toString())
36-
return hmac.digest('hex')
37+
const secureExpire = getSecureExpire(options)
38+
hmac.update(secureExpire)
39+
const secureSignature = hmac.digest('hex')
40+
return { secureSignature, secureExpire }
3741
}

0 commit comments

Comments
 (0)