Skip to content

Commit

Permalink
graphql: utilize default auth (#12586)
Browse files Browse the repository at this point in the history
  • Loading branch information
iartemiev authored Nov 19, 2023
1 parent 1f5eefd commit 4d80239
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 14 deletions.
149 changes: 149 additions & 0 deletions packages/api-graphql/__tests__/generateClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
expectSubWithHeadersFn,
} from './utils/expects';
import { Observable, from } from 'rxjs';
import * as internals from '../src/internals/';

const serverManagedFields = {
id: 'some-id',
Expand Down Expand Up @@ -5367,4 +5368,152 @@ describe('generateClient', () => {
});
});
});

describe('graphql default auth', () => {
beforeEach(() => {
jest.clearAllMocks();
Amplify.configure({
...configFixture,
aws_appsync_authenticationType: 'AWS_IAM', // make IAM default
} as any);

jest
.spyOn(Amplify.Auth, 'fetchAuthSession')
.mockImplementation(async () => {
return {
credentials: {
accessKeyId: 'test',
secretAccessKey: 'test',
},
} as any;
});
});

test('default iam produces expected signingInfo', async () => {
const spy = mockApiResponse({
data: {
listTodos: {
items: [
{
name: 'some name',
description: 'something something',
},
],
},
},
});

const client = generateClient({ amplify: Amplify });
await client.graphql({
query: `query { listTodos { __typename id owner createdAt updatedAt name description } }`,
});

expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
options: expect.objectContaining({
body: {
query: expect.stringContaining('listTodos'),
variables: {},
},
signingServiceInfo: { region: 'us-west-1', service: 'appsync' },
}),
})
);
});
});

describe('graphql - client-level auth override', () => {
beforeEach(() => {
jest.clearAllMocks();
Amplify.configure(configFixture as any);

jest
.spyOn(Amplify.Auth, 'fetchAuthSession')
.mockImplementation(async () => {
return {
credentials: {
accessKeyId: 'test',
secretAccessKey: 'test',
},
} as any;
});
});

test('client auth override query', async () => {
const spy = mockApiResponse({
data: {
listTodos: {
items: [
{
__typename: 'Todo',
...serverManagedFields,
name: 'some name',
description: 'something something',
},
],
},
},
});

// API key is the default auth mode for this API
const client = generateClient({ amplify: Amplify, authMode: 'iam' });
await client.graphql({
query: `query { listTodos { __typename id owner createdAt updatedAt name description } }`,
});

expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
options: expect.objectContaining({
body: {
query: expect.stringContaining('listTodos'),
variables: {},
},
signingServiceInfo: { region: 'us-west-1', service: 'appsync' },
}),
})
);
});

test('client auth override query - lambda', async () => {
const spy = mockApiResponse({
data: {
listTodos: {
items: [
{
__typename: 'Todo',
...serverManagedFields,
name: 'some name',
description: 'something something',
},
],
},
},
});

// API key is the default auth mode for this API
const client = generateClient({
amplify: Amplify,
authMode: 'lambda',
authToken: 'trustno1',
});
await client.graphql({
query: `query { listTodos { __typename id owner createdAt updatedAt name description } }`,
});

expect(spy).toHaveBeenCalledWith(
expect.objectContaining({
options: expect.objectContaining({
body: {
query: expect.stringContaining('listTodos'),
variables: {},
},
headers: expect.objectContaining({
Authorization: 'trustno1',
}),
signingServiceInfo: { region: 'us-west-1', service: 'appsync' },
}),
})
);
});
});
});
4 changes: 2 additions & 2 deletions packages/api-graphql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
},
"homepage": "https://aws-amplify.github.io/",
"devDependencies": {
"@aws-amplify/data-schema": "^0.12.6",
"@aws-amplify/data-schema": "^0.12.9",
"@rollup/plugin-typescript": "11.1.5",
"rollup": "3.29.4",
"typescript": "5.0.2"
Expand All @@ -81,7 +81,7 @@
"dependencies": {
"@aws-amplify/api-rest": "4.0.3",
"@aws-amplify/core": "6.0.3",
"@aws-amplify/data-schema-types": "^0.6.4",
"@aws-amplify/data-schema-types": "^0.6.6",
"@aws-sdk/types": "3.387.0",
"graphql": "15.8.0",
"rxjs": "^7.8.1",
Expand Down
9 changes: 5 additions & 4 deletions packages/api-graphql/src/internals/InternalGraphQLAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,11 @@ export class InternalGraphQLAPIClass {
region: region,
endpoint: appSyncGraphqlEndpoint,
apiKey,
defaultAuthMode,
} = resolveConfig(amplify);

const authenticationType = authMode || defaultAuthMode || 'iam';
let headers = {};

switch (authenticationType) {
switch (authMode) {
case 'apiKey':
if (!apiKey) {
throw new Error(GraphQLAuthError.NO_API_KEY);
Expand Down Expand Up @@ -227,7 +225,7 @@ export class InternalGraphQLAPIClass {

private async _graphql<T = any>(
amplify: AmplifyClassV6,
{ query, variables, authMode }: GraphQLOptions,
{ query, variables, authMode: explicitAuthMode }: GraphQLOptions,
additionalHeaders: CustomHeaders = {},
abortController: AbortController,
customUserAgentDetails?: CustomUserAgentDetails,
Expand All @@ -238,8 +236,11 @@ export class InternalGraphQLAPIClass {
endpoint: appSyncGraphqlEndpoint,
customEndpoint,
customEndpointRegion,
defaultAuthMode
} = resolveConfig(amplify);

const authMode = explicitAuthMode || defaultAuthMode || 'iam';

/**
* Retrieve library options from Amplify configuration.
* `customHeaders` here are from the Amplify configuration options,
Expand Down
6 changes: 6 additions & 0 deletions packages/api-graphql/src/internals/v6.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import { GraphQLAPI } from '../GraphQLAPI';
import {
__amplify,
__authMode,
__authToken,
V6Client,
GraphQLOptionsV6,
GraphQLResponseV6,
Expand Down Expand Up @@ -101,6 +103,10 @@ export function graphql<
options: GraphQLOptionsV6<FALLBACK_TYPES, TYPED_GQL_STRING>,
additionalHeaders?: CustomHeaders
): GraphQLResponseV6<FALLBACK_TYPES, TYPED_GQL_STRING> {
// inject client-level auth
options.authMode = options.authMode || this[__authMode];
options.authToken = options.authToken || this[__authToken];

/**
* The correctness of these typings depends on correct string branding or overrides.
* Neither of these can actually be validated at runtime. Hence, we don't perform
Expand Down
16 changes: 8 additions & 8 deletions yarn.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4d80239

Please sign in to comment.