194 lines
9.8 KiB
Plaintext
194 lines
9.8 KiB
Plaintext
---
|
|
title: Conexões
|
|
description: Permita que seu aplicativo aja em nome de um usuário em serviços de terceiros via OAuth.
|
|
icon: plug
|
|
---
|
|
|
|
Conexões são credenciais que um usuário mantém para um serviço externo (Linear, GitHub, Slack, ...). Seu app declara **como** essas credenciais são obtidas — um **provedor de conexão** — e as consome em tempo de execução para fazer chamadas autenticadas à API de terceiros.
|
|
|
|
Atualmente, apenas o OAuth 2.0 tem suporte. Tipos de credenciais futuros (tokens de acesso pessoal, chaves de API, autenticação básica) serão conectados à mesma interface — apps que já usam `defineConnectionProvider({ type: 'oauth', ... })` não precisarão migrar.
|
|
|
|
<AccordionGroup>
|
|
|
|
<Accordion title="defineConnectionProvider" description="Declare como as conexões do seu app são obtidas">
|
|
|
|
Um provedor de conexão descreve o handshake OAuth de que seu app precisa. O usuário clica em "Adicionar conexão" nas configurações do seu app, conclui a tela de consentimento do provedor e uma linha `ConnectedAccount` é criada no seu workspace.
|
|
|
|
Uma configuração funcional precisa de **dois arquivos** — o provedor de conexão e uma declaração correspondente de `serverVariables` em `defineApplication` que contém as credenciais do cliente OAuth.
|
|
|
|
```ts src/connection-providers/linear-connection.ts
|
|
import { defineConnectionProvider } from 'twenty-sdk/define';
|
|
|
|
export default defineConnectionProvider({
|
|
universalIdentifier: '9c7d1f5e-6a0b-4d44-be0c-3f8b5a9d4e6f',
|
|
name: 'linear',
|
|
displayName: 'Linear',
|
|
icon: 'IconBrandLinear',
|
|
type: 'oauth',
|
|
oauth: {
|
|
authorizationEndpoint: 'https://linear.app/oauth/authorize',
|
|
tokenEndpoint: 'https://api.linear.app/oauth/token',
|
|
scopes: ['read', 'write'],
|
|
// These must match keys in `defineApplication.serverVariables` below.
|
|
clientIdVariable: 'LINEAR_CLIENT_ID',
|
|
clientSecretVariable: 'LINEAR_CLIENT_SECRET',
|
|
// Optional: defaults to 'json'. Some providers (Linear, Slack) want
|
|
// 'form-urlencoded' for the token request.
|
|
tokenRequestContentType: 'form-urlencoded',
|
|
// Optional: defaults to true. Disable only if the provider rejects PKCE.
|
|
usePkce: false,
|
|
// Optional: extra query params on the authorize URL.
|
|
// authorizationParams: { prompt: 'consent' },
|
|
// Optional: provider's RFC 7009 token revocation endpoint, called on disconnect.
|
|
// revokeEndpoint: 'https://example.com/oauth/revoke',
|
|
},
|
|
});
|
|
```
|
|
|
|
```ts src/application.config.ts
|
|
import { defineApplication } from 'twenty-sdk/define';
|
|
|
|
export default defineApplication({
|
|
universalIdentifier: '...',
|
|
displayName: 'Linear',
|
|
description: 'Connect Linear to Twenty.',
|
|
defaultRoleUniversalIdentifier: '...',
|
|
// OAuth client credentials live on the app registration (one OAuth app per
|
|
// Twenty server, configured by the admin) — not per-workspace. Declare them
|
|
// as serverVariables so the admin can fill them in once for all installs.
|
|
serverVariables: {
|
|
LINEAR_CLIENT_ID: {
|
|
description: 'OAuth client ID from your Linear OAuth application.',
|
|
isSecret: false,
|
|
isRequired: true,
|
|
},
|
|
LINEAR_CLIENT_SECRET: {
|
|
description: 'OAuth client secret from your Linear OAuth application.',
|
|
isSecret: true,
|
|
isRequired: true,
|
|
},
|
|
},
|
|
});
|
|
```
|
|
|
|
Pontos-chave:
|
|
|
|
* `name` é a string de identificador exclusivo usada em `listConnections({ providerName })` (kebab-case, deve corresponder a `^[a-z][a-z0-9-]*$`).
|
|
* `displayName` aparece na aba de configurações do app e na lista de ferramentas de IA.
|
|
* `clientIdVariable` / `clientSecretVariable` são **nomes**, não valores — devem corresponder às chaves declaradas em `defineApplication.serverVariables`. Os `client_id` e `client_secret` reais são inseridos pelo administrador do servidor por meio da interface de registro do app e nunca são versionados no seu repositório.
|
|
* Use `serverVariables` (não `applicationVariables`) — as credenciais OAuth são do servidor como um todo e há um app OAuth por servidor do Twenty.
|
|
* Até que ambos os `serverVariables` sejam preenchidos, a aba de configurações do app mostra uma dica "precisa de administrador do servidor" e o botão "Adicionar conexão" fica desativado.
|
|
* `type: 'oauth'` é o único valor compatível atualmente. O discriminador é compatível com versões futuras: tipos futuros (`'pat'`, `'api-key'`, ...) adicionarão novos blocos de subconfiguração ao lado de `oauth`.
|
|
|
|
O URL de callback do OAuth que seu provedor precisa adicionar à lista de permissões é:
|
|
|
|
```
|
|
https://<your-twenty-server>/apps/oauth/callback
|
|
```
|
|
|
|
</Accordion>
|
|
|
|
<Accordion title="listConnections / getConnection" description="Use conexões a partir de uma função de lógica">
|
|
|
|
Dentro de um handler de função de lógica, `listConnections({ providerName })` retorna as linhas `ConnectedAccount` deste app para o provedor fornecido, com tokens de acesso atualizados.
|
|
|
|
```ts src/logic-functions/handlers/create-linear-issue-handler.ts
|
|
import { listConnections } from 'twenty-sdk/logic-function';
|
|
|
|
export const createLinearIssueHandler = async (input: {
|
|
teamId?: string;
|
|
title?: string;
|
|
}) => {
|
|
if (!input.teamId || !input.title) {
|
|
return { success: false, error: 'teamId and title are required' };
|
|
}
|
|
|
|
const connections = await listConnections({ providerName: 'linear' });
|
|
|
|
// Workspace-shared credentials win when present; fall back to the first
|
|
// user-visibility one. For HTTP-route triggers you typically pick the
|
|
// request user's connection via event.userWorkspaceId instead.
|
|
const connection =
|
|
connections.find((c) => c.visibility === 'workspace') ?? connections[0];
|
|
|
|
if (!connection) {
|
|
return {
|
|
success: false,
|
|
error:
|
|
'Linear is not connected. Open the app settings and click "Add connection".',
|
|
};
|
|
}
|
|
|
|
// Use connection.accessToken to call the third-party API.
|
|
const response = await fetch('https://api.linear.app/graphql', {
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: `Bearer ${connection.accessToken}`,
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
query: `mutation { issueCreate(input: { teamId: "${input.teamId}", title: "${input.title}" }) { success } }`,
|
|
}),
|
|
});
|
|
|
|
return { success: response.ok };
|
|
};
|
|
```
|
|
|
|
Cada conexão tem:
|
|
|
|
| Campo | Descrição |
|
|
| ----------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
| `id` | ID de linha exclusivo; passe para `getConnection(id)` para buscar novamente um único registro |
|
|
| `visibilidade` | `'user'` (privada para um membro do workspace) ou `'workspace'` (compartilhada com todos os membros) |
|
|
| `escopos` | Permissões OAuth concedidas pelo provedor de origem (distintas de `visibility` — não têm relação) |
|
|
| `userWorkspaceId` | O id de userWorkspace do proprietário — útil para selecionar "a conexão do usuário da requisição" em gatilhos de rota HTTP |
|
|
| `accessToken` | Token de acesso OAuth recente (atualizado automaticamente se estiver expirado) |
|
|
| `name` / `handle` | O nome de exibição da conexão (derivado automaticamente no callback do OAuth, renomeável pelo usuário) |
|
|
| `authFailedAt` | Definido quando a atualização mais recente falhou; o usuário deve reconectar |
|
|
|
|
Pontos-chave:
|
|
|
|
* Passe `{ providerName }` para filtrar por provedor; omita para obter todas as conexões que este app possui em todos os provedores.
|
|
* O servidor atualiza transparentemente o token de acesso antes de retornar. Seu handler sempre vê um token utilizável (ou `authFailedAt` definido).
|
|
* `getConnection(id)` é o equivalente de uma única linha.
|
|
|
|
</Accordion>
|
|
|
|
<Accordion title="Visibilidade por usuário vs. compartilhada no workspace" description="Como os usuários escolhem entre credenciais privadas e compartilhadas">
|
|
|
|
Quando um usuário clica em "Adicionar conexão", é solicitado que escolha uma visibilidade:
|
|
|
|
* **Apenas para mim** — a credencial é privada para o usuário que a conectou. Qualquer função de lógica chamada em seu nome (gatilho de rota HTTP com `isAuthRequired: true`) a vê; gatilhos cron e eventos de banco de dados não.
|
|
* **Compartilhada no workspace** — qualquer membro do workspace pode usar a credencial. Gatilhos de cron / banco de dados também a veem, pois não há um usuário da requisição.
|
|
|
|
Use a adequada para cada handler:
|
|
|
|
```ts
|
|
// HTTP-route trigger — prefer the request user's own connection.
|
|
const conn =
|
|
connections.find((c) => c.userWorkspaceId === event.userWorkspaceId) ??
|
|
connections.find((c) => c.visibility === 'workspace');
|
|
|
|
// Cron trigger — no request user; only shared credentials are sensible.
|
|
const conn = connections.find((c) => c.visibility === 'workspace');
|
|
```
|
|
|
|
Várias conexões por (usuário, provedor) são permitidas, então o mesmo usuário pode manter "Linear pessoal" e "Linear de trabalho" lado a lado.
|
|
|
|
</Accordion>
|
|
|
|
<Accordion title="Configuração única do provedor" description="Registre seu app OAuth no serviço de terceiros">
|
|
|
|
Para cada provedor de conexão, o administrador do servidor precisa primeiro registrar um app OAuth no serviço de terceiros.
|
|
|
|
1. Acesse as configurações de desenvolvedor do provedor (por exemplo, https://linear.app/settings/api/applications/new).
|
|
2. Defina a **URI de redirecionamento** como `\<SERVER_URL>/apps/oauth/callback`.
|
|
3. Copie o **ID do cliente** e o **Segredo do cliente** gerados.
|
|
4. Abra o app instalado no Twenty como administrador do servidor → defina os valores nos `serverVariables` correspondentes.
|
|
5. Os membros do workspace podem então adicionar conexões na seção **Conexões** de cada app.
|
|
|
|
</Accordion>
|
|
|
|
</AccordionGroup>
|