Skip to content

Commit 2408953

Browse files
radenkovicAaronDDM
andauthored
Allow custom code exchange (#12)
Co-authored-by: Aaron de Mello <314152+AaronDDM@users.noreply.github.com>
1 parent 8951348 commit 2408953

File tree

7 files changed

+657
-7
lines changed

7 files changed

+657
-7
lines changed

.changeset/custom-code-exchange.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
"@nylas/connect": major
3+
---
4+
5+
Add custom code exchange functionality for enhanced security.
6+
7+
### Usage
8+
9+
```typescript
10+
// Handle code exchange on your backend
11+
const nylasConnect = new NylasConnect({
12+
clientId: 'your-client-id',
13+
redirectUri: 'http://localhost:3000/callback',
14+
codeExchange: async (params) => {
15+
const response = await fetch('/api/auth/exchange', {
16+
method: 'POST',
17+
body: JSON.stringify(params),
18+
});
19+
return await response.json();
20+
}
21+
});
22+
23+
// Use normally - custom exchange is called automatically
24+
const result = await nylasConnect.connect({ method: 'popup' });
25+
```
26+

.github/workflows/pr-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ concurrency:
1616
jobs:
1717
test:
1818
name: Test Suite
19-
runs-on: blacksmith-2vcpu-ubuntu-2404
19+
runs-on: ubuntu-latest
2020
timeout-minutes: 15
2121

2222
steps:

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ concurrency: ${{ github.workflow }}-${{ github.ref }}
1212
jobs:
1313
release:
1414
name: Release
15-
runs-on: blacksmith-2vcpu-ubuntu-2404
15+
runs-on: ubuntu-latest
1616
permissions:
1717
contents: write # to create release commits and tags
1818
pull-requests: write # to create release PRs

packages/nylas-connect/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,81 @@ try {
119119
| `apiUrl` | `string` | `https://api.us.nylas.com` | API base URL |
120120
| `persistTokens` | `boolean` | `true` | Store tokens in localStorage |
121121
| `debug` | `boolean` | `true` on localhost | Enable debug logging |
122+
| `codeExchange` | (param: CodeExchangeParams) => Promise<ConnectResult>` | - | Custom code exchange method |
123+
124+
## Custom Code Exchange
125+
126+
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.
127+
128+
### Backend Code Exchange
129+
130+
```typescript
131+
const nylasConnect = new NylasConnect({
132+
clientId: 'your-nylas-client-id',
133+
redirectUri: 'http://localhost:3000/auth/callback',
134+
codeExchange: async (params) => {
135+
// Send the authorization code to your backend
136+
const response = await fetch('/api/auth/exchange', {
137+
method: 'POST',
138+
headers: { 'Content-Type': 'application/json' },
139+
body: JSON.stringify({
140+
code: params.code,
141+
state: params.state,
142+
clientId: params.clientId,
143+
redirectUri: params.redirectUri,
144+
scopes: params.scopes,
145+
provider: params.provider,
146+
}),
147+
});
148+
149+
if (!response.ok) {
150+
throw new Error(`Token exchange failed: ${response.statusText}`);
151+
}
152+
153+
const tokenData = await response.json();
154+
155+
// Return the expected ConnectResult format
156+
return {
157+
accessToken: tokenData.access_token,
158+
idToken: tokenData.id_token,
159+
grantId: tokenData.grant_id,
160+
expiresAt: Date.now() + tokenData.expires_in * 1000,
161+
scope: tokenData.scope,
162+
grantInfo: tokenData.grant_info,
163+
};
164+
}
165+
});
166+
167+
// Use normally - the custom exchange will be called automatically
168+
const result = await nylasConnect.connect({ method: 'popup' });
169+
```
170+
171+
### Backend Implementation Example
172+
173+
```typescript
174+
// Example backend endpoint (/api/auth/exchange)
175+
export async function POST(request: Request) {
176+
const { code, clientId, redirectUri } = await request.json();
177+
178+
// Exchange code for tokens using your API key
179+
const response = await fetch('https://api.us.nylas.com/connect/token', {
180+
method: 'POST',
181+
headers: {
182+
'Content-Type': 'application/x-www-form-urlencoded',
183+
'Authorization': `Bearer ${process.env.NYLAS_API_KEY}`,
184+
},
185+
body: new URLSearchParams({
186+
client_id: clientId,
187+
redirect_uri: redirectUri,
188+
code,
189+
grant_type: 'authorization_code',
190+
}),
191+
});
192+
193+
const tokenData = await response.json();
194+
return Response.json(tokenData);
195+
}
196+
```
122197

123198
## API
124199

0 commit comments

Comments
 (0)