Skip to content

Commit

Permalink
OAuth: use nonce of 64 chars max length, use timestamp in seconds (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
lionralfs authored Oct 3, 2023
1 parent b12b9e0 commit 71ec996
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 7 deletions.
2 changes: 1 addition & 1 deletion browser/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export function randomBytes(_) {
return {
toString(_) {
// not quite the same but it's only used for nonce generation
return window.crypto.randomUUID();
return window.crypto.randomUUID().slice(0, 64);
},
};
}
5 changes: 4 additions & 1 deletion lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from './types.js';
import { toAuthHeader } from './oauth.js';
import { hasProperty } from './helpers-internal.js';
import * as crypto from 'crypto';

const version = process.env.VERSION_NUMBER || 'dev';
const homepage = 'https://github.com/lionralfs/discogs-client';
Expand Down Expand Up @@ -197,7 +198,9 @@ export class DiscogsClient {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.auth.accessToken!,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.auth.accessTokenSecret!
this.auth.accessTokenSecret!,
{ now: () => Date.now() },
crypto
);
} else if (this.auth.method === 'discogs') {
authHeader = 'Discogs';
Expand Down
11 changes: 8 additions & 3 deletions lib/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,14 @@ export function toAuthHeader(
consumerKey: string,
consumerSecret: string,
accessToken: string,
accessTokenSecret: string
accessTokenSecret: string,
clock: { now: () => number },
_crypto: typeof crypto
) {
const nonce = crypto.randomBytes(64).toString('hex');
const timestamp = Date.now();
// generate 32 bytes which is a string of 64 characters in hex encoding
// because any request that includes a nonce string of length > 64 characters is rejected
const nonce = _crypto.randomBytes(32).toString('hex', 0, 64);
// timestamp is expected in seconds
const timestamp = Math.floor(clock.now() / 1000);
return `OAuth oauth_consumer_key="${consumerKey}", oauth_token="${accessToken}", oauth_signature_method="PLAINTEXT", oauth_signature="${consumerSecret}&${accessTokenSecret}", oauth_timestamp="${timestamp}", oauth_nonce="${nonce}", oauth_token_secret="${accessTokenSecret}", oauth_version="1.0"`;
}
28 changes: 26 additions & 2 deletions test/unit/oauth.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
import test from 'ava';
import { toAuthHeader } from '@lib/oauth.js';
import type * as crypto from 'crypto';

const fakeCrypto: typeof crypto = {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
randomBytes(size: number) {
return {
toString(encoding: string, start: number, end: number) {
if (size === 32 && encoding === 'hex' && start === 0 && end === 64) {
return 'correct';
} else {
return 'incorrect';
}
},
};
},
};

test('OAuth: toAuthHeader', t => {
t.regex(
toAuthHeader('consumer_key', 'consumer_secret', 'access_token', 'access_token_secret'),
/^OAuth oauth_consumer_key="consumer_key", oauth_token="access_token", oauth_signature_method="PLAINTEXT", oauth_signature="consumer_secret&access_token_secret", oauth_timestamp="\d+", oauth_nonce=".+", oauth_token_secret="access_token_secret", oauth_version="1.0"$/
toAuthHeader(
'consumer_key',
'consumer_secret',
'access_token',
'access_token_secret',
{ now: () => 12345 },
fakeCrypto
),
/^OAuth oauth_consumer_key="consumer_key", oauth_token="access_token", oauth_signature_method="PLAINTEXT", oauth_signature="consumer_secret&access_token_secret", oauth_timestamp="12", oauth_nonce="correct", oauth_token_secret="access_token_secret", oauth_version="1.0"$/
);
});

0 comments on commit 71ec996

Please sign in to comment.