mcp-twake-mail 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +663 -0
- package/README.md +332 -0
- package/build/auth/index.d.ts +3 -0
- package/build/auth/index.js +4 -0
- package/build/auth/index.js.map +1 -0
- package/build/auth/oidc-flow.d.ts +47 -0
- package/build/auth/oidc-flow.js +146 -0
- package/build/auth/oidc-flow.js.map +1 -0
- package/build/auth/token-refresh.d.ts +56 -0
- package/build/auth/token-refresh.js +132 -0
- package/build/auth/token-refresh.js.map +1 -0
- package/build/auth/token-store.d.ts +33 -0
- package/build/auth/token-store.js +63 -0
- package/build/auth/token-store.js.map +1 -0
- package/build/cli/commands/auth.d.ts +5 -0
- package/build/cli/commands/auth.js +49 -0
- package/build/cli/commands/auth.js.map +1 -0
- package/build/cli/commands/check.d.ts +4 -0
- package/build/cli/commands/check.js +125 -0
- package/build/cli/commands/check.js.map +1 -0
- package/build/cli/commands/setup.d.ts +4 -0
- package/build/cli/commands/setup.js +172 -0
- package/build/cli/commands/setup.js.map +1 -0
- package/build/cli/index.d.ts +15 -0
- package/build/cli/index.js +56 -0
- package/build/cli/index.js.map +1 -0
- package/build/cli/prompts/setup-wizard.d.ts +38 -0
- package/build/cli/prompts/setup-wizard.js +121 -0
- package/build/cli/prompts/setup-wizard.js.map +1 -0
- package/build/config/__tests__/logger.test.d.ts +1 -0
- package/build/config/__tests__/logger.test.js +28 -0
- package/build/config/__tests__/logger.test.js.map +1 -0
- package/build/config/__tests__/schema.test.d.ts +1 -0
- package/build/config/__tests__/schema.test.js +101 -0
- package/build/config/__tests__/schema.test.js.map +1 -0
- package/build/config/logger.d.ts +3 -0
- package/build/config/logger.js +9 -0
- package/build/config/logger.js.map +1 -0
- package/build/config/schema.d.ts +28 -0
- package/build/config/schema.js +81 -0
- package/build/config/schema.js.map +1 -0
- package/build/errors.d.ts +34 -0
- package/build/errors.js +154 -0
- package/build/errors.js.map +1 -0
- package/build/errors.test.d.ts +1 -0
- package/build/errors.test.js +234 -0
- package/build/errors.test.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +12 -0
- package/build/index.js.map +1 -0
- package/build/jmap/client.d.ts +96 -0
- package/build/jmap/client.js +267 -0
- package/build/jmap/client.js.map +1 -0
- package/build/mcp/server.d.ts +24 -0
- package/build/mcp/server.js +68 -0
- package/build/mcp/server.js.map +1 -0
- package/build/mcp/tools/attachment.d.ts +30 -0
- package/build/mcp/tools/attachment.js +246 -0
- package/build/mcp/tools/attachment.js.map +1 -0
- package/build/mcp/tools/attachment.test.d.ts +1 -0
- package/build/mcp/tools/attachment.test.js +457 -0
- package/build/mcp/tools/attachment.test.js.map +1 -0
- package/build/mcp/tools/email-operations.d.ts +10 -0
- package/build/mcp/tools/email-operations.js +828 -0
- package/build/mcp/tools/email-operations.js.map +1 -0
- package/build/mcp/tools/email-operations.test.d.ts +1 -0
- package/build/mcp/tools/email-operations.test.js +453 -0
- package/build/mcp/tools/email-operations.test.js.map +1 -0
- package/build/mcp/tools/email-sending.d.ts +10 -0
- package/build/mcp/tools/email-sending.js +682 -0
- package/build/mcp/tools/email-sending.js.map +1 -0
- package/build/mcp/tools/email.d.ts +10 -0
- package/build/mcp/tools/email.js +365 -0
- package/build/mcp/tools/email.js.map +1 -0
- package/build/mcp/tools/email.test.d.ts +1 -0
- package/build/mcp/tools/email.test.js +332 -0
- package/build/mcp/tools/email.test.js.map +1 -0
- package/build/mcp/tools/index.d.ts +14 -0
- package/build/mcp/tools/index.js +29 -0
- package/build/mcp/tools/index.js.map +1 -0
- package/build/mcp/tools/mailbox.d.ts +10 -0
- package/build/mcp/tools/mailbox.js +195 -0
- package/build/mcp/tools/mailbox.js.map +1 -0
- package/build/mcp/tools/mailbox.test.d.ts +1 -0
- package/build/mcp/tools/mailbox.test.js +231 -0
- package/build/mcp/tools/mailbox.test.js.map +1 -0
- package/build/mcp/tools/thread.d.ts +10 -0
- package/build/mcp/tools/thread.js +282 -0
- package/build/mcp/tools/thread.js.map +1 -0
- package/build/mcp/tools/thread.test.d.ts +1 -0
- package/build/mcp/tools/thread.test.js +384 -0
- package/build/mcp/tools/thread.test.js.map +1 -0
- package/build/transformers/__tests__/email.test.d.ts +1 -0
- package/build/transformers/__tests__/email.test.js +438 -0
- package/build/transformers/__tests__/email.test.js.map +1 -0
- package/build/transformers/__tests__/mailbox.test.d.ts +1 -0
- package/build/transformers/__tests__/mailbox.test.js +222 -0
- package/build/transformers/__tests__/mailbox.test.js.map +1 -0
- package/build/transformers/email.d.ts +76 -0
- package/build/transformers/email.js +138 -0
- package/build/transformers/email.js.map +1 -0
- package/build/transformers/index.d.ts +5 -0
- package/build/transformers/index.js +6 -0
- package/build/transformers/index.js.map +1 -0
- package/build/transformers/mailbox.d.ts +43 -0
- package/build/transformers/mailbox.js +70 -0
- package/build/transformers/mailbox.js.map +1 -0
- package/build/types/dto.d.ts +91 -0
- package/build/types/dto.js +9 -0
- package/build/types/dto.js.map +1 -0
- package/build/types/jmap.d.ts +110 -0
- package/build/types/jmap.js +5 -0
- package/build/types/jmap.js.map +1 -0
- package/package.json +71 -0
package/README.md
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# mcp-twake-mail
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://nodejs.org/)
|
|
5
|
+
|
|
6
|
+
**MCP server for [Twake.ai](https://www.twake.ai/) — integrate your sovereign JMAP email server with any MCP-compatible AI assistant**
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
mcp-twake-mail is a Model Context Protocol (MCP) server that connects any MCP-compatible AI assistant (Claude Desktop, Claude Code, etc.) to your JMAP email server. Compatible with JMAP-compliant servers including Apache James, Cyrus IMAP, and other RFC 8620/8621 implementations.
|
|
13
|
+
|
|
14
|
+
**Key benefits:**
|
|
15
|
+
- Your data stays on your own servers — sovereign infrastructure
|
|
16
|
+
- Works with any MCP-compatible AI assistant
|
|
17
|
+
- Full control over email data — read, write, send, and organize
|
|
18
|
+
- Multiple authentication methods: Basic, Bearer token, or OIDC
|
|
19
|
+
- Secure HTTPS-only connections
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
**Email Read Tools:**
|
|
24
|
+
- `list_emails` - List emails with filtering, search, and pagination
|
|
25
|
+
- `get_email` - Get full email content including body and headers
|
|
26
|
+
- `search_emails` - Search emails by keywords
|
|
27
|
+
- `get_thread` - Get all emails in a conversation thread
|
|
28
|
+
|
|
29
|
+
**Email Compose Tools:**
|
|
30
|
+
- `send_email` - Compose and send a new email (plain text and/or HTML)
|
|
31
|
+
- `reply_email` - Reply to an email with proper threading (In-Reply-To, References)
|
|
32
|
+
- `create_draft` - Create a draft email for later editing or sending
|
|
33
|
+
|
|
34
|
+
**Email Management Tools:**
|
|
35
|
+
- `mark_as_read` - Mark an email as read
|
|
36
|
+
- `mark_as_unread` - Mark an email as unread
|
|
37
|
+
- `delete_email` - Move to trash or permanently delete
|
|
38
|
+
- `move_email` - Move an email to a different mailbox
|
|
39
|
+
- `add_label` - Add a label/mailbox to an email
|
|
40
|
+
- `remove_label` - Remove a label/mailbox from an email
|
|
41
|
+
|
|
42
|
+
**Mailbox Tools:**
|
|
43
|
+
- `list_mailboxes` - List all mailboxes (Inbox, Sent, Drafts, etc.)
|
|
44
|
+
|
|
45
|
+
**Attachment Tools:**
|
|
46
|
+
- `get_attachments` - List attachment metadata for an email
|
|
47
|
+
- `download_attachment` - Download attachment content (auto-saves large files to disk)
|
|
48
|
+
|
|
49
|
+
**Advanced Features:**
|
|
50
|
+
- OIDC authentication with PKCE (S256) for enterprise SSO
|
|
51
|
+
- Automatic token refresh for OIDC sessions
|
|
52
|
+
- Thread-based email grouping
|
|
53
|
+
- Inline vs regular attachment detection
|
|
54
|
+
- Large attachment handling (auto-saves to ~/Downloads for files > 750KB)
|
|
55
|
+
- MCP tool annotations (readOnlyHint, destructiveHint, idempotentHint)
|
|
56
|
+
|
|
57
|
+
## Prerequisites
|
|
58
|
+
|
|
59
|
+
- **Node.js** >= 20.0.0
|
|
60
|
+
- **JMAP Server** - A JMAP-compliant email server such as:
|
|
61
|
+
- Apache James
|
|
62
|
+
- Cyrus IMAP
|
|
63
|
+
- Stalwart Mail Server
|
|
64
|
+
- Fastmail
|
|
65
|
+
- **MCP-compatible AI assistant** - Claude Desktop, Claude Code, or any MCP client
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
**From source:**
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/linagora/mcp-twake-mail.git
|
|
72
|
+
cd mcp-twake-mail
|
|
73
|
+
npm install
|
|
74
|
+
npm run build
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Quick Setup (Recommended)
|
|
78
|
+
|
|
79
|
+
The easiest way to configure mcp-twake-mail is to use the interactive setup wizard:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npx mcp-twake-mail setup
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The wizard will:
|
|
86
|
+
1. Ask for your JMAP server session URL
|
|
87
|
+
2. Ask for your authentication method (Basic, Bearer, or OIDC)
|
|
88
|
+
3. Collect the required credentials
|
|
89
|
+
4. Test the connection to your JMAP server
|
|
90
|
+
5. Generate and optionally write the configuration to your Claude Desktop config file
|
|
91
|
+
|
|
92
|
+
Example session:
|
|
93
|
+
```
|
|
94
|
+
=== MCP Twake Mail Setup Wizard ===
|
|
95
|
+
|
|
96
|
+
JMAP Session URL: https://jmap.example.com/jmap/session
|
|
97
|
+
|
|
98
|
+
Authentication method:
|
|
99
|
+
1. Basic (username/password)
|
|
100
|
+
2. Bearer token (JWT)
|
|
101
|
+
3. OIDC (OpenID Connect)
|
|
102
|
+
Choose [1-3]: 1
|
|
103
|
+
|
|
104
|
+
Username: user@example.com
|
|
105
|
+
Password: ********
|
|
106
|
+
|
|
107
|
+
Testing connection...
|
|
108
|
+
Connected! Account ID: abc123
|
|
109
|
+
|
|
110
|
+
Server name for Claude config [twake-mail]: twake-mail
|
|
111
|
+
|
|
112
|
+
--- Generated Claude Desktop Config ---
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"twake-mail": {
|
|
116
|
+
"command": "npx",
|
|
117
|
+
"args": ["-y", "mcp-twake-mail"],
|
|
118
|
+
"env": {
|
|
119
|
+
"JMAP_SESSION_URL": "https://jmap.example.com/jmap/session",
|
|
120
|
+
"JMAP_AUTH_METHOD": "basic",
|
|
121
|
+
"JMAP_USERNAME": "user@example.com",
|
|
122
|
+
"JMAP_PASSWORD": "********"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
---------------------------------------
|
|
128
|
+
|
|
129
|
+
Write to Claude Desktop config? [Y/n]: y
|
|
130
|
+
|
|
131
|
+
Config written successfully!
|
|
132
|
+
Restart Claude Desktop to load the new configuration.
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### CLI Commands
|
|
136
|
+
|
|
137
|
+
| Command | Description |
|
|
138
|
+
|---------|-------------|
|
|
139
|
+
| `mcp-twake-mail` | Start MCP server (default) |
|
|
140
|
+
| `mcp-twake-mail setup` | Interactive configuration wizard |
|
|
141
|
+
| `mcp-twake-mail auth` | Re-run OIDC authentication flow |
|
|
142
|
+
| `mcp-twake-mail check` | Verify configuration and test connection |
|
|
143
|
+
|
|
144
|
+
## Configuration
|
|
145
|
+
|
|
146
|
+
### Environment Variables
|
|
147
|
+
|
|
148
|
+
#### Basic Auth
|
|
149
|
+
|
|
150
|
+
| Variable | Required | Description | Example |
|
|
151
|
+
|----------|----------|-------------|---------|
|
|
152
|
+
| `JMAP_SESSION_URL` | Yes | JMAP session endpoint URL | `https://jmap.example.com/jmap/session` |
|
|
153
|
+
| `JMAP_AUTH_METHOD` | No | Set to `basic` (default) | `basic` |
|
|
154
|
+
| `JMAP_USERNAME` | Yes | Username for authentication | `user@example.com` |
|
|
155
|
+
| `JMAP_PASSWORD` | Yes | Password for authentication | `your-password` |
|
|
156
|
+
|
|
157
|
+
#### Bearer Token
|
|
158
|
+
|
|
159
|
+
| Variable | Required | Description | Example |
|
|
160
|
+
|----------|----------|-------------|---------|
|
|
161
|
+
| `JMAP_SESSION_URL` | Yes | JMAP session endpoint URL | `https://jmap.example.com/jmap/session` |
|
|
162
|
+
| `JMAP_AUTH_METHOD` | Yes | Must be set to `bearer` | `bearer` |
|
|
163
|
+
| `JMAP_TOKEN` | Yes | JWT Bearer token | `eyJhbGciOiJSUzI1NiIs...` |
|
|
164
|
+
|
|
165
|
+
#### OIDC Authentication
|
|
166
|
+
|
|
167
|
+
For enterprise SSO with OpenID Connect (PKCE S256):
|
|
168
|
+
|
|
169
|
+
| Variable | Required | Description | Example |
|
|
170
|
+
|----------|----------|-------------|---------|
|
|
171
|
+
| `JMAP_SESSION_URL` | Yes | JMAP session endpoint URL | `https://jmap.example.com/jmap/session` |
|
|
172
|
+
| `JMAP_AUTH_METHOD` | Yes | Must be set to `oidc` | `oidc` |
|
|
173
|
+
| `JMAP_OIDC_ISSUER` | Yes | OIDC provider issuer URL | `https://sso.example.com` |
|
|
174
|
+
| `JMAP_OIDC_CLIENT_ID` | Yes | OIDC client ID | `my-client-id` |
|
|
175
|
+
| `JMAP_OIDC_SCOPE` | No | OIDC scopes | `openid profile email offline_access` |
|
|
176
|
+
| `JMAP_OIDC_REDIRECT_URI` | No | Callback URI for OIDC flow | `http://localhost:5678/callback` |
|
|
177
|
+
|
|
178
|
+
#### Optional
|
|
179
|
+
|
|
180
|
+
| Variable | Description | Default |
|
|
181
|
+
|----------|-------------|---------|
|
|
182
|
+
| `LOG_LEVEL` | Log verbosity: `fatal`, `error`, `warn`, `info`, `debug`, `trace` | `info` |
|
|
183
|
+
| `JMAP_REQUEST_TIMEOUT` | Request timeout in milliseconds | `30000` |
|
|
184
|
+
|
|
185
|
+
### Claude Desktop Configuration
|
|
186
|
+
|
|
187
|
+
Add the following to your Claude Desktop configuration file:
|
|
188
|
+
|
|
189
|
+
**Configuration file location:**
|
|
190
|
+
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
191
|
+
- **Linux:** `~/.config/Claude/claude_desktop_config.json`
|
|
192
|
+
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
193
|
+
|
|
194
|
+
**Configuration (Basic Auth):**
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"mcpServers": {
|
|
199
|
+
"twake-mail": {
|
|
200
|
+
"command": "node",
|
|
201
|
+
"args": ["/path/to/mcp-twake-mail/build/index.js"],
|
|
202
|
+
"env": {
|
|
203
|
+
"JMAP_SESSION_URL": "https://jmap.example.com/jmap/session",
|
|
204
|
+
"JMAP_AUTH_METHOD": "basic",
|
|
205
|
+
"JMAP_USERNAME": "user@example.com",
|
|
206
|
+
"JMAP_PASSWORD": "your-password"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Configuration (OIDC):**
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
{
|
|
217
|
+
"mcpServers": {
|
|
218
|
+
"twake-mail": {
|
|
219
|
+
"command": "node",
|
|
220
|
+
"args": ["/path/to/mcp-twake-mail/build/index.js"],
|
|
221
|
+
"env": {
|
|
222
|
+
"JMAP_SESSION_URL": "https://jmap.example.com/jmap/session",
|
|
223
|
+
"JMAP_AUTH_METHOD": "oidc",
|
|
224
|
+
"JMAP_OIDC_ISSUER": "https://sso.example.com",
|
|
225
|
+
"JMAP_OIDC_CLIENT_ID": "my-client-id",
|
|
226
|
+
"JMAP_OIDC_SCOPE": "openid profile email offline_access",
|
|
227
|
+
"JMAP_OIDC_REDIRECT_URI": "http://localhost:5678/callback"
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
After updating the configuration, restart Claude Desktop for changes to take effect.
|
|
235
|
+
|
|
236
|
+
## Usage Examples
|
|
237
|
+
|
|
238
|
+
Once configured, you can ask Claude natural language questions about your email:
|
|
239
|
+
|
|
240
|
+
**Email queries:**
|
|
241
|
+
- "What are my unread emails?"
|
|
242
|
+
- "Show me emails from Pierre"
|
|
243
|
+
- "What's the latest email in my inbox?"
|
|
244
|
+
- "Find emails about the budget meeting"
|
|
245
|
+
- "Show the conversation thread for this email"
|
|
246
|
+
|
|
247
|
+
**Email composition:**
|
|
248
|
+
- "Send an email to pierre@example.com about the meeting tomorrow"
|
|
249
|
+
- "Reply to this email thanking them for the information"
|
|
250
|
+
- "Create a draft email to the team about the project update"
|
|
251
|
+
|
|
252
|
+
**Email management:**
|
|
253
|
+
- "Mark this email as read"
|
|
254
|
+
- "Move this email to the Archive folder"
|
|
255
|
+
- "Delete all spam emails"
|
|
256
|
+
- "Add the 'Important' label to this email"
|
|
257
|
+
|
|
258
|
+
**Attachments:**
|
|
259
|
+
- "What attachments are in this email?"
|
|
260
|
+
- "Download the PDF attachment"
|
|
261
|
+
|
|
262
|
+
## Available Tools
|
|
263
|
+
|
|
264
|
+
| Tool Name | Description |
|
|
265
|
+
|-----------|-------------|
|
|
266
|
+
| `list_emails` | List emails with optional filters (mailbox, limit, search) |
|
|
267
|
+
| `get_email` | Get full email content by ID |
|
|
268
|
+
| `search_emails` | Search emails by keywords |
|
|
269
|
+
| `get_thread` | Get all emails in a thread |
|
|
270
|
+
| `send_email` | Send a new email |
|
|
271
|
+
| `reply_email` | Reply to an email with threading |
|
|
272
|
+
| `create_draft` | Create a draft email |
|
|
273
|
+
| `mark_as_read` | Mark email as read |
|
|
274
|
+
| `mark_as_unread` | Mark email as unread |
|
|
275
|
+
| `delete_email` | Delete or trash an email |
|
|
276
|
+
| `move_email` | Move email to another mailbox |
|
|
277
|
+
| `add_label` | Add mailbox/label to email |
|
|
278
|
+
| `remove_label` | Remove mailbox/label from email |
|
|
279
|
+
| `list_mailboxes` | List all mailboxes |
|
|
280
|
+
| `get_attachments` | List attachment metadata |
|
|
281
|
+
| `download_attachment` | Download attachment content |
|
|
282
|
+
|
|
283
|
+
## Development
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
git clone https://github.com/linagora/mcp-twake-mail.git
|
|
287
|
+
cd mcp-twake-mail
|
|
288
|
+
npm install
|
|
289
|
+
npm run build # compile TypeScript
|
|
290
|
+
npm test # run tests
|
|
291
|
+
npm run dev # watch mode (auto-rebuild on file changes)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The server uses the MCP stdio transport and communicates via JSON-RPC on stdin/stdout.
|
|
295
|
+
|
|
296
|
+
## Architecture
|
|
297
|
+
|
|
298
|
+
mcp-twake-mail is built with a layered architecture:
|
|
299
|
+
|
|
300
|
+
1. **Configuration Layer** - Zod-based environment variable validation with fail-fast behavior
|
|
301
|
+
2. **Logging Layer** - Pino logger configured for stderr output (prevents stdout contamination in MCP stdio transport)
|
|
302
|
+
3. **Authentication Layer** - Multi-method auth support (Basic, Bearer, OIDC with PKCE)
|
|
303
|
+
4. **Token Management** - Automatic token refresh for OIDC with secure token storage
|
|
304
|
+
5. **JMAP Client Layer** - Session management, request batching, blob download support
|
|
305
|
+
6. **Transformation Layer** - Email/Mailbox data transformation for AI-friendly output
|
|
306
|
+
7. **MCP Tool Layer** - 16 MCP tools exposing email functionality with tool annotations
|
|
307
|
+
8. **Entry Point** - MCP server initialization with stdio transport
|
|
308
|
+
|
|
309
|
+
**Key design decisions:**
|
|
310
|
+
- ESM modules with `.js` import extensions (required by MCP SDK)
|
|
311
|
+
- JMAP RFC 8620/8621 compliance for broad server compatibility
|
|
312
|
+
- AI-friendly error formatting for troubleshooting
|
|
313
|
+
- Large attachment handling (auto-save to disk for files > 750KB)
|
|
314
|
+
- MCP tool annotations for AI clients (readOnlyHint, destructiveHint, idempotentHint)
|
|
315
|
+
|
|
316
|
+
## License
|
|
317
|
+
|
|
318
|
+
This project is licensed under the **GNU Affero General Public License v3.0 (AGPL-3.0)**.
|
|
319
|
+
|
|
320
|
+
See the [LICENSE](LICENSE) file for details.
|
|
321
|
+
|
|
322
|
+
**Copyright (c) 2026 LINAGORA** <https://linagora.com>
|
|
323
|
+
|
|
324
|
+
## Contributing
|
|
325
|
+
|
|
326
|
+
Contributions are welcome! Please read our [Contributing Guidelines](CONTRIBUTING.md) for details on the development workflow, code style, and pull request process.
|
|
327
|
+
|
|
328
|
+
## Support
|
|
329
|
+
|
|
330
|
+
For issues, questions, or feature requests, please open an issue on the GitHub repository.
|
|
331
|
+
|
|
332
|
+
For commercial support or inquiries, contact LINAGORA at <https://linagora.com>.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type StoredTokens } from './token-store.js';
|
|
2
|
+
/**
|
|
3
|
+
* Options for performing OIDC authorization code flow with PKCE
|
|
4
|
+
*/
|
|
5
|
+
export interface OIDCFlowOptions {
|
|
6
|
+
/** OIDC issuer URL (e.g., https://auth.example.com) */
|
|
7
|
+
issuerUrl: string;
|
|
8
|
+
/** OAuth client ID registered with the OIDC provider */
|
|
9
|
+
clientId: string;
|
|
10
|
+
/** OAuth scopes to request (space-separated) */
|
|
11
|
+
scope: string;
|
|
12
|
+
/** Redirect URI (must be registered with the OIDC provider) */
|
|
13
|
+
redirectUri: string;
|
|
14
|
+
/** Local port for callback server (overrides port extracted from redirectUri) */
|
|
15
|
+
localPort?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Perform OIDC authorization code flow with PKCE (S256)
|
|
19
|
+
*
|
|
20
|
+
* This function:
|
|
21
|
+
* 1. Discovers OIDC provider configuration
|
|
22
|
+
* 2. Generates PKCE code verifier and challenge (S256)
|
|
23
|
+
* 3. Builds authorization URL with PKCE
|
|
24
|
+
* 4. Opens browser for user authentication
|
|
25
|
+
* 5. Captures authorization code via localhost callback
|
|
26
|
+
* 6. Exchanges code for tokens
|
|
27
|
+
* 7. Saves tokens to secure storage
|
|
28
|
+
*
|
|
29
|
+
* @param options - OIDC flow configuration
|
|
30
|
+
* @returns Stored tokens after successful authentication
|
|
31
|
+
* @throws JMAPError on OIDC flow failures
|
|
32
|
+
*/
|
|
33
|
+
export declare function performOIDCFlow(options: OIDCFlowOptions): Promise<StoredTokens>;
|
|
34
|
+
/**
|
|
35
|
+
* Helper to extract OIDC flow options from environment config
|
|
36
|
+
*
|
|
37
|
+
* @param config - Config object with OIDC fields
|
|
38
|
+
* @returns OIDCFlowOptions or null if OIDC is not configured
|
|
39
|
+
*/
|
|
40
|
+
export declare function getOIDCOptionsFromConfig(config: {
|
|
41
|
+
JMAP_AUTH_METHOD: string;
|
|
42
|
+
JMAP_OIDC_ISSUER?: string;
|
|
43
|
+
JMAP_OIDC_CLIENT_ID?: string;
|
|
44
|
+
JMAP_OIDC_SCOPE: string;
|
|
45
|
+
JMAP_OIDC_REDIRECT_URI: string;
|
|
46
|
+
JMAP_OIDC_LOCAL_PORT?: number;
|
|
47
|
+
}): OIDCFlowOptions | null;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import * as client from 'openid-client';
|
|
2
|
+
import { getAuthCode } from 'oauth-callback';
|
|
3
|
+
import open from 'open';
|
|
4
|
+
import { saveTokens } from './token-store.js';
|
|
5
|
+
import { JMAPError } from '../errors.js';
|
|
6
|
+
/**
|
|
7
|
+
* Perform OIDC authorization code flow with PKCE (S256)
|
|
8
|
+
*
|
|
9
|
+
* This function:
|
|
10
|
+
* 1. Discovers OIDC provider configuration
|
|
11
|
+
* 2. Generates PKCE code verifier and challenge (S256)
|
|
12
|
+
* 3. Builds authorization URL with PKCE
|
|
13
|
+
* 4. Opens browser for user authentication
|
|
14
|
+
* 5. Captures authorization code via localhost callback
|
|
15
|
+
* 6. Exchanges code for tokens
|
|
16
|
+
* 7. Saves tokens to secure storage
|
|
17
|
+
*
|
|
18
|
+
* @param options - OIDC flow configuration
|
|
19
|
+
* @returns Stored tokens after successful authentication
|
|
20
|
+
* @throws JMAPError on OIDC flow failures
|
|
21
|
+
*/
|
|
22
|
+
export async function performOIDCFlow(options) {
|
|
23
|
+
const { issuerUrl, clientId, scope, redirectUri, localPort } = options;
|
|
24
|
+
// Parse redirect URI to extract port and path
|
|
25
|
+
const redirectUrl = new URL(redirectUri);
|
|
26
|
+
const isLocalhost = ['localhost', '127.0.0.1', '::1'].includes(redirectUrl.hostname);
|
|
27
|
+
// Determine callback port: explicit localPort > port from URI > defaults
|
|
28
|
+
let callbackPort;
|
|
29
|
+
if (localPort) {
|
|
30
|
+
// Explicit local port provided (useful for ngrok scenarios)
|
|
31
|
+
callbackPort = localPort;
|
|
32
|
+
}
|
|
33
|
+
else if (redirectUrl.port) {
|
|
34
|
+
// Explicit port in URI - use it
|
|
35
|
+
callbackPort = parseInt(redirectUrl.port, 10);
|
|
36
|
+
}
|
|
37
|
+
else if (isLocalhost) {
|
|
38
|
+
// Localhost without explicit port - use protocol default
|
|
39
|
+
callbackPort = redirectUrl.protocol === 'https:' ? 443 : 80;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Remote URL (ngrok, etc.) without explicit port - use default 3000
|
|
43
|
+
callbackPort = 3000;
|
|
44
|
+
}
|
|
45
|
+
// Extract callback path from redirect URI (default to /callback if no path)
|
|
46
|
+
const callbackPath = redirectUrl.pathname || '/callback';
|
|
47
|
+
// Step 1: OIDC Discovery
|
|
48
|
+
let config;
|
|
49
|
+
try {
|
|
50
|
+
config = await client.discovery(new URL(issuerUrl), clientId, undefined, // No client secret (public client with PKCE)
|
|
51
|
+
client.None() // Public client authentication
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw JMAPError.oidcFlowError('discovery', error instanceof Error ? error.message : String(error));
|
|
56
|
+
}
|
|
57
|
+
// Step 2: Generate PKCE values (S256 - NEVER use plain)
|
|
58
|
+
const codeVerifier = client.randomPKCECodeVerifier();
|
|
59
|
+
const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier);
|
|
60
|
+
const state = client.randomState();
|
|
61
|
+
// Step 4: Build authorization URL with PKCE S256
|
|
62
|
+
const authParams = {
|
|
63
|
+
redirect_uri: redirectUri,
|
|
64
|
+
scope,
|
|
65
|
+
code_challenge: codeChallenge,
|
|
66
|
+
code_challenge_method: 'S256', // CRITICAL: Always S256, never plain (AUTH-04)
|
|
67
|
+
state,
|
|
68
|
+
};
|
|
69
|
+
const authorizationUrl = client.buildAuthorizationUrl(config, authParams);
|
|
70
|
+
// Step 5: Launch browser and capture callback
|
|
71
|
+
let authCode;
|
|
72
|
+
let returnedState;
|
|
73
|
+
try {
|
|
74
|
+
const result = await getAuthCode({
|
|
75
|
+
port: callbackPort,
|
|
76
|
+
callbackPath,
|
|
77
|
+
authorizationUrl: authorizationUrl.toString(),
|
|
78
|
+
launch: open,
|
|
79
|
+
timeout: 120000, // 2 minutes for user to complete auth
|
|
80
|
+
});
|
|
81
|
+
authCode = result.code;
|
|
82
|
+
// State can be in params (older oauth-callback) or directly on result (newer versions)
|
|
83
|
+
returnedState = result.params?.state ?? result.state;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
throw JMAPError.oidcFlowError('callback', error instanceof Error ? error.message : String(error));
|
|
87
|
+
}
|
|
88
|
+
// Step 6: Validate state to prevent CSRF attacks
|
|
89
|
+
if (returnedState !== state) {
|
|
90
|
+
throw JMAPError.oidcFlowError('state validation', 'State parameter mismatch. Possible CSRF attack.');
|
|
91
|
+
}
|
|
92
|
+
// Step 7: Exchange code for tokens
|
|
93
|
+
let tokenResponse;
|
|
94
|
+
try {
|
|
95
|
+
// Build callback URL with the authorization code for authorizationCodeGrant
|
|
96
|
+
const callbackUrl = new URL(redirectUri);
|
|
97
|
+
callbackUrl.searchParams.set('code', authCode);
|
|
98
|
+
callbackUrl.searchParams.set('state', state);
|
|
99
|
+
tokenResponse = await client.authorizationCodeGrant(config, callbackUrl, {
|
|
100
|
+
pkceCodeVerifier: codeVerifier,
|
|
101
|
+
expectedState: state,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (error instanceof client.AuthorizationResponseError) {
|
|
106
|
+
throw JMAPError.oidcFlowError('authorization', error.error_description || error.error);
|
|
107
|
+
}
|
|
108
|
+
if (error instanceof client.ResponseBodyError) {
|
|
109
|
+
throw JMAPError.oidcFlowError('token exchange', error.message);
|
|
110
|
+
}
|
|
111
|
+
throw JMAPError.oidcFlowError('token exchange', error instanceof Error ? error.message : String(error));
|
|
112
|
+
}
|
|
113
|
+
// Step 8: Build and save StoredTokens
|
|
114
|
+
const tokens = {
|
|
115
|
+
accessToken: tokenResponse.access_token,
|
|
116
|
+
refreshToken: tokenResponse.refresh_token,
|
|
117
|
+
idToken: tokenResponse.id_token,
|
|
118
|
+
expiresAt: tokenResponse.expires_in
|
|
119
|
+
? Math.floor(Date.now() / 1000) + tokenResponse.expires_in
|
|
120
|
+
: undefined,
|
|
121
|
+
};
|
|
122
|
+
await saveTokens(tokens);
|
|
123
|
+
return tokens;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Helper to extract OIDC flow options from environment config
|
|
127
|
+
*
|
|
128
|
+
* @param config - Config object with OIDC fields
|
|
129
|
+
* @returns OIDCFlowOptions or null if OIDC is not configured
|
|
130
|
+
*/
|
|
131
|
+
export function getOIDCOptionsFromConfig(config) {
|
|
132
|
+
if (config.JMAP_AUTH_METHOD !== 'oidc') {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
if (!config.JMAP_OIDC_ISSUER || !config.JMAP_OIDC_CLIENT_ID) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
issuerUrl: config.JMAP_OIDC_ISSUER,
|
|
140
|
+
clientId: config.JMAP_OIDC_CLIENT_ID,
|
|
141
|
+
scope: config.JMAP_OIDC_SCOPE,
|
|
142
|
+
redirectUri: config.JMAP_OIDC_REDIRECT_URI,
|
|
143
|
+
localPort: config.JMAP_OIDC_LOCAL_PORT,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=oidc-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oidc-flow.js","sourceRoot":"","sources":["../../src/auth/oidc-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as client from 'openid-client';
|
|
2
|
+
import type { StoredTokens } from './token-store.js';
|
|
3
|
+
/**
|
|
4
|
+
* Refresh tokens 60 seconds before actual expiry
|
|
5
|
+
* This buffer ensures tokens are refreshed before they become invalid
|
|
6
|
+
*/
|
|
7
|
+
export declare const TOKEN_EXPIRY_BUFFER = 60;
|
|
8
|
+
/**
|
|
9
|
+
* Token refresher with mutex for concurrent access safety
|
|
10
|
+
*
|
|
11
|
+
* When an MCP server handles multiple simultaneous requests, each may try
|
|
12
|
+
* to refresh the token if it's expiring soon. Without coordination, this
|
|
13
|
+
* causes "invalid_grant" errors. This class ensures only one refresh
|
|
14
|
+
* happens at a time and all callers get the fresh token.
|
|
15
|
+
*/
|
|
16
|
+
export declare class TokenRefresher {
|
|
17
|
+
private issuerUrl;
|
|
18
|
+
private clientId;
|
|
19
|
+
private refreshPromise;
|
|
20
|
+
private cachedConfig;
|
|
21
|
+
constructor(issuerUrl: string, clientId: string);
|
|
22
|
+
/**
|
|
23
|
+
* Get or create cached OIDC issuer configuration
|
|
24
|
+
*/
|
|
25
|
+
getIssuerConfig(): Promise<client.Configuration>;
|
|
26
|
+
/**
|
|
27
|
+
* Check if token is valid (not expired or expiring soon)
|
|
28
|
+
* Returns false if token will expire within TOKEN_EXPIRY_BUFFER seconds
|
|
29
|
+
*/
|
|
30
|
+
isTokenValid(tokens: StoredTokens): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Ensure we have a valid token, refreshing if necessary
|
|
33
|
+
*
|
|
34
|
+
* Uses a mutex pattern: if a refresh is already in progress,
|
|
35
|
+
* all callers wait for that same promise rather than starting
|
|
36
|
+
* parallel refresh requests.
|
|
37
|
+
*/
|
|
38
|
+
ensureValidToken(): Promise<StoredTokens>;
|
|
39
|
+
/**
|
|
40
|
+
* Perform the actual token refresh
|
|
41
|
+
*/
|
|
42
|
+
private doRefresh;
|
|
43
|
+
/**
|
|
44
|
+
* Clear cached configuration (useful for testing)
|
|
45
|
+
*/
|
|
46
|
+
clearCache(): void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Factory function to create a TokenRefresher instance
|
|
50
|
+
*/
|
|
51
|
+
export declare function createTokenRefresher(issuerUrl: string, clientId: string): TokenRefresher;
|
|
52
|
+
/**
|
|
53
|
+
* Convenience function to ensure valid token with a new refresher
|
|
54
|
+
* For simple use cases that don't need to maintain refresher state
|
|
55
|
+
*/
|
|
56
|
+
export declare function ensureValidToken(issuerUrl: string, clientId: string): Promise<StoredTokens>;
|