Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: resolve issues with graphiql explorer #13555

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"dependencies": {
"lerna": "^6.6.1",
"node-gyp": "^9.3.1",
"node-pty": "^1.0.0",
"strip-ansi": "^6.0.0",
"yargs": "^17.7.2"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/amplify-appsync-simulator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"@types/express": "^4.17.3",
"@types/node": "^12.12.6",
"@types/ws": "^8.2.2",
"jsonwebtoken": "^9.0.0"
"jose": "^5.2.0"
},
"packageExtensions": {
"graphql-iso-date": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import * as http from 'http';
import type { GraphQLError } from 'graphql';
import { AmplifyAppSyncSimulator, AmplifyAppSyncSimulatorAuthenticationType } from '../../';
import jwt from 'jsonwebtoken';
import { SignJWT } from 'jose';

/**
* Minimal gql tag just for syntax highlighting and Prettier while writing client GraphQL queries
Expand Down Expand Up @@ -54,14 +54,14 @@ export async function appSyncClient<ResponseDataType = unknown, VarsType = Recor
break;

case AmplifyAppSyncSimulatorAuthenticationType.AMAZON_COGNITO_USER_POOLS:
headers.Authorization = jwt.sign(
{
username: auth.username,
'cognito:groups': auth.groups ?? [],
},
'mockSecret',
{ issuer: `https://cognito-idp.mock-region.amazonaws.com/mockUserPool` },
);
headers.Authorization = await new SignJWT({
username: auth.username,
'cognito:groups': auth.groups ?? [],
})
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setIssuer('https://cognito-idp.mock-region.amazonaws.com/mockUserPool')
.sign(new TextEncoder().encode('mockSecret'));
break;

case AmplifyAppSyncSimulatorAuthenticationType.AWS_IAM:
Expand Down
3 changes: 3 additions & 0 deletions packages/amplify-appsync-simulator/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ export class AmplifyAppSyncSimulator {
get url(): string {
return this._server.url.graphql;
}
get localhostUrl(): string {
return this._server.localhostUrl.graphql;
}
get config(): AmplifyAppSyncSimulatorConfig {
return this._config;
}
Expand Down
7 changes: 7 additions & 0 deletions packages/amplify-appsync-simulator/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export class AppSyncSimulatorServer {
private _httpServer: Server;
private _realTimeSubscriptionServer: AppSyncSimulatorSubscriptionServer;
private _url: string;
private _localhostUrl: string;

constructor(private config: AppSyncSimulatorServerConfig, private simulatorContext: AmplifyAppSyncSimulator) {
this._operationServer = new OperationServer(config, simulatorContext);
Expand Down Expand Up @@ -49,6 +50,7 @@ export class AppSyncSimulatorServer {
this._httpServer.listen(port);
await fromEvent(this._httpServer, 'listening').then(() => {
this._url = `http://${getLocalIpAddress()}:${port}`;
this._localhostUrl = `http://localhost:${port}`;
phani-srikar marked this conversation as resolved.
Show resolved Hide resolved
});
}

Expand All @@ -61,4 +63,9 @@ export class AppSyncSimulatorServer {
graphql: this._url,
};
}
get localhostUrl() {
return {
graphql: this._localhostUrl,
};
}
}
25 changes: 13 additions & 12 deletions packages/amplify-graphiql-explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"private": true,
"dependencies": {
"@babel/core": "^7.23.2",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
"@svgr/webpack": "^5.5.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.11",
"@svgr/webpack": "^8.1.0",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
Expand All @@ -21,23 +21,24 @@
"cross-env": "^7.0.3",
"crypto-browserify": "^3.12.0",
"css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^5.0.1",
"css-minimizer-webpack-plugin": "^6.0.0",
"dotenv": "^10.0.0",
"dotenv-expand": "^5.1.0",
"eslint": "^8.3.0",
"eslint-config-react-app": "^7.0.0",
"eslint-webpack-plugin": "^3.1.1",
"eslint-webpack-plugin": "^4.0.1",
"file-loader": "^6.2.0",
"fs-extra": "^10.0.0",
"graphiql": "^1.5.16",
"graphiql": ">=1.5.16 <=1.8.10",
"graphiql-explorer": "^0.6.2",
"graphql": "^15.5.0",
"html-webpack-plugin": "^5.5.0",
"graphql-ws": "^5.14.3",
"html-webpack-plugin": "^5.6.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.0.0",
"jest-resolve": "^26.0.2",
"jest-watch-typeahead": "^1.0.0",
"jsonwebtoken": "^9.0.0",
"jose": "^5.2.0",
"mini-css-extract-plugin": "^2.4.5",
"postcss": "^8.4.31",
"postcss-flexbugs-fixes": "^5.0.2",
Expand All @@ -61,14 +62,14 @@
"stream-browserify": "^3.0.0",
"style-loader": "^3.3.1",
"tailwindcss": "^3.0.2",
"terser-webpack-plugin": "^5.2.5",
"terser-webpack-plugin": "^5.3.10",
"typescript": "^4.9.5",
"util": "^0.12.4",
"web-vitals": "^0.2.4",
"webpack": "^5.64.4",
"webpack-dev-server": "^4.6.0",
"webpack-manifest-plugin": "^4.0.2",
"workbox-webpack-plugin": "^6.4.1"
"webpack": "^5.89.0",
"webpack-dev-server": "^4.15.1",
"webpack-manifest-plugin": "^5.0.0",
"workbox-webpack-plugin": "^7.0.0"
afnx marked this conversation as resolved.
Show resolved Hide resolved
},
"devDependencies": {
"@semantic-ui-react/css-patch": "^1.0.0",
Expand Down
65 changes: 45 additions & 20 deletions packages/amplify-graphiql-explorer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import GraphiQL from 'graphiql';
import GraphiQLExplorer from 'graphiql-explorer';
import 'graphiql/graphiql.css';
import { buildClientSchema, getIntrospectionQuery, GraphQLSchema, parse } from 'graphql';
import React, { Component } from 'react';
import { Component } from 'react';
import 'semantic-ui-css/semantic.min.css';
import './App.css';
import { AuthModal, AUTH_MODE } from './AuthModal';
Expand Down Expand Up @@ -125,14 +125,14 @@ class App extends Component<{}, State> {
}
async componentDidMount() {
const apiInfo = await getAPIInfo();
this.loadCredentials(apiInfo);
await this.loadCredentials(apiInfo);
this.setState({ apiInfo });
const introspectionResult = await this.fetch({
query: getIntrospectionQuery(),
});

const editor = this._graphiql?.getQueryEditor();
editor.setOption('extraKeys', {
editor?.setOption('extraKeys', {
...(editor.options.extraKeys || {}),
'Shift-Alt-LeftClick': this._handleInspectOperation,
});
Expand Down Expand Up @@ -263,7 +263,7 @@ class App extends Component<{}, State> {
}));
}

loadCredentials(apiInfo = this.state.apiInfo) {
async loadCredentials(apiInfo = this.state.apiInfo) {
const credentials = {};
const authProviders = [apiInfo.defaultAuthenticationType, ...apiInfo.additionalAuthenticationProviders];
const possibleAuth = authProviders.map((auth) => auth.authenticationType);
Expand All @@ -273,12 +273,13 @@ class App extends Component<{}, State> {
}

if (possibleAuth.includes('AMAZON_COGNITO_USER_POOLS')) {
try {
credentials['cognitoJWTToken'] = refreshToken(window.localStorage.getItem(LOCAL_STORAGE_KEY_NAMES.cognitoToken) || '');
} catch (e) {
let token = window.localStorage.getItem(LOCAL_STORAGE_KEY_NAMES.cognitoToken);
if (token) {
credentials['cognitoJWTToken'] = await refreshToken(token);
} else {
console.warn('Invalid Cognito token found in local storage. Using the default OIDC token');
// token is not valid
credentials['cognitoJWTToken'] = refreshToken(DEFAULT_COGNITO_JWT_TOKEN);
credentials['cognitoJWTToken'] = await refreshToken(DEFAULT_COGNITO_JWT_TOKEN);
}
}

Expand All @@ -287,10 +288,10 @@ class App extends Component<{}, State> {
.filter((auth) => auth.authenticationType === AUTH_MODE.OPENID_CONNECT)
.map((auth: any) => auth.openIDConnectConfig.Issuer);
try {
credentials['oidcJWTToken'] = refreshToken(window.localStorage.getItem(LOCAL_STORAGE_KEY_NAMES.oidcToken) || '', issuers[0]);
credentials['oidcJWTToken'] = await refreshToken(window.localStorage.getItem(LOCAL_STORAGE_KEY_NAMES.oidcToken) || '', issuers[0]);
} catch (e) {
console.warn('Invalid OIDC token found in local storage. Using the default OIDC token');
credentials['oidcJWTToken'] = refreshToken(DEFAULT_OIDC_JWT_TOKEN, issuers[0]);
credentials['oidcJWTToken'] = await refreshToken(DEFAULT_OIDC_JWT_TOKEN, issuers[0]);
}
}

Expand Down Expand Up @@ -329,6 +330,35 @@ class App extends Component<{}, State> {
const clearDataModal = clearDataModalVisible ? (
<ClearDataModal onClose={this.hideDataModal} onClear={this.clearDataAndShowMessage} />
) : null;

const buttons = [
{
onClick: () => this._graphiql?.handlePrettifyQuery(),
label: 'Prettify',
title: 'Prettify Query (Shift-Ctrl-P)',
},
{
onClick: () => this._graphiql?.handleToggleHistory(),
label: 'History',
title: 'Show History',
},
{
onClick: this._handleToggleExplorer,
label: 'Explorer',
title: 'Toggle Explorer',
},
{
onClick: this.toggleAuthModal,
label: 'Update Auth',
title: 'Auth Setting',
},
{
onClick: this.toggleClearDataModal,
label: 'Clear data',
title: 'Clear Mock Data',
},
];

return (
<>
{authModal}
Expand All @@ -351,23 +381,18 @@ class App extends Component<{}, State> {
response={clearResponse}
>
<GraphiQL.Toolbar>
<GraphiQL.Button
onClick={() => this._graphiql?.handlePrettifyQuery()}
label="Prettify"
title="Prettify Query (Shift-Ctrl-P)"
/>
<GraphiQL.Button onClick={() => this._graphiql?.handleToggleHistory()} label="History" title="Show History" />
<GraphiQL.Button onClick={this._handleToggleExplorer} label="Explorer" title="Toggle Explorer" />
<GraphiQL.Button onClick={this.toggleAuthModal} label="Update Auth" title="Auth Setting" />
<GraphiQL.Button onClick={this.toggleClearDataModal} label="Clear data" title="Clear Mock Data" />
{buttons.map((button, index) => (
<GraphiQL.Button key={index} onClick={button.onClick} label={button.label} title={button.title} />
))}
<GraphiQL.Menu
label={`Auth - ${AUTH_TYPE_TO_NAME[this.state.currentAuthMode]}${
this.state.currentAuthMode === 'AWS_IAM' ? `(${this.state.credentials.iamRole} Role)` : ''
}`}
title={AUTH_TYPE_TO_NAME[this.state.currentAuthMode]}
>
{authModes.map((mode) => (
{authModes.map((mode, index) => (
<GraphiQL.MenuItem
key={index}
title={AUTH_TYPE_TO_NAME[mode]}
label={`Use: ${AUTH_TYPE_TO_NAME[mode]}`}
onSelect={() => this.switchAuthMode(mode)}
Expand Down
14 changes: 7 additions & 7 deletions packages/amplify-graphiql-explorer/src/AuthModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,23 +304,23 @@ export class AuthModal extends Component<Props, State> {
</Modal>
);
}
onGenerate() {
async onGenerate() {
try {
const newState = {
isOpen: false,
};
if (this.state.currentAuthMode === AUTH_MODE.AMAZON_COGNITO_USER_POOLS) {
newState['currentCognitoToken'] = this.generateCognitoJWTToken();
newState['currentCognitoToken'] = await this.generateCognitoJWTToken();
} else if (this.state.currentAuthMode === AUTH_MODE.OPENID_CONNECT) {
newState['currentOIDCToken'] = this.generateOIDCJWTToken();
newState['currentOIDCToken'] = await this.generateOIDCJWTToken();
}
this.setState(newState, () => {
this.onClose();
});
} catch (e) {}
}

generateCognitoJWTToken() {
async generateCognitoJWTToken() {
let additionalFields;
try {
additionalFields = JSON.parse(this.state.additionalFields?.trim() || '{}');
Expand Down Expand Up @@ -348,14 +348,14 @@ export class AuthModal extends Component<Props, State> {
tokenPayload['cognito:groups'] = this.state.userGroups;
tokenPayload['auth_time'] = Math.floor(Date.now() / 1000); // In seconds

const token = generateToken(tokenPayload);
const token = await generateToken(tokenPayload);
return token;
}

generateOIDCJWTToken() {
async generateOIDCJWTToken() {
const tokenPayload = this.state.currentOIDCTokenDecoded || '';
try {
return generateToken(tokenPayload);
return await generateToken(tokenPayload);
} catch (e) {
this.setState({
oidcTokenError: e.message,
Expand Down
20 changes: 12 additions & 8 deletions packages/amplify-graphiql-explorer/src/utils/jwt.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { decode, sign, verify } from 'jsonwebtoken';
import { decodeJwt, SignJWT, jwtVerify, JWTPayload } from 'jose';

export function generateToken(decodedToken: string | object): string {
export async function generateToken(decodedToken: string | object): Promise<string> {
try {
if (typeof decodedToken === 'string') {
decodedToken = JSON.parse(decodedToken);
}
const token = sign(decodedToken, 'open-secrete');
verify(token, 'open-secrete');
const secret = new TextEncoder().encode('open-secrete');
const token = await new SignJWT(decodedToken as JWTPayload).setProtectedHeader({ alg: 'HS256' }).sign(secret);
await jwtVerify(token, secret);
return token;
} catch (e) {
const err = new Error('Error when generating OIDC token: ' + e.message);
throw err;
}
}

export function parse(token): object {
const decodedToken = decode(token);
export function parse(token: string | undefined): object | null {
if (typeof token === 'undefined' || typeof token !== 'string') {
return null;
}
const decodedToken = decodeJwt(token);
return decodedToken as object;
}

Expand All @@ -25,7 +29,7 @@ export function parse(token): object {
* @param token
* @param issuer
*/
export function refreshToken(token: string, issuer?: string): string {
export async function refreshToken(token: string, issuer?: string): Promise<string> {
const tokenObj: any = parse(token);
if (!Object.keys(tokenObj).length) {
throw new Error(`Invalid token ${token}`);
Expand All @@ -34,5 +38,5 @@ export function refreshToken(token: string, issuer?: string): string {
tokenObj.iss = issuer;
}
tokenObj.exp = Math.floor(Date.now() / 100 + 20000);
return generateToken(JSON.stringify(tokenObj));
return await generateToken(JSON.stringify(tokenObj));
}
2 changes: 1 addition & 1 deletion packages/amplify-util-mock/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"graphql-versioned-transformer": "^5.2.72",
"isomorphic-fetch": "^3.0.0",
"jest": "^29.0.0",
"jsonwebtoken": "^9.0.0",
"jose": "^5.2.0",
afnx marked this conversation as resolved.
Show resolved Hide resolved
"uuid": "^8.3.2",
"ws": "^7.5.7"
},
Expand Down
Loading