From 37c6b57a48e0d0dedda808393963867604d7ad05 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Sun, 6 Nov 2022 17:20:12 +0100 Subject: [PATCH] feat(strava): add authentication support --- .../src/apps/strava/auth/create-auth-data.ts | 26 ++++ .../backend/src/apps/strava/auth/index.ts | 127 ++++++++++++++++++ .../apps/strava/auth/verify-credentials.ts | 28 ++++ .../src/apps/strava/common/add-auth-header.ts | 13 ++ packages/backend/src/apps/strava/index.ts | 4 + 5 files changed, 198 insertions(+) create mode 100644 packages/backend/src/apps/strava/auth/create-auth-data.ts create mode 100644 packages/backend/src/apps/strava/auth/index.ts create mode 100644 packages/backend/src/apps/strava/auth/verify-credentials.ts create mode 100644 packages/backend/src/apps/strava/common/add-auth-header.ts diff --git a/packages/backend/src/apps/strava/auth/create-auth-data.ts b/packages/backend/src/apps/strava/auth/create-auth-data.ts new file mode 100644 index 00000000..eb4fe55f --- /dev/null +++ b/packages/backend/src/apps/strava/auth/create-auth-data.ts @@ -0,0 +1,26 @@ +import { IField, IGlobalVariable } from '@automatisch/types'; +import qs from 'qs'; + +export default async function createAuthData($: IGlobalVariable) { + try { + const oauthRedirectUrlField = $.app.auth.fields.find( + (field: IField) => field.key == 'oAuthRedirectUrl' + ); + const redirectUri = oauthRedirectUrlField.value; + const searchParams = qs.stringify({ + client_id: $.auth.data.consumerKey as string, + redirect_uri: redirectUri, + approval_prompt: 'force', + response_type: 'code', + scope: 'read_all,profile:read_all,activity:read_all,activity:write', + }); + + await $.auth.set({ + url: `${$.app.baseUrl}/oauth/authorize?${searchParams}`, + }); + } catch (error) { + throw new Error( + `Error occured while verifying credentials: ${error}` + ); + } +} diff --git a/packages/backend/src/apps/strava/auth/index.ts b/packages/backend/src/apps/strava/auth/index.ts new file mode 100644 index 00000000..34fc86ee --- /dev/null +++ b/packages/backend/src/apps/strava/auth/index.ts @@ -0,0 +1,127 @@ +import createAuthData from './create-auth-data'; +import verifyCredentials from './verify-credentials'; +import isStillVerified from './is-still-verified'; + +export default { + fields: [ + { + key: 'oAuthRedirectUrl', + label: 'OAuth Redirect URL', + type: 'string' as const, + required: true, + readOnly: true, + value: '{WEB_APP_URL}/app/strava/connections/add', + placeholder: null, + description: + 'When asked to input an OAuth callback or redirect URL in Strava OAuth, enter the URL above.', + clickToCopy: true, + }, + { + key: 'consumerKey', + label: 'Consumer Key', + type: 'string' as const, + required: true, + readOnly: false, + value: null, + placeholder: null, + description: null, + clickToCopy: false, + }, + { + key: 'consumerSecret', + label: 'Consumer Secret', + type: 'string' as const, + required: true, + readOnly: false, + value: null, + placeholder: null, + description: null, + clickToCopy: false, + }, + ], + authenticationSteps: [ + { + step: 1, + type: 'mutation' as const, + name: 'createConnection', + arguments: [ + { + name: 'key', + value: '{key}', + }, + { + name: 'formattedData', + value: null, + properties: [ + { + name: 'consumerKey', + value: '{fields.consumerKey}', + }, + { + name: 'consumerSecret', + value: '{fields.consumerSecret}', + }, + ], + }, + ], + }, + { + step: 2, + type: 'mutation' as const, + name: 'createAuthData', + arguments: [ + { + name: 'id', + value: '{createConnection.id}', + }, + ], + }, + { + step: 3, + type: 'openWithPopup' as const, + name: 'openAuthPopup', + arguments: [ + { + name: 'url', + value: '{createAuthData.url}', + }, + ], + }, + { + step: 4, + type: 'mutation' as const, + name: 'updateConnection', + arguments: [ + { + name: 'id', + value: '{createConnection.id}', + }, + { + name: 'formattedData', + value: null, + properties: [ + { + name: 'code', + value: '{openAuthPopup.code}', + }, + ], + }, + ], + }, + { + step: 5, + type: 'mutation' as const, + name: 'verifyConnection', + arguments: [ + { + name: 'id', + value: '{createConnection.id}', + }, + ], + }, + ], + + createAuthData, + verifyCredentials, + isStillVerified, +}; diff --git a/packages/backend/src/apps/strava/auth/verify-credentials.ts b/packages/backend/src/apps/strava/auth/verify-credentials.ts new file mode 100644 index 00000000..1226530d --- /dev/null +++ b/packages/backend/src/apps/strava/auth/verify-credentials.ts @@ -0,0 +1,28 @@ +import qs from 'qs'; +import { IGlobalVariable } from '@automatisch/types'; + +const verifyCredentials = async ($: IGlobalVariable) => { + try { + const searchParams = { + client_id: $.auth.data.consumerKey, + client_secret: $.auth.data.consumerSecret, + code: $.auth.data.code, + grant_type: 'authorization_code', + }; + const { data } = await $.http.post( + `/v3/oauth/token?${qs.stringify(searchParams)}a`, + ); + + await $.auth.set({ + accessToken: data.access_token, + refreshToken: data.refresh_token, + tokenType: data.token_type, + userId: data.athlete.id, + screenName: `${data.athlete.firstname} ${data.athlete.lastname}`, + }); + } catch (error) { + throw new Error('Error occured while verifying credentials!'); + } +}; + +export default verifyCredentials; diff --git a/packages/backend/src/apps/strava/common/add-auth-header.ts b/packages/backend/src/apps/strava/common/add-auth-header.ts new file mode 100644 index 00000000..2420110a --- /dev/null +++ b/packages/backend/src/apps/strava/common/add-auth-header.ts @@ -0,0 +1,13 @@ +import { TBeforeRequest } from '@automatisch/types'; + +const addAuthHeader: TBeforeRequest = ($, requestConfig) => { + const { accessToken, tokenType } = $.auth.data; + + if (accessToken && tokenType) { + requestConfig.headers.Authorization = `${tokenType} ${accessToken}`; + } + + return requestConfig; +}; + +export default addAuthHeader; diff --git a/packages/backend/src/apps/strava/index.ts b/packages/backend/src/apps/strava/index.ts index 1d446bea..2a38b3b9 100644 --- a/packages/backend/src/apps/strava/index.ts +++ b/packages/backend/src/apps/strava/index.ts @@ -1,4 +1,6 @@ import defineApp from '../../helpers/define-app'; +import addAuthHeader from './common/add-auth-header'; +import auth from './auth'; export default defineApp({ name: 'Strava', @@ -9,4 +11,6 @@ export default defineApp({ baseUrl: 'https://www.strava.com', apiBaseUrl: 'https://www.strava.com/api', primaryColor: 'fc4c01', + beforeRequest: [addAuthHeader], + auth, });