Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
26 changes: 26 additions & 0 deletions .changeset/custom-code-exchange.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
"@nylas/connect": major
---

Add custom code exchange functionality for enhanced security.

### Usage

```typescript
// Handle code exchange on your backend
const nylasConnect = new NylasConnect({
clientId: 'your-client-id',
redirectUri: 'http://localhost:3000/callback',
codeExchange: async (params) => {
const response = await fetch('/api/auth/exchange', {
method: 'POST',
body: JSON.stringify(params),
});
return await response.json();
}
});

// Use normally - custom exchange is called automatically
const result = await nylasConnect.connect({ method: 'popup' });
```

2 changes: 1 addition & 1 deletion .github/workflows/pr-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ concurrency:
jobs:
test:
name: Test Suite
runs-on: blacksmith-2vcpu-ubuntu-2404
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
release:
name: Release
runs-on: blacksmith-2vcpu-ubuntu-2404
runs-on: ubuntu-latest
permissions:
contents: write # to create release commits and tags
pull-requests: write # to create release PRs
Expand Down
75 changes: 75 additions & 0 deletions packages/nylas-connect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,81 @@ try {
| `apiUrl` | `string` | `https://api.us.nylas.com` | API base URL |
| `persistTokens` | `boolean` | `true` | Store tokens in localStorage |
| `debug` | `boolean` | `true` on localhost | Enable debug logging |
| `codeExchange` | (param: CodeExchangeParams) => Promise<ConnectResult>` | - | Custom code exchange method |

## Custom Code Exchange

For enhanced security, you can handle the OAuth code exchange on your backend instead of in the browser. This approach keeps your API keys secure and gives you full control over the token exchange process.

### Backend Code Exchange

```typescript
const nylasConnect = new NylasConnect({
clientId: 'your-nylas-client-id',
redirectUri: 'http://localhost:3000/auth/callback',
codeExchange: async (params) => {
// Send the authorization code to your backend
const response = await fetch('/api/auth/exchange', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code: params.code,
state: params.state,
clientId: params.clientId,
redirectUri: params.redirectUri,
scopes: params.scopes,
provider: params.provider,
}),
});

if (!response.ok) {
throw new Error(`Token exchange failed: ${response.statusText}`);
}

const tokenData = await response.json();

// Return the expected ConnectResult format
return {
accessToken: tokenData.access_token,
idToken: tokenData.id_token,
grantId: tokenData.grant_id,
expiresAt: Date.now() + tokenData.expires_in * 1000,
scope: tokenData.scope,
grantInfo: tokenData.grant_info,
};
}
});

// Use normally - the custom exchange will be called automatically
const result = await nylasConnect.connect({ method: 'popup' });
```

### Backend Implementation Example

```typescript
// Example backend endpoint (/api/auth/exchange)
export async function POST(request: Request) {
const { code, clientId, redirectUri } = await request.json();

// Exchange code for tokens using your API key
const response = await fetch('https://api.us.nylas.com/connect/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Bearer ${process.env.NYLAS_API_KEY}`,
},
body: new URLSearchParams({
client_id: clientId,
redirect_uri: redirectUri,
code,
grant_type: 'authorization_code',
}),
});

const tokenData = await response.json();
return Response.json(tokenData);
}
```

## API

Expand Down
Loading