Skip to content
This repository has been archived by the owner on Oct 31, 2024. It is now read-only.

Commit

Permalink
feat: Allow integration to determine oauth scope via preConnect
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyxiao committed Oct 31, 2024
1 parent ed58d57 commit 3a1f35a
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 19 deletions.
36 changes: 33 additions & 3 deletions connectors/connector-google/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,57 @@ export const googleServer = {
// body: JSON.stringify(input.body),
// }),
// eslint-disable-next-line @typescript-eslint/require-await
async preConnect(_, context) {
// This returns auth options for Nango connect because it is an oauth integration
// this behavior is not type checked though and could use some improvement
// May be fixed if we turn nango into a connector
if (context.integrationExternalId === 'drive') {
return {
authorization_params: {
scope: 'https://www.googleapis.com/auth/drive',
},
}
}
if (context.integrationExternalId === 'calendar') {
return {
authorization_params: {
scope: 'https://www.googleapis.com/auth/calendar',
},
}
}
if (context.integrationExternalId === 'drive') {
return {
authorization_params: {
scope: 'https://www.googleapis.com/auth/gmail.readonly',
// • https://www.googleapis.com/auth/gmail.send (Send only)
// TODO: How do we determine more specific scopes here?
},
}
}
return {}
},
// eslint-disable-next-line @typescript-eslint/require-await
async listIntegrations() {
return {
has_next_page: false,
items: [
{
id: 'google-drive',
id: 'drive',
name: 'Google Drive',
// TODO: Differ oauth scope use in Connect based on which integration
raw_data: {} as any,
updated_at: new Date().toISOString(),
logo_url: '/_assets/logo-google-drive.svg',
},
{
id: 'google-gmail',
id: 'gmail',
name: 'Gmail',
raw_data: {} as any,
updated_at: new Date().toISOString(),
logo_url: '/_assets/logo-google-gmail.svg',
},
{
id: 'google-calendar',
id: 'calendar',
name: 'Google Calendar',
raw_data: {} as any,
updated_at: new Date().toISOString(),
Expand Down
19 changes: 15 additions & 4 deletions kits/cdk/internal/oauthConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,23 +80,34 @@ export function oauthConnect({
connectorName,
connectorConfigId,
resourceId,
authOptions,
}: {
nangoFrontend: NangoFrontend
connectorName: string
connectorConfigId: Id['ccfg']
/** Should address the re-connect scenario, but let's see... */
resourceId?: Id['reso']
authOptions?: {
authorization_params?: Record<string, string | undefined>
}
}): Promise<OauthBaseTypes['connectOutput']> {
// console.log('oauthConnect', {
// connectorName,
// connectorConfigId,
// resourceId,
// authOptions,
// })
return nangoFrontend
.auth(
connectorConfigId,
resourceId ?? makeId('reso', connectorName, makeUlid()),
{
params: {},
authorization_params: {
// TODO: Add integration specific scope right here...
scope: 'https://www.googleapis.com/auth/drive.readonly',
},
...authOptions,
// authOptions would tend to contain the authorization_params needed to make the initial connection
// authorization_params: {
// scope: 'https://www.googleapis.com/auth/drive.readonly',
// },
},
)
.then((r) => oauthBaseSchema.connectOutput.parse(r))
Expand Down
9 changes: 2 additions & 7 deletions packages/engine-backend/router/connectorRouter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import {zodToOas31Schema} from '@opensdks/util-zod'
import {
extractId,
metaForConnector,
zId,
zVerticalKey,
} from '@openint/cdk'
import {extractId, metaForConnector, zId, zVerticalKey} from '@openint/cdk'
import type {RouterMeta} from '@openint/trpc'
import {TRPCError} from '@openint/trpc'
import {R, z} from '@openint/util'
Expand Down Expand Up @@ -156,7 +151,7 @@ const _connectorRouter = trpc.router({
...res,
items: res.items.map((item) => ({
...item,
id: `${name}_${item.id}`,
id: `int_${name}_${item.id}`,
connector_name: name,
})),
}))
Expand Down
2 changes: 2 additions & 0 deletions packages/engine-frontend/components/IntegrationSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import {Loader, Search} from 'lucide-react'
import React from 'react'
import {Id} from '@openint/cdk'
import {Input} from '@openint/ui'
import {ConnectionCard} from '@openint/ui/domain-components/ConnectionCard'
import type {ConnectorConfig} from '../hocs/WithConnectConfig'
Expand Down Expand Up @@ -92,6 +93,7 @@ export function IntegrationSearch({
id: int.connector_config_id,
connector: int.ccfg.connector,
}}
integration={{id: int.id as Id['int']}}
onEvent={(e) => {
onEvent?.({
type: e.type,
Expand Down
19 changes: 14 additions & 5 deletions packages/engine-frontend/hocs/WithConnectorConnect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,15 @@ const __DEBUG__ = Boolean(

export const WithConnectorConnect = ({
connectorConfig: ccfg,
integration,
resource,
onEvent,
children,
}: {
connectorConfig: {id: Id['ccfg']; connector: ConnectorMeta}
integration?: {
id: Id['int']
}
resource?: Resource
onEvent?: (event: {type: ConnectEventType}) => void
children: (props: {
Expand Down Expand Up @@ -77,23 +81,28 @@ export const WithConnectorConnect = ({
openDialog: () => {},
}) ??
(nangoProvider
? (_, {connectorConfigId}) => {
? (connInput, {connectorConfigId}) => {
if (!nangoFrontend) {
throw new Error('Missing nango public key')
}
return oauthConnect({
connectorConfigId,
nangoFrontend,
connectorName: ccfg.connector.name,
resourceId: resource?.id,
authOptions: connInput,
})
}
: undefined)

const resourceExternalId = resource ? extractId(resource.id)[2] : undefined
const integrationExternalId = integration
? extractId(integration.id)[2]
: undefined

// TODO: Handle preConnectInput schema and such... for example for Plaid
const preConnect = _trpcReact.preConnect.useQuery(
[ccfg.id, {resourceExternalId}, {}],
[ccfg.id, {resourceExternalId, integrationExternalId}, {}],
{enabled: ccfg.connector.hasPreConnect},
)
const postConnect = _trpcReact.postConnect.useMutation()
Expand Down Expand Up @@ -150,7 +159,7 @@ export const WithConnectorConnect = ({
if (err === CANCELLATION_TOKEN) {
return
}
console.log(ccfg.connector.displayName + ' connection error:',err)
console.log(ccfg.connector.displayName + ' connection error:', err)
toast({
title: `Failed to connect to ${ccfg.connector.displayName}`,
// description: `${err}`,
Expand Down Expand Up @@ -205,8 +214,8 @@ export const WithConnectorConnect = ({
</span>
{ccfg.connector.name === 'greenhouse' && (
<div className="relative inline-block">
<InfoIcon className="h-5 w-5 cursor-help text-gray-500 peer" />
<div className="absolute bottom-full left-1/2 mb-2 w-64 -translate-x-1/2 rounded-md bg-[#272731] p-2 text-sm text-white opacity-0 transition-opacity peer-hover:opacity-100 pointer-events-none">
<InfoIcon className="peer h-5 w-5 cursor-help text-gray-500" />
<div className="pointer-events-none absolute bottom-full left-1/2 mb-2 w-64 -translate-x-1/2 rounded-md bg-[#272731] p-2 text-sm text-white opacity-0 transition-opacity peer-hover:opacity-100">
<p className="italic">
Generate a custom API key with{' '}
<a
Expand Down

0 comments on commit 3a1f35a

Please sign in to comment.