From 0a1e9ee10c6c7286df4215cc065ef8402d3d61b0 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 5 Oct 2019 16:37:15 -0700 Subject: [PATCH 1/3] set up authentication --- typings/index.d.ts | 2 ++ web-app/src/Routes.tsx | 2 +- web-app/src/services/apollo/auth.ts | 15 ++++++++++++ web-app/src/services/apollo/index.ts | 5 ++-- .../services/apollo/mutations/authenticate.ts | 23 +++++++++++++++++++ web-app/src/services/state/actions/api.ts | 23 +++++++++++++++++++ web-app/src/services/state/machine.ts | 10 ++++++-- 7 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 web-app/src/services/apollo/auth.ts create mode 100644 web-app/src/services/apollo/mutations/authenticate.ts diff --git a/typings/index.d.ts b/typings/index.d.ts index 6796f3cb..8b23f8e9 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -122,6 +122,7 @@ export interface Action { export interface Environment { machineId: string sessionId: string + token: string } export interface MachineContext { @@ -142,6 +143,7 @@ export interface MachineStateSchema { Start: { states: { Startup: {} + Authenticate: {} NewOrContinue: {} SelectTutorial: {} ContinueTutorial: {} diff --git a/web-app/src/Routes.tsx b/web-app/src/Routes.tsx index cb3c1184..307ecb6c 100644 --- a/web-app/src/Routes.tsx +++ b/web-app/src/Routes.tsx @@ -19,7 +19,7 @@ const Routes = () => { return ( - + diff --git a/web-app/src/services/apollo/auth.ts b/web-app/src/services/apollo/auth.ts new file mode 100644 index 00000000..b227a551 --- /dev/null +++ b/web-app/src/services/apollo/auth.ts @@ -0,0 +1,15 @@ +import {Operation} from 'apollo-boost' + +let authToken: string | null = null + +export const setAuthToken = (token: string | null) => { + authToken = token +} + +export const authorizeHeaders = (operation: Operation) => { + operation.setContext({ + headers: { + token: authToken + } + }) +} diff --git a/web-app/src/services/apollo/index.ts b/web-app/src/services/apollo/index.ts index 6fb3d296..b5d8708d 100644 --- a/web-app/src/services/apollo/index.ts +++ b/web-app/src/services/apollo/index.ts @@ -1,12 +1,11 @@ import ApolloClient, {InMemoryCache} from 'apollo-boost' +import {authorizeHeaders} from './auth' export const cache = new InMemoryCache() const client = new ApolloClient({ uri: process.env.REACT_APP_GQL_URI, - headers: { - Authorization: process.env.REACT_APP_GQL_AUTH_TOKEN, - }, + request: authorizeHeaders, cache, }) diff --git a/web-app/src/services/apollo/mutations/authenticate.ts b/web-app/src/services/apollo/mutations/authenticate.ts new file mode 100644 index 00000000..1c02a307 --- /dev/null +++ b/web-app/src/services/apollo/mutations/authenticate.ts @@ -0,0 +1,23 @@ +import {gql} from 'apollo-boost' + +export default gql` + mutation Authenticate( + $machineId: String!, + $sessionId: String!, + $editor: EditorEnum! + ) { + editorLogin(input: { + machineId: $machineId, + sessionId: $sessionId, + editor: $editor + }) { + token + user { + id + name + email + avatarUrl + } + } + } +` diff --git a/web-app/src/services/state/actions/api.ts b/web-app/src/services/state/actions/api.ts index 9b2486cd..1b0077a2 100644 --- a/web-app/src/services/state/actions/api.ts +++ b/web-app/src/services/state/actions/api.ts @@ -1,6 +1,29 @@ import * as CR from 'typings' +import client from '../../apollo' +import authenticateMutation from '../../apollo/mutations/authenticate' +import {setAuthToken} from '../../apollo/auth' +import {send} from 'xstate' export default { + authenticate: (async (context: CR.MachineContext): Promise => { + const result = await client.mutate({ + mutation: authenticateMutation, + variables: { + machineId: context.env.machineId, + sessionId: context.env.sessionId, + editor: 'VSCODE', + } + }) + + if (!result || !result.data) { + // TODO: handle failed authentication + console.log('unauthenticated') + } + const {token} = result.data.editorLogin + console.log(token) + setAuthToken(token) + return send({type: 'AUTHENTICATED'}) + }), userTutorialComplete(context: CR.MachineContext) { console.log('should update user tutorial as complete') } diff --git a/web-app/src/services/state/machine.ts b/web-app/src/services/state/machine.ts index 70aa4ece..232f61b6 100644 --- a/web-app/src/services/state/machine.ts +++ b/web-app/src/services/state/machine.ts @@ -12,7 +12,7 @@ export const machine = Machine Date: Sat, 5 Oct 2019 16:46:48 -0700 Subject: [PATCH 2/3] complete authentication on client --- web-app/src/services/channel/index.ts | 24 +---------------------- web-app/src/services/state/actions/api.ts | 9 +++++---- 2 files changed, 6 insertions(+), 27 deletions(-) diff --git a/web-app/src/services/channel/index.ts b/web-app/src/services/channel/index.ts index b51272b1..18d0c700 100644 --- a/web-app/src/services/channel/index.ts +++ b/web-app/src/services/channel/index.ts @@ -38,39 +38,17 @@ class Channel { // messages from core switch (action.type) { case 'ENV_LOAD': - this.machineSend(action) - return + case 'AUTHENTICATED': case 'TUTORIAL_LOADED': - // send action to state machine - this.machineSend('TUTORIAL_LOADED') - console.log('send action to state machine') - return case 'NEW_TUTORIAL': - this.machineSend(action) - return case 'TUTORIAL_CONFIGURED': - this.machineSend(action) - return case 'CONTINUE_TUTORIAL': - this.machineSend(action) - return case 'TEST_PASS': - // { type: 'TEST_PASS', payload: { stepId: string }} - this.machineSend(action) - return case 'TEST_FAIL': - this.machineSend(action) - return case 'TEST_RUNNING': - this.machineSend(action) - return case 'TEST_ERROR': - console.log('TEST_ERROR') this.machineSend(action) return - case 'ACTIONS_LOADED': - // TODO: use this for verifying completion of stepActions - return default: if (action.type) { console.warn(`Unknown received action ${action.type}`, action) diff --git a/web-app/src/services/state/actions/api.ts b/web-app/src/services/state/actions/api.ts index 1b0077a2..6a0a8aa8 100644 --- a/web-app/src/services/state/actions/api.ts +++ b/web-app/src/services/state/actions/api.ts @@ -2,10 +2,10 @@ import * as CR from 'typings' import client from '../../apollo' import authenticateMutation from '../../apollo/mutations/authenticate' import {setAuthToken} from '../../apollo/auth' -import {send} from 'xstate' +import channel from '../../../services/channel' export default { - authenticate: (async (context: CR.MachineContext): Promise => { + authenticate: (async (context: CR.MachineContext): Promise => { const result = await client.mutate({ mutation: authenticateMutation, variables: { @@ -20,9 +20,10 @@ export default { console.log('unauthenticated') } const {token} = result.data.editorLogin - console.log(token) + // add token to headers setAuthToken(token) - return send({type: 'AUTHENTICATED'}) + // pass authenticated action back to state machine + channel.receive({data: {type: 'AUTHENTICATED'}}) }), userTutorialComplete(context: CR.MachineContext) { console.log('should update user tutorial as complete') From 8b32f1884307c226e9a5bf0e4e891d94086bd65e Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 5 Oct 2019 17:39:20 -0700 Subject: [PATCH 3/3] complete authorization --- web-app/src/components/ErrorBoundary/index.tsx | 4 ++-- web-app/src/services/apollo/auth.ts | 12 +++++++----- web-app/src/services/state/actions/api.ts | 4 +++- web-app/src/services/state/actions/context.ts | 5 ++++- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/web-app/src/components/ErrorBoundary/index.tsx b/web-app/src/components/ErrorBoundary/index.tsx index ce6a93e3..b855ae68 100644 --- a/web-app/src/components/ErrorBoundary/index.tsx +++ b/web-app/src/components/ErrorBoundary/index.tsx @@ -7,8 +7,8 @@ class ErrorBoundary extends React.Component { // Display fallback UI this.setState({ hasError: true }) // You can also log the error to an error reporting service - console.error(error) - console.log(info) + console.error(JSON.stringify(error)) + console.log(JSON.stringify(info)) } public render() { diff --git a/web-app/src/services/apollo/auth.ts b/web-app/src/services/apollo/auth.ts index b227a551..a1637dc3 100644 --- a/web-app/src/services/apollo/auth.ts +++ b/web-app/src/services/apollo/auth.ts @@ -7,9 +7,11 @@ export const setAuthToken = (token: string | null) => { } export const authorizeHeaders = (operation: Operation) => { - operation.setContext({ - headers: { - token: authToken - } - }) + if (authToken) { + operation.setContext({ + headers: { + 'Authorization': authToken + } + }) + } } diff --git a/web-app/src/services/state/actions/api.ts b/web-app/src/services/state/actions/api.ts index 6a0a8aa8..f5707c9a 100644 --- a/web-app/src/services/state/actions/api.ts +++ b/web-app/src/services/state/actions/api.ts @@ -6,6 +6,7 @@ import channel from '../../../services/channel' export default { authenticate: (async (context: CR.MachineContext): Promise => { + const result = await client.mutate({ mutation: authenticateMutation, variables: { @@ -15,9 +16,10 @@ export default { } }) + if (!result || !result.data) { // TODO: handle failed authentication - console.log('unauthenticated') + console.error('ERROR: Authentication failed') } const {token} = result.data.editorLogin // add token to headers diff --git a/web-app/src/services/state/actions/context.ts b/web-app/src/services/state/actions/context.ts index 2a910cd7..7ac2857f 100644 --- a/web-app/src/services/state/actions/context.ts +++ b/web-app/src/services/state/actions/context.ts @@ -6,7 +6,10 @@ import * as selectors from '../../selectors' export default { setEnv: assign({ env: (context: CR.MachineContext, event: CR.MachineEvent) => { - return event.payload.env + return { + ...context.env, + ...event.payload.env + } } }), continueTutorial: assign({