fluxmail 0.16.0 → 0.17.1
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/dist/auth.d.ts +22 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +29 -3
- package/dist/auth.js.map +1 -1
- package/dist/authorization.d.ts +2 -0
- package/dist/authorization.d.ts.map +1 -1
- package/dist/authorization.js +9 -4
- package/dist/authorization.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +93 -5
- package/dist/cli.js.map +1 -1
- package/dist/cliMail.d.ts.map +1 -1
- package/dist/cliMail.js +21 -1
- package/dist/cliMail.js.map +1 -1
- package/dist/deploymentConfig.d.ts +1 -1
- package/dist/deploymentConfig.d.ts.map +1 -1
- package/dist/deploymentConfig.js +1 -1
- package/dist/deploymentConfig.js.map +1 -1
- package/dist/http/app.d.ts +3 -0
- package/dist/http/app.d.ts.map +1 -1
- package/dist/http/app.js +37 -22
- package/dist/http/app.js.map +1 -1
- package/dist/http/identity.d.ts +20 -0
- package/dist/http/identity.d.ts.map +1 -1
- package/dist/http/identity.js +146 -0
- package/dist/http/identity.js.map +1 -1
- package/dist/http/oauth.d.ts +28 -0
- package/dist/http/oauth.d.ts.map +1 -0
- package/dist/http/oauth.js +552 -0
- package/dist/http/oauth.js.map +1 -0
- package/dist/http/oauthPages.d.ts +36 -0
- package/dist/http/oauthPages.d.ts.map +1 -0
- package/dist/http/oauthPages.js +213 -0
- package/dist/http/oauthPages.js.map +1 -0
- package/dist/http/rest.d.ts.map +1 -1
- package/dist/http/rest.js +13 -7
- package/dist/http/rest.js.map +1 -1
- package/dist/mcp/attachmentContent.d.ts +1 -1
- package/dist/mcp/attachmentContent.d.ts.map +1 -1
- package/dist/mcp/buildServer.d.ts +1 -1
- package/dist/mcp/buildServer.d.ts.map +1 -1
- package/dist/mcp/buildServer.js +77 -46
- package/dist/mcp/buildServer.js.map +1 -1
- package/dist/mcp/clientTelemetry.d.ts +19 -0
- package/dist/mcp/clientTelemetry.d.ts.map +1 -0
- package/dist/mcp/clientTelemetry.js +152 -0
- package/dist/mcp/clientTelemetry.js.map +1 -0
- package/dist/mcp/outputSchemas.d.ts +6 -6
- package/dist/oauth/clientMetadata.d.ts +39 -0
- package/dist/oauth/clientMetadata.d.ts.map +1 -0
- package/dist/oauth/clientMetadata.js +234 -0
- package/dist/oauth/clientMetadata.js.map +1 -0
- package/dist/oauth/redirectUris.d.ts +13 -0
- package/dist/oauth/redirectUris.d.ts.map +1 -0
- package/dist/oauth/redirectUris.js +78 -0
- package/dist/oauth/redirectUris.js.map +1 -0
- package/dist/permissions.d.ts +4 -0
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +10 -0
- package/dist/permissions.js.map +1 -1
- package/dist/service/emailService.d.ts.map +1 -1
- package/dist/service/emailService.js +4 -1
- package/dist/service/emailService.js.map +1 -1
- package/dist/service/publicErrors.d.ts +4 -0
- package/dist/service/publicErrors.d.ts.map +1 -1
- package/dist/service/publicErrors.js +16 -1
- package/dist/service/publicErrors.js.map +1 -1
- package/dist/storage/db.d.ts +824 -2
- package/dist/storage/db.d.ts.map +1 -1
- package/dist/storage/db.js +142 -1
- package/dist/storage/db.js.map +1 -1
- package/dist/storage/imapState.d.ts +3 -0
- package/dist/storage/imapState.d.ts.map +1 -1
- package/dist/storage/imapState.js +21 -2
- package/dist/storage/imapState.js.map +1 -1
- package/dist/storage/members.d.ts.map +1 -1
- package/dist/storage/members.js +2 -0
- package/dist/storage/members.js.map +1 -1
- package/dist/storage/oauth.d.ts +178 -0
- package/dist/storage/oauth.d.ts.map +1 -0
- package/dist/storage/oauth.js +556 -0
- package/dist/storage/oauth.js.map +1 -0
- package/package.json +9 -6
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { isEmailError } from '@fluxmail/core';
|
|
3
|
+
import { isBootstrapComplete, verifyMemberPassword } from '../auth.js';
|
|
4
|
+
import { memberCanAccessAccount } from '../authorization.js';
|
|
5
|
+
import { getMember } from '../storage/members.js';
|
|
6
|
+
import { FULL_PERMISSION_POLICY } from '../permissions.js';
|
|
7
|
+
import { approveOAuthAuthorizationRequest, canonicalOAuthScopes, createOAuthAuthorizationRequest, deleteOAuthAuthorizationRequest, exchangeOAuthAuthorizationCode, getOAuthAuthorizationRequest, getOAuthClient, isOAuthScope, mailScopesOf, OAUTH_SCOPES, OFFLINE_ACCESS_SCOPE, refreshOAuthTokens, registerOAuthClient, reserveOAuthRateLimit, revokeOAuthToken, signInOAuthAuthorizationRequest, } from '../storage/oauth.js';
|
|
8
|
+
import { fetchClientMetadata, isClientMetadataUrl, resolveMetadataClient, } from '../oauth/clientMetadata.js';
|
|
9
|
+
import { redirectUriMatches, validateRedirectUri } from '../oauth/redirectUris.js';
|
|
10
|
+
import { captureOperation } from '../telemetry.js';
|
|
11
|
+
import { administrationUsesHttps, requestBodyExceedsLimit, requestClientAddress } from './admin.js';
|
|
12
|
+
import { OAUTH_PAGE_HEADERS, oauthApprovePage, oauthErrorPage, oauthSignInPage } from './oauthPages.js';
|
|
13
|
+
const REGISTRATIONS_PER_MINUTE = 5;
|
|
14
|
+
const TOKEN_REQUESTS_PER_MINUTE = 120;
|
|
15
|
+
const BODY_LIMIT = 64 * 1024;
|
|
16
|
+
/** Scopes a client receives when it asks for none: every mail scope except permanent deletion. */
|
|
17
|
+
export const DEFAULT_OAUTH_MAIL_SCOPES = mailScopesOf(FULL_PERMISSION_POLICY.capabilities);
|
|
18
|
+
function isLoopbackHost(hostname) {
|
|
19
|
+
const host = hostname.toLowerCase().replace(/^\[|\]$/g, '');
|
|
20
|
+
return host === 'localhost' || host === '::1' || /^127(?:\.\d{1,3}){3}$/.test(host);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The issuer is the public URL. OAuth is only offered over HTTPS, or over
|
|
24
|
+
* plain HTTP when the public URL is a loopback address.
|
|
25
|
+
*/
|
|
26
|
+
export function oauthIssuer(config) {
|
|
27
|
+
let url;
|
|
28
|
+
try {
|
|
29
|
+
url = new URL(config.publicUrl);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (url.protocol === 'https:' || (url.protocol === 'http:' && isLoopbackHost(url.hostname))) {
|
|
35
|
+
return config.publicUrl.replace(/\/+$/, '');
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/** The WWW-Authenticate challenge for an unauthenticated MCP request. */
|
|
40
|
+
export function mcpAuthenticateChallenge(config) {
|
|
41
|
+
const issuer = oauthIssuer(config);
|
|
42
|
+
if (!issuer)
|
|
43
|
+
return 'Bearer';
|
|
44
|
+
const scope = [...DEFAULT_OAUTH_MAIL_SCOPES, OFFLINE_ACCESS_SCOPE].join(' ');
|
|
45
|
+
return `Bearer resource_metadata="${issuer}/.well-known/oauth-protected-resource/mcp", scope="${scope}"`;
|
|
46
|
+
}
|
|
47
|
+
export function createOAuthRoutes(deps) {
|
|
48
|
+
const { config, db, registry, telemetry } = deps;
|
|
49
|
+
const fetcher = deps.metadataFetcher ?? fetchClientMetadata;
|
|
50
|
+
const app = new Hono();
|
|
51
|
+
const issuer = oauthIssuer(config);
|
|
52
|
+
const resource = issuer ? `${issuer}/mcp` : null;
|
|
53
|
+
const authorizeEndpoint = issuer ? `${issuer}/oauth/authorize` : '/oauth/authorize';
|
|
54
|
+
const issuerPath = issuer ? new URL(issuer).pathname.replace(/\/+$/, '') : '';
|
|
55
|
+
const capture = (operation, startedAt, outcome, errorCode, properties) => {
|
|
56
|
+
captureOperation(telemetry, {
|
|
57
|
+
productSurface: 'rest',
|
|
58
|
+
operation,
|
|
59
|
+
outcome,
|
|
60
|
+
durationMs: performance.now() - startedAt,
|
|
61
|
+
...(errorCode ? { errorCode } : {}),
|
|
62
|
+
...(properties ? { properties } : {}),
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const connection = (c) => ({
|
|
66
|
+
remoteAddress: c.env?.incoming?.socket.remoteAddress,
|
|
67
|
+
encrypted: Boolean(c.env?.incoming?.socket?.encrypted),
|
|
68
|
+
});
|
|
69
|
+
const usesHttps = (c) => administrationUsesHttps(c.req.raw, connection(c), config.trustProxy);
|
|
70
|
+
const clientAddress = (c) => requestClientAddress(c.req.raw, connection(c), config.trustProxy);
|
|
71
|
+
const protocolError = (c, error, description, status = 400) => c.json({ error, error_description: description }, status, {
|
|
72
|
+
'cache-control': 'no-store',
|
|
73
|
+
pragma: 'no-cache',
|
|
74
|
+
});
|
|
75
|
+
const page = (c, html, status = 200) => c.html(html, status, OAUTH_PAGE_HEADERS);
|
|
76
|
+
const errorPage = (c, title, message, status = 400) => page(c, oauthErrorPage(title, message), status);
|
|
77
|
+
const redirectWith = (redirectUri, params) => {
|
|
78
|
+
const url = new URL(redirectUri);
|
|
79
|
+
for (const [key, value] of Object.entries(params)) {
|
|
80
|
+
if (value !== null && value !== undefined)
|
|
81
|
+
url.searchParams.set(key, value);
|
|
82
|
+
}
|
|
83
|
+
if (issuer)
|
|
84
|
+
url.searchParams.set('iss', issuer);
|
|
85
|
+
return new Response(null, { status: 302, headers: { location: url.href, ...OAUTH_PAGE_HEADERS } });
|
|
86
|
+
};
|
|
87
|
+
const resolveClient = async (id) => {
|
|
88
|
+
if (!id || id.length > 2_000)
|
|
89
|
+
return { ok: false, error: 'invalid_client', description: 'Unknown client_id.' };
|
|
90
|
+
if (!isClientMetadataUrl(id)) {
|
|
91
|
+
const client = getOAuthClient(db, id);
|
|
92
|
+
return client?.kind === 'registered'
|
|
93
|
+
? { ok: true, client }
|
|
94
|
+
: { ok: false, error: 'invalid_client', description: 'Unknown client_id.' };
|
|
95
|
+
}
|
|
96
|
+
const startedAt = performance.now();
|
|
97
|
+
const resolution = await resolveMetadataClient(db, id, fetcher);
|
|
98
|
+
if (resolution.status === 'ok') {
|
|
99
|
+
// Cached documents need no fetch, so only fetches are recorded.
|
|
100
|
+
if (resolution.source !== 'cached') {
|
|
101
|
+
capture('oauthClientMetadata', startedAt, 'success', undefined, { metadata_source: resolution.source });
|
|
102
|
+
}
|
|
103
|
+
return { ok: true, client: resolution.client };
|
|
104
|
+
}
|
|
105
|
+
if (resolution.status === 'invalid') {
|
|
106
|
+
capture('oauthClientMetadata', startedAt, 'error', 'invalid_client_metadata');
|
|
107
|
+
return { ok: false, error: 'invalid_client', description: resolution.reason };
|
|
108
|
+
}
|
|
109
|
+
capture('oauthClientMetadata', startedAt, 'error', 'temporarily_unavailable');
|
|
110
|
+
return { ok: false, error: 'temporarily_unavailable', description: resolution.reason };
|
|
111
|
+
};
|
|
112
|
+
const cors = (c) => {
|
|
113
|
+
c.header('access-control-allow-origin', '*');
|
|
114
|
+
c.header('access-control-allow-methods', 'GET, POST, OPTIONS');
|
|
115
|
+
c.header('access-control-allow-headers', 'authorization, content-type, mcp-protocol-version');
|
|
116
|
+
c.header('access-control-max-age', '600');
|
|
117
|
+
};
|
|
118
|
+
for (const path of [
|
|
119
|
+
'/.well-known/oauth-protected-resource',
|
|
120
|
+
'/.well-known/oauth-protected-resource/*',
|
|
121
|
+
'/.well-known/oauth-authorization-server',
|
|
122
|
+
'/.well-known/oauth-authorization-server/*',
|
|
123
|
+
'/oauth/register',
|
|
124
|
+
'/oauth/token',
|
|
125
|
+
'/oauth/revoke',
|
|
126
|
+
]) {
|
|
127
|
+
app.use(path, async (c, next) => {
|
|
128
|
+
if (!issuer)
|
|
129
|
+
return c.notFound();
|
|
130
|
+
cors(c);
|
|
131
|
+
if (c.req.method === 'OPTIONS')
|
|
132
|
+
return c.body(null, 204);
|
|
133
|
+
return next();
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
app.use('/oauth/*', async (c, next) => {
|
|
137
|
+
if (!issuer)
|
|
138
|
+
return c.notFound();
|
|
139
|
+
c.header('cache-control', 'no-store');
|
|
140
|
+
c.header('x-content-type-options', 'nosniff');
|
|
141
|
+
if (!usesHttps(c)) {
|
|
142
|
+
return c.req.path === '/oauth/authorize'
|
|
143
|
+
? errorPage(c, 'HTTPS is required', 'Fluxmail only accepts OAuth sign-ins over HTTPS. If a reverse proxy handles HTTPS, ' +
|
|
144
|
+
'the administrator needs to set FLUXMAIL_TRUST_PROXY=1.')
|
|
145
|
+
: protocolError(c, 'invalid_request', 'OAuth requests require HTTPS outside loopback.');
|
|
146
|
+
}
|
|
147
|
+
if (c.req.method === 'POST' && (await requestBodyExceedsLimit(c.req.raw, BODY_LIMIT))) {
|
|
148
|
+
return protocolError(c, 'invalid_request', 'Request body is limited to 64 KiB.', 413);
|
|
149
|
+
}
|
|
150
|
+
return next();
|
|
151
|
+
});
|
|
152
|
+
const protectedResourceMetadata = (c) => c.json({
|
|
153
|
+
resource,
|
|
154
|
+
authorization_servers: [issuer],
|
|
155
|
+
scopes_supported: OAUTH_SCOPES,
|
|
156
|
+
bearer_methods_supported: ['header'],
|
|
157
|
+
resource_name: 'Fluxmail',
|
|
158
|
+
});
|
|
159
|
+
app.get('/.well-known/oauth-protected-resource', protectedResourceMetadata);
|
|
160
|
+
app.get('/.well-known/oauth-protected-resource/mcp', protectedResourceMetadata);
|
|
161
|
+
const authorizationServerMetadata = (c) => c.json({
|
|
162
|
+
issuer,
|
|
163
|
+
authorization_endpoint: authorizeEndpoint,
|
|
164
|
+
token_endpoint: `${issuer}/oauth/token`,
|
|
165
|
+
registration_endpoint: `${issuer}/oauth/register`,
|
|
166
|
+
revocation_endpoint: `${issuer}/oauth/revoke`,
|
|
167
|
+
scopes_supported: OAUTH_SCOPES,
|
|
168
|
+
response_types_supported: ['code'],
|
|
169
|
+
response_modes_supported: ['query'],
|
|
170
|
+
grant_types_supported: ['authorization_code', 'refresh_token'],
|
|
171
|
+
token_endpoint_auth_methods_supported: ['none'],
|
|
172
|
+
revocation_endpoint_auth_methods_supported: ['none'],
|
|
173
|
+
code_challenge_methods_supported: ['S256'],
|
|
174
|
+
client_id_metadata_document_supported: true,
|
|
175
|
+
authorization_response_iss_parameter_supported: true,
|
|
176
|
+
});
|
|
177
|
+
app.get('/.well-known/oauth-authorization-server', authorizationServerMetadata);
|
|
178
|
+
// RFC 8414 and RFC 9728 put metadata for an issuer with a path, such as https://mail.example/fluxmail,
|
|
179
|
+
// at the origin root with the path appended. A proxy that serves Fluxmail under that path must route these too.
|
|
180
|
+
if (issuerPath) {
|
|
181
|
+
app.get(`/.well-known/oauth-authorization-server${issuerPath}`, authorizationServerMetadata);
|
|
182
|
+
app.get(`/.well-known/oauth-protected-resource${issuerPath}/mcp`, protectedResourceMetadata);
|
|
183
|
+
}
|
|
184
|
+
app.post('/oauth/register', async (c) => {
|
|
185
|
+
const startedAt = performance.now();
|
|
186
|
+
const fail = (error, description, status = 400) => {
|
|
187
|
+
capture('oauthRegister', startedAt, 'error', error);
|
|
188
|
+
return protocolError(c, error, description, status);
|
|
189
|
+
};
|
|
190
|
+
if (!reserveOAuthRateLimit(db, `register:${clientAddress(c)}`, REGISTRATIONS_PER_MINUTE, 60_000)) {
|
|
191
|
+
return fail('slow_down', 'Too many client registrations. Try again in a minute.', 429);
|
|
192
|
+
}
|
|
193
|
+
let body;
|
|
194
|
+
try {
|
|
195
|
+
const parsed = (await c.req.json());
|
|
196
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
197
|
+
throw new Error('not an object');
|
|
198
|
+
body = parsed;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return fail('invalid_client_metadata', 'The registration request must be a JSON object.');
|
|
202
|
+
}
|
|
203
|
+
const redirectUris = body.redirect_uris;
|
|
204
|
+
if (!Array.isArray(redirectUris) ||
|
|
205
|
+
!redirectUris.length ||
|
|
206
|
+
redirectUris.length > 20 ||
|
|
207
|
+
!redirectUris.every((uri) => typeof uri === 'string')) {
|
|
208
|
+
return fail('invalid_redirect_uri', 'redirect_uris must list between 1 and 20 URIs.');
|
|
209
|
+
}
|
|
210
|
+
for (const uri of redirectUris) {
|
|
211
|
+
const problem = validateRedirectUri(uri);
|
|
212
|
+
if (problem)
|
|
213
|
+
return fail('invalid_redirect_uri', problem);
|
|
214
|
+
}
|
|
215
|
+
const grantTypes = body.grant_types ?? ['authorization_code', 'refresh_token'];
|
|
216
|
+
if (!Array.isArray(grantTypes) ||
|
|
217
|
+
!grantTypes.every((type) => type === 'authorization_code' || type === 'refresh_token')) {
|
|
218
|
+
return fail('invalid_client_metadata', 'Only the authorization_code and refresh_token grants are supported.');
|
|
219
|
+
}
|
|
220
|
+
const responseTypes = body.response_types ?? ['code'];
|
|
221
|
+
if (!Array.isArray(responseTypes) || !responseTypes.every((type) => type === 'code')) {
|
|
222
|
+
return fail('invalid_client_metadata', 'Only the code response type is supported.');
|
|
223
|
+
}
|
|
224
|
+
const name = typeof body.client_name === 'string' && body.client_name.trim()
|
|
225
|
+
? body.client_name.trim().slice(0, 200)
|
|
226
|
+
: 'Unnamed MCP client';
|
|
227
|
+
let scopes = DEFAULT_OAUTH_MAIL_SCOPES;
|
|
228
|
+
if (body.scope !== undefined) {
|
|
229
|
+
if (typeof body.scope !== 'string')
|
|
230
|
+
return fail('invalid_client_metadata', 'scope must be a string.');
|
|
231
|
+
const requested = body.scope.split(' ').filter(Boolean);
|
|
232
|
+
const unknown = requested.filter((scope) => !isOAuthScope(scope));
|
|
233
|
+
if (unknown.length)
|
|
234
|
+
return fail('invalid_client_metadata', `Unknown scope: ${unknown.join(', ')}.`);
|
|
235
|
+
if (mailScopesOf(requested).length)
|
|
236
|
+
scopes = mailScopesOf(requested);
|
|
237
|
+
}
|
|
238
|
+
// Every client is public. Client keys, logos, and other URLs are dropped
|
|
239
|
+
// so Fluxmail never fetches anything a registration names.
|
|
240
|
+
const client = registerOAuthClient(db, { name, redirectUris: redirectUris, scopes });
|
|
241
|
+
if (!client) {
|
|
242
|
+
c.header('retry-after', '3600');
|
|
243
|
+
return fail('temporarily_unavailable', 'Fluxmail has reached its OAuth client limit.', 503);
|
|
244
|
+
}
|
|
245
|
+
capture('oauthRegister', startedAt, 'success');
|
|
246
|
+
return c.json({
|
|
247
|
+
client_id: client.id,
|
|
248
|
+
client_id_issued_at: Math.floor(client.createdAt / 1000),
|
|
249
|
+
client_name: client.name,
|
|
250
|
+
redirect_uris: client.redirectUris,
|
|
251
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
252
|
+
response_types: ['code'],
|
|
253
|
+
token_endpoint_auth_method: 'none',
|
|
254
|
+
scope: [...client.scopes, OFFLINE_ACCESS_SCOPE].join(' '),
|
|
255
|
+
}, 201, { 'cache-control': 'no-store' });
|
|
256
|
+
});
|
|
257
|
+
const accessibleAccounts = (memberId) => registry.listAccounts().filter((account) => memberCanAccessAccount(memberId, account));
|
|
258
|
+
const renderApprove = (c, client, request, csrf, memberId, extra = {}) => {
|
|
259
|
+
const member = getMember(db, memberId);
|
|
260
|
+
return page(c, oauthApprovePage({
|
|
261
|
+
action: authorizeEndpoint,
|
|
262
|
+
client,
|
|
263
|
+
redirectUri: request.redirectUri,
|
|
264
|
+
requestId: request.id,
|
|
265
|
+
csrf,
|
|
266
|
+
memberName: member.email ? `${member.name} (${member.email})` : member.name,
|
|
267
|
+
scopes: mailScopesOf(request.scopes),
|
|
268
|
+
accounts: accessibleAccounts(memberId),
|
|
269
|
+
...extra,
|
|
270
|
+
}), extra.error ? 400 : 200);
|
|
271
|
+
};
|
|
272
|
+
app.get('/oauth/authorize', async (c) => {
|
|
273
|
+
const startedAt = performance.now();
|
|
274
|
+
const query = c.req.query();
|
|
275
|
+
const failPage = (code, title, message) => {
|
|
276
|
+
capture('oauthAuthorize', startedAt, 'error', code);
|
|
277
|
+
return errorPage(c, title, message);
|
|
278
|
+
};
|
|
279
|
+
if (!isBootstrapComplete(db)) {
|
|
280
|
+
return failPage('setup_required', 'Fluxmail is not set up yet', 'An administrator needs to run "fluxmail setup" before apps can connect.');
|
|
281
|
+
}
|
|
282
|
+
const resolved = await resolveClient(query.client_id ?? '');
|
|
283
|
+
if (!resolved.ok) {
|
|
284
|
+
return failPage(resolved.error, resolved.error === 'temporarily_unavailable' ? 'Try again shortly' : 'This app is not recognized', resolved.description);
|
|
285
|
+
}
|
|
286
|
+
const client = resolved.client;
|
|
287
|
+
const redirectUri = query.redirect_uri ?? (client.redirectUris.length === 1 ? client.redirectUris[0] : undefined);
|
|
288
|
+
if (!redirectUri || !redirectUriMatches(client.redirectUris, redirectUri)) {
|
|
289
|
+
return failPage('invalid_redirect_uri', 'This sign-in link is not valid', 'The app asked Fluxmail to return you to an address it did not register.');
|
|
290
|
+
}
|
|
291
|
+
const state = query.state;
|
|
292
|
+
const redirectError = (error, description) => {
|
|
293
|
+
capture('oauthAuthorize', startedAt, 'error', error);
|
|
294
|
+
return redirectWith(redirectUri, { error, error_description: description, state });
|
|
295
|
+
};
|
|
296
|
+
if (query.response_type !== 'code')
|
|
297
|
+
return redirectError('unsupported_response_type', 'response_type must be code.');
|
|
298
|
+
if (query.code_challenge_method !== 'S256' || !/^[A-Za-z0-9_-]{43}$/.test(query.code_challenge ?? '')) {
|
|
299
|
+
return redirectError('invalid_request', 'An S256 PKCE code_challenge is required.');
|
|
300
|
+
}
|
|
301
|
+
if (query.resource !== undefined && query.resource.replace(/\/+$/, '') !== resource) {
|
|
302
|
+
return redirectError('invalid_target', `resource must be ${resource}.`);
|
|
303
|
+
}
|
|
304
|
+
if (state !== undefined && state.length > 2_000) {
|
|
305
|
+
return redirectError('invalid_request', 'state is limited to 2,000 characters.');
|
|
306
|
+
}
|
|
307
|
+
if (query.prompt === 'none')
|
|
308
|
+
return redirectError('login_required', 'Fluxmail needs you to sign in.');
|
|
309
|
+
const requested = (query.scope ?? '').split(' ').filter(Boolean);
|
|
310
|
+
const unknown = requested.filter((scope) => !isOAuthScope(scope));
|
|
311
|
+
if (unknown.length)
|
|
312
|
+
return redirectError('invalid_scope', `Unknown scope: ${unknown.join(', ')}.`);
|
|
313
|
+
let mailScopes = mailScopesOf(requested);
|
|
314
|
+
if (mailScopes.some((scope) => !client.scopes.includes(scope))) {
|
|
315
|
+
return redirectError('invalid_scope', 'The app requested a scope it did not register for.');
|
|
316
|
+
}
|
|
317
|
+
if (!mailScopes.length)
|
|
318
|
+
mailScopes = DEFAULT_OAUTH_MAIL_SCOPES.filter((scope) => client.scopes.includes(scope));
|
|
319
|
+
if (!mailScopes.length)
|
|
320
|
+
return redirectError('invalid_scope', 'Request at least one mail scope.');
|
|
321
|
+
const { request, csrf } = createOAuthAuthorizationRequest(db, {
|
|
322
|
+
clientId: client.id,
|
|
323
|
+
redirectUri,
|
|
324
|
+
...(state !== undefined ? { state } : {}),
|
|
325
|
+
codeChallenge: query.code_challenge,
|
|
326
|
+
scopes: canonicalOAuthScopes([...mailScopes, OFFLINE_ACCESS_SCOPE]),
|
|
327
|
+
});
|
|
328
|
+
capture('oauthAuthorize', startedAt, 'success', undefined, { client_kind: client.kind });
|
|
329
|
+
return page(c, oauthSignInPage({ action: authorizeEndpoint, client, redirectUri, requestId: request.id, csrf }));
|
|
330
|
+
});
|
|
331
|
+
app.post('/oauth/authorize', async (c) => {
|
|
332
|
+
const startedAt = performance.now();
|
|
333
|
+
const fetchSite = c.req.header('sec-fetch-site');
|
|
334
|
+
const origin = c.req.header('origin');
|
|
335
|
+
const sameOrigin = (value) => value === new URL(issuer).origin || value === new URL(c.req.url).origin;
|
|
336
|
+
if ((fetchSite && fetchSite !== 'same-origin' && fetchSite !== 'none') || (origin && !sameOrigin(origin))) {
|
|
337
|
+
capture('oauthConsent', startedAt, 'error', 'cross_origin');
|
|
338
|
+
return errorPage(c, 'This request was blocked', 'Approve the connection from the Fluxmail page.', 403);
|
|
339
|
+
}
|
|
340
|
+
let form;
|
|
341
|
+
try {
|
|
342
|
+
form = await c.req.parseBody({ all: true });
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
return errorPage(c, 'This request is not valid', 'Start connecting again from your app.');
|
|
346
|
+
}
|
|
347
|
+
const field = (name) => {
|
|
348
|
+
const value = form[name];
|
|
349
|
+
const first = Array.isArray(value) ? value[0] : value;
|
|
350
|
+
return typeof first === 'string' ? first : '';
|
|
351
|
+
};
|
|
352
|
+
const fields = (name) => {
|
|
353
|
+
const value = form[name];
|
|
354
|
+
return (Array.isArray(value) ? value : value === undefined ? [] : [value]).filter((item) => typeof item === 'string');
|
|
355
|
+
};
|
|
356
|
+
const step = field('step') === 'approve' ? 'approve' : 'sign_in';
|
|
357
|
+
const request = getOAuthAuthorizationRequest(db, field('request_id'), field('csrf'));
|
|
358
|
+
const client = request ? getOAuthClient(db, request.clientId) : null;
|
|
359
|
+
if (!request || !client) {
|
|
360
|
+
capture('oauthConsent', startedAt, 'error', 'expired_request', { step });
|
|
361
|
+
return errorPage(c, 'This request has expired', 'Fluxmail sign-in requests last 10 minutes. Start connecting again from your app.', 410);
|
|
362
|
+
}
|
|
363
|
+
if (field('action') === 'deny') {
|
|
364
|
+
deleteOAuthAuthorizationRequest(db, request.id);
|
|
365
|
+
capture('oauthConsent', startedAt, 'success', undefined, { step, consent: 'denied' });
|
|
366
|
+
return redirectWith(request.redirectUri, {
|
|
367
|
+
error: 'access_denied',
|
|
368
|
+
error_description: 'The member denied the request.',
|
|
369
|
+
state: request.state,
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
if (step === 'sign_in') {
|
|
373
|
+
const email = field('email');
|
|
374
|
+
try {
|
|
375
|
+
const member = await verifyMemberPassword(db, {
|
|
376
|
+
email,
|
|
377
|
+
password: field('password'),
|
|
378
|
+
ipAddress: clientAddress(c),
|
|
379
|
+
});
|
|
380
|
+
const csrf = signInOAuthAuthorizationRequest(db, request.id, member.id);
|
|
381
|
+
if (!csrf)
|
|
382
|
+
throw new Error('request expired');
|
|
383
|
+
capture('oauthConsent', startedAt, 'success', undefined, { step });
|
|
384
|
+
return renderApprove(c, client, { ...request, memberId: member.id }, csrf, member.id);
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
const code = isEmailError(error) ? error.code : 'internal';
|
|
388
|
+
capture('oauthConsent', startedAt, 'error', code, { step });
|
|
389
|
+
const message = code === 'rate_limited'
|
|
390
|
+
? 'Too many sign-in attempts. Try again later.'
|
|
391
|
+
: code === 'invalid_request'
|
|
392
|
+
? 'Enter a valid email address.'
|
|
393
|
+
: code === 'permission_denied'
|
|
394
|
+
? 'Email or password is incorrect.'
|
|
395
|
+
: 'Fluxmail could not sign you in. Try again.';
|
|
396
|
+
return page(c, oauthSignInPage({
|
|
397
|
+
action: authorizeEndpoint,
|
|
398
|
+
client,
|
|
399
|
+
redirectUri: request.redirectUri,
|
|
400
|
+
requestId: request.id,
|
|
401
|
+
csrf: field('csrf'),
|
|
402
|
+
email,
|
|
403
|
+
error: message,
|
|
404
|
+
}), 400);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
const memberId = request.memberId;
|
|
408
|
+
if (!memberId) {
|
|
409
|
+
capture('oauthConsent', startedAt, 'error', 'invalid_request', { step });
|
|
410
|
+
return errorPage(c, 'Sign in first', 'Start connecting again from your app.');
|
|
411
|
+
}
|
|
412
|
+
if (getMember(db, memberId).status !== 'active') {
|
|
413
|
+
capture('oauthConsent', startedAt, 'error', 'permission_denied', { step });
|
|
414
|
+
return errorPage(c, 'This member is not active', 'Ask a Fluxmail administrator for help.', 403);
|
|
415
|
+
}
|
|
416
|
+
const requestedMail = mailScopesOf(request.scopes);
|
|
417
|
+
const checkedScopes = mailScopesOf(fields('scope')).filter((scope) => requestedMail.includes(scope));
|
|
418
|
+
const allMailboxes = field('all_mailboxes') === '1';
|
|
419
|
+
const accessible = new Set(accessibleAccounts(memberId).map((account) => account.id));
|
|
420
|
+
const checkedAccountIds = [...new Set(fields('account'))];
|
|
421
|
+
const retry = (error) => {
|
|
422
|
+
capture('oauthConsent', startedAt, 'error', 'invalid_request', { step });
|
|
423
|
+
return renderApprove(c, client, request, field('csrf'), memberId, {
|
|
424
|
+
error,
|
|
425
|
+
checkedScopes,
|
|
426
|
+
checkedAccountIds,
|
|
427
|
+
allMailboxes,
|
|
428
|
+
});
|
|
429
|
+
};
|
|
430
|
+
if (!checkedScopes.length)
|
|
431
|
+
return retry('Choose at least one permission.');
|
|
432
|
+
if (!allMailboxes && !checkedAccountIds.length)
|
|
433
|
+
return retry('Choose at least one mailbox, or all mailboxes.');
|
|
434
|
+
if (!allMailboxes && checkedAccountIds.some((id) => !accessible.has(id))) {
|
|
435
|
+
return retry('One of the selected mailboxes is no longer available.');
|
|
436
|
+
}
|
|
437
|
+
const approved = approveOAuthAuthorizationRequest(db, {
|
|
438
|
+
requestId: request.id,
|
|
439
|
+
memberId,
|
|
440
|
+
scopes: checkedScopes,
|
|
441
|
+
accountIds: allMailboxes ? null : checkedAccountIds,
|
|
442
|
+
});
|
|
443
|
+
if (!approved) {
|
|
444
|
+
capture('oauthConsent', startedAt, 'error', 'expired_request', { step });
|
|
445
|
+
return errorPage(c, 'This request has expired', 'Start connecting again from your app.', 410);
|
|
446
|
+
}
|
|
447
|
+
capture('oauthConsent', startedAt, 'success', undefined, { step, consent: 'approved' });
|
|
448
|
+
return redirectWith(approved.redirectUri, { code: approved.code, state: approved.state });
|
|
449
|
+
});
|
|
450
|
+
const formFields = async (c) => {
|
|
451
|
+
const mediaType = c.req.header('content-type')?.split(';', 1)[0]?.trim().toLowerCase();
|
|
452
|
+
if (mediaType !== 'application/x-www-form-urlencoded')
|
|
453
|
+
return null;
|
|
454
|
+
try {
|
|
455
|
+
const body = await c.req.parseBody();
|
|
456
|
+
return Object.fromEntries(Object.entries(body).filter((entry) => typeof entry[1] === 'string'));
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
const basicClientId = (c) => {
|
|
463
|
+
const header = c.req.header('authorization');
|
|
464
|
+
const encoded = header?.match(/^Basic\s+([A-Za-z0-9+/=]+)$/i)?.[1];
|
|
465
|
+
if (!encoded)
|
|
466
|
+
return undefined;
|
|
467
|
+
const decoded = Buffer.from(encoded, 'base64').toString('utf8');
|
|
468
|
+
const separator = decoded.indexOf(':');
|
|
469
|
+
const id = separator === -1 ? decoded : decoded.slice(0, separator);
|
|
470
|
+
try {
|
|
471
|
+
return decodeURIComponent(id);
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
return id;
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
const tokenResponse = (c, tokens) => c.json({
|
|
478
|
+
access_token: tokens.accessToken,
|
|
479
|
+
token_type: 'Bearer',
|
|
480
|
+
expires_in: tokens.expiresIn,
|
|
481
|
+
...(tokens.refreshToken ? { refresh_token: tokens.refreshToken } : {}),
|
|
482
|
+
scope: tokens.scopes.join(' '),
|
|
483
|
+
}, 200, { 'cache-control': 'no-store', pragma: 'no-cache' });
|
|
484
|
+
app.post('/oauth/token', async (c) => {
|
|
485
|
+
const startedAt = performance.now();
|
|
486
|
+
const form = await formFields(c);
|
|
487
|
+
const grantType = form?.grant_type;
|
|
488
|
+
const grantProperty = grantType === 'authorization_code' || grantType === 'refresh_token' ? { grant_type: grantType } : undefined;
|
|
489
|
+
const fail = (error, description, status = 400) => {
|
|
490
|
+
capture('oauthToken', startedAt, 'error', error, grantProperty);
|
|
491
|
+
return protocolError(c, error, description, status);
|
|
492
|
+
};
|
|
493
|
+
if (!form)
|
|
494
|
+
return fail('invalid_request', 'Send the token request as application/x-www-form-urlencoded.');
|
|
495
|
+
if (!reserveOAuthRateLimit(db, `token:${clientAddress(c)}`, TOKEN_REQUESTS_PER_MINUTE, 60_000)) {
|
|
496
|
+
return fail('slow_down', 'Too many token requests. Try again in a minute.', 429);
|
|
497
|
+
}
|
|
498
|
+
const clientId = form.client_id ?? basicClientId(c) ?? '';
|
|
499
|
+
const client = clientId ? getOAuthClient(db, clientId) : null;
|
|
500
|
+
if (!client)
|
|
501
|
+
return fail('invalid_client', 'Unknown client_id.', 401);
|
|
502
|
+
if (form.resource !== undefined && form.resource.replace(/\/+$/, '') !== resource) {
|
|
503
|
+
return fail('invalid_target', `resource must be ${resource}.`);
|
|
504
|
+
}
|
|
505
|
+
let result;
|
|
506
|
+
if (grantType === 'authorization_code') {
|
|
507
|
+
if (!form.code || !form.redirect_uri || !form.code_verifier) {
|
|
508
|
+
return fail('invalid_request', 'code, redirect_uri, and code_verifier are required.');
|
|
509
|
+
}
|
|
510
|
+
result = exchangeOAuthAuthorizationCode(db, {
|
|
511
|
+
code: form.code,
|
|
512
|
+
clientId: client.id,
|
|
513
|
+
redirectUri: form.redirect_uri,
|
|
514
|
+
codeVerifier: form.code_verifier,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
else if (grantType === 'refresh_token') {
|
|
518
|
+
if (!form.refresh_token)
|
|
519
|
+
return fail('invalid_request', 'refresh_token is required.');
|
|
520
|
+
const scopes = form.scope?.split(' ').filter(Boolean);
|
|
521
|
+
if (scopes?.some((scope) => !isOAuthScope(scope)))
|
|
522
|
+
return fail('invalid_scope', 'Unknown scope.');
|
|
523
|
+
result = refreshOAuthTokens(db, {
|
|
524
|
+
refreshToken: form.refresh_token,
|
|
525
|
+
clientId: client.id,
|
|
526
|
+
...(scopes?.length ? { scopes } : {}),
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
return fail('unsupported_grant_type', 'grant_type must be authorization_code or refresh_token.');
|
|
531
|
+
}
|
|
532
|
+
if (!result.ok)
|
|
533
|
+
return fail(result.error, result.description);
|
|
534
|
+
capture('oauthToken', startedAt, 'success', undefined, grantProperty);
|
|
535
|
+
return tokenResponse(c, result.tokens);
|
|
536
|
+
});
|
|
537
|
+
app.post('/oauth/revoke', async (c) => {
|
|
538
|
+
const startedAt = performance.now();
|
|
539
|
+
const form = await formFields(c);
|
|
540
|
+
if (!form?.token) {
|
|
541
|
+
capture('oauthRevoke', startedAt, 'error', 'invalid_request');
|
|
542
|
+
return protocolError(c, 'invalid_request', 'token is required.');
|
|
543
|
+
}
|
|
544
|
+
const clientId = form.client_id ?? basicClientId(c) ?? '';
|
|
545
|
+
if (clientId)
|
|
546
|
+
revokeOAuthToken(db, { token: form.token, clientId });
|
|
547
|
+
capture('oauthRevoke', startedAt, 'success');
|
|
548
|
+
return c.body(null, 200, { 'cache-control': 'no-store' });
|
|
549
|
+
});
|
|
550
|
+
return app;
|
|
551
|
+
}
|
|
552
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/http/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Account } from '@fluxmail/core';
|
|
2
|
+
import type { OAuthClient } from '../storage/oauth.js';
|
|
3
|
+
import type { McpCapability } from '../permissions.js';
|
|
4
|
+
export declare const OAUTH_PAGE_HEADERS: {
|
|
5
|
+
readonly 'cache-control': "no-store";
|
|
6
|
+
readonly 'content-security-policy': "default-src 'none'; style-src 'unsafe-inline'; img-src 'none'; base-uri 'none'; frame-ancestors 'none'";
|
|
7
|
+
readonly 'x-content-type-options': "nosniff";
|
|
8
|
+
readonly 'x-frame-options': "DENY";
|
|
9
|
+
readonly 'referrer-policy': "same-origin";
|
|
10
|
+
};
|
|
11
|
+
export declare function escapeHtml(value: string): string;
|
|
12
|
+
export declare function oauthErrorPage(title: string, message: string): string;
|
|
13
|
+
export declare function oauthSignInPage(input: {
|
|
14
|
+
action: string;
|
|
15
|
+
client: OAuthClient;
|
|
16
|
+
redirectUri: string;
|
|
17
|
+
requestId: string;
|
|
18
|
+
csrf: string;
|
|
19
|
+
email?: string;
|
|
20
|
+
error?: string;
|
|
21
|
+
}): string;
|
|
22
|
+
export declare function oauthApprovePage(input: {
|
|
23
|
+
action: string;
|
|
24
|
+
client: OAuthClient;
|
|
25
|
+
redirectUri: string;
|
|
26
|
+
requestId: string;
|
|
27
|
+
csrf: string;
|
|
28
|
+
memberName: string;
|
|
29
|
+
scopes: readonly McpCapability[];
|
|
30
|
+
checkedScopes?: readonly McpCapability[];
|
|
31
|
+
accounts: readonly Account[];
|
|
32
|
+
checkedAccountIds?: readonly string[];
|
|
33
|
+
allMailboxes?: boolean;
|
|
34
|
+
error?: string;
|
|
35
|
+
}): string;
|
|
36
|
+
//# sourceMappingURL=oauthPages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauthPages.d.ts","sourceRoot":"","sources":["../../src/http/oauthPages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,eAAO,MAAM,kBAAkB;;;;;;CAQrB,CAAC;AAiEX,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAgFD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAErE;AAgDD,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CAeT;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,aAAa,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,MAAM,CA8CT"}
|