Commit e1e43428 authored by Robert Knight's avatar Robert Knight

Simplify `state` string generation mocking

Now that we have good support for mocking imports in tests, we can use
that to mock `generateHexString` calls in `OAuthClient` rather than
needing to have a test seam in the class.

This improves test coverage slightly.
parent 8040bf0d
...@@ -3,7 +3,7 @@ import { generateHexString } from './random'; ...@@ -3,7 +3,7 @@ import { generateHexString } from './random';
/** /**
* An object holding the details of an access token from the tokenUrl endpoint. * An object holding the details of an access token from the tokenUrl endpoint.
* *
* @typedef {Object} TokenInfo * @typedef TokenInfo
* @prop {string} accessToken - The access token itself. * @prop {string} accessToken - The access token itself.
* @prop {number} expiresAt - The date when the timestamp will expire. * @prop {number} expiresAt - The date when the timestamp will expire.
* @prop {string} refreshToken - The refresh token that can be used to * @prop {string} refreshToken - The refresh token that can be used to
...@@ -24,25 +24,14 @@ export class TokenError extends Error { ...@@ -24,25 +24,14 @@ export class TokenError extends Error {
} }
} }
/**
* Generate a short random string suitable for use as the "state" param in
* authorization requests.
*
* See https://tools.ietf.org/html/rfc6749#section-4.1.1.
*/
function generateState() {
return generateHexString(16);
}
/** /**
* OAuthClient configuration. * OAuthClient configuration.
* *
* @typedef {Object} Config * @typedef Config
* @property {string} clientId - OAuth client ID * @prop {string} clientId - OAuth client ID
* @property {string} tokenEndpoint - OAuth token exchange/refresh endpoint * @prop {string} tokenEndpoint - OAuth token exchange/refresh endpoint
* @property {string} authorizationEndpoint - OAuth authorization endpoint * @prop {string} authorizationEndpoint - OAuth authorization endpoint
* @property {string} revokeEndpoint - RFC 7009 token revocation endpoint * @prop {string} revokeEndpoint - RFC 7009 token revocation endpoint
* @property {() => string} [generateState] - Authorization "state" parameter generator
*/ */
/** /**
...@@ -60,9 +49,6 @@ export default class OAuthClient { ...@@ -60,9 +49,6 @@ export default class OAuthClient {
this.tokenEndpoint = config.tokenEndpoint; this.tokenEndpoint = config.tokenEndpoint;
this.authorizationEndpoint = config.authorizationEndpoint; this.authorizationEndpoint = config.authorizationEndpoint;
this.revokeEndpoint = config.revokeEndpoint; this.revokeEndpoint = config.revokeEndpoint;
// Test seam
this.generateState = config.generateState || generateState;
} }
/** /**
...@@ -136,7 +122,9 @@ export default class OAuthClient { ...@@ -136,7 +122,9 @@ export default class OAuthClient {
authorize($window, authWindow) { authorize($window, authWindow) {
// Random state string used to check that auth messages came from the popup // Random state string used to check that auth messages came from the popup
// window that we opened. // window that we opened.
const state = this.generateState(); //
// See https://tools.ietf.org/html/rfc6749#section-4.1.1.
const state = generateHexString(16);
// Promise which resolves or rejects when the user accepts or closes the // Promise which resolves or rejects when the user accepts or closes the
// auth popup. // auth popup.
......
import fetchMock from 'fetch-mock'; import fetchMock from 'fetch-mock';
import sinon from 'sinon'; import sinon from 'sinon';
import { TokenError } from '../oauth-client'; import { $imports, TokenError } from '../oauth-client';
import OAuthClient from '../oauth-client'; import OAuthClient from '../oauth-client';
import FakeWindow from '../../test/fake-window'; import FakeWindow from '../../test/fake-window';
...@@ -31,7 +31,6 @@ describe('sidebar/util/oauth-client', () => { ...@@ -31,7 +31,6 @@ describe('sidebar/util/oauth-client', () => {
authorizationEndpoint: 'https://annota.te/oauth/authorize', authorizationEndpoint: 'https://annota.te/oauth/authorize',
tokenEndpoint: 'https://annota.te/api/token', tokenEndpoint: 'https://annota.te/api/token',
revokeEndpoint: 'https://annota.te/oauth/revoke', revokeEndpoint: 'https://annota.te/oauth/revoke',
generateState: () => 'notrandom',
}; };
beforeEach(() => { beforeEach(() => {
...@@ -44,6 +43,7 @@ describe('sidebar/util/oauth-client', () => { ...@@ -44,6 +43,7 @@ describe('sidebar/util/oauth-client', () => {
}); });
afterEach(() => { afterEach(() => {
$imports.$restore();
fetchMock.restore(); fetchMock.restore();
clock.restore(); clock.restore();
}); });
...@@ -215,6 +215,12 @@ describe('sidebar/util/oauth-client', () => { ...@@ -215,6 +215,12 @@ describe('sidebar/util/oauth-client', () => {
beforeEach(() => { beforeEach(() => {
fakeWindow = new FakeWindow(); fakeWindow = new FakeWindow();
$imports.$mock({
'./random': {
generateHexString: () => 'notrandom',
},
});
}); });
function authorize() { function authorize() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment