Skip to content

Commit 973d43e

Browse files
committed
Explicit error if query signing is invalid
We always return an error if misconfigured, even for non-GraphQL HTTP requests, because we want to make the misconfiguration extremely visible as soon as possible. Otherwise, the app might look like it is working until we navigate to a page that does GraphQL queries.
1 parent 6214ebc commit 973d43e

File tree

2 files changed

+39
-7
lines changed

2 files changed

+39
-7
lines changed

projects/natural/src/lib/classes/signing.spec.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ const batchedGraphqlQuery = [
1919

2020
const key = 'my-secret-1';
2121

22-
function expectSigned(request: HttpRequest<unknown>, done: DoneFn): void {
23-
const signer = graphqlQuerySigner(key);
24-
22+
function createHandlerSpy(): jasmine.Spy<HttpHandlerFn> {
2523
const handler = jasmine.createSpy<HttpHandlerFn>('HttpHandlerFn');
2624
handler.and.callFake(() => of(new HttpResponse()));
25+
return handler;
26+
}
27+
28+
function expectSigned(request: HttpRequest<unknown>, done: DoneFn): void {
29+
const signer = graphqlQuerySigner(key);
30+
const handler = createHandlerSpy();
2731

2832
signer(request, handler).subscribe(() => {
2933
expect(handler).toHaveBeenCalledTimes(1);
@@ -35,9 +39,7 @@ function expectSigned(request: HttpRequest<unknown>, done: DoneFn): void {
3539

3640
function expectNotSigned(request: HttpRequest<unknown>, done: DoneFn): void {
3741
const signer = graphqlQuerySigner(key);
38-
39-
const handler = jasmine.createSpy<HttpHandlerFn>('HttpHandlerFn');
40-
handler.and.callFake(() => of(new HttpResponse()));
42+
const handler = createHandlerSpy();
4143

4244
signer(request, handler).subscribe(() => {
4345
expect(handler).toHaveBeenCalledOnceWith(request);
@@ -84,4 +86,22 @@ describe('graphqlQuerySigner', () => {
8486

8587
expectNotSigned(request, done);
8688
});
89+
90+
it('if mis-configured, will always error, even if query should not be signed', done => {
91+
const request = new HttpRequest('GET', 'foo');
92+
const signer = graphqlQuerySigner('');
93+
const handler = createHandlerSpy();
94+
95+
signer(request, handler).subscribe({
96+
error: error => {
97+
expect(handler).not.toHaveBeenCalled();
98+
expect(error).toBeInstanceOf(Error);
99+
expect('message' in error).toBeTrue();
100+
expect(error.message).toBe(
101+
'graphqlQuerySigner requires a non-empty key. Configure it in local.php under signedQueries.',
102+
);
103+
done();
104+
},
105+
});
106+
});
87107
});

projects/natural/src/lib/classes/signing.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {HttpInterceptorFn, HttpRequest} from '@angular/common/http';
22
import {hmacSha256} from './crypto';
3-
import {from, switchMap} from 'rxjs';
3+
import {from, switchMap, throwError} from 'rxjs';
44

55
function getOperations(req: HttpRequest<unknown>): string {
66
if (req.body instanceof FormData) {
@@ -22,6 +22,18 @@ function getOperations(req: HttpRequest<unknown>): string {
2222
* The server will validate the signature before executing the GraphQL query.
2323
*/
2424
export function graphqlQuerySigner(key: string): HttpInterceptorFn {
25+
// Validates the configuration exactly 1 time (not for
26+
// every query), and if not reject **all** HTTP requests
27+
if (!key) {
28+
return () =>
29+
throwError(
30+
() =>
31+
new Error(
32+
'graphqlQuerySigner requires a non-empty key. Configure it in local.php under signedQueries.',
33+
),
34+
);
35+
}
36+
2537
return (req, next) => {
2638
const mustSign = req.method === 'POST' && /\/graphql(\?|$)/.exec(req.url);
2739
if (!mustSign) {

0 commit comments

Comments
 (0)