mcp-twake-mail 0.1.0 → 1.0.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/README.md +77 -48
- package/build/cli/commands/setup.js +54 -12
- package/build/cli/commands/setup.js.map +1 -1
- package/build/cli/prompts/setup-wizard.d.ts +33 -1
- package/build/cli/prompts/setup-wizard.js +134 -2
- package/build/cli/prompts/setup-wizard.js.map +1 -1
- package/build/cli/prompts/setup-wizard.test.d.ts +1 -0
- package/build/cli/prompts/setup-wizard.test.js +161 -0
- package/build/cli/prompts/setup-wizard.test.js.map +1 -0
- package/build/config/schema.d.ts +2 -0
- package/build/config/schema.js +3 -0
- package/build/config/schema.js.map +1 -1
- package/build/discovery/dns-srv.d.ts +18 -0
- package/build/discovery/dns-srv.js +60 -0
- package/build/discovery/dns-srv.js.map +1 -0
- package/build/discovery/dns-srv.test.d.ts +4 -0
- package/build/discovery/dns-srv.test.js +79 -0
- package/build/discovery/dns-srv.test.js.map +1 -0
- package/build/discovery/index.d.ts +9 -0
- package/build/discovery/index.js +13 -0
- package/build/discovery/index.js.map +1 -0
- package/build/discovery/oauth-discovery.d.ts +34 -0
- package/build/discovery/oauth-discovery.js +160 -0
- package/build/discovery/oauth-discovery.js.map +1 -0
- package/build/discovery/oauth-discovery.test.d.ts +1 -0
- package/build/discovery/oauth-discovery.test.js +198 -0
- package/build/discovery/oauth-discovery.test.js.map +1 -0
- package/build/discovery/orchestrator.d.ts +31 -0
- package/build/discovery/orchestrator.js +87 -0
- package/build/discovery/orchestrator.js.map +1 -0
- package/build/discovery/orchestrator.test.d.ts +4 -0
- package/build/discovery/orchestrator.test.js +242 -0
- package/build/discovery/orchestrator.test.js.map +1 -0
- package/build/discovery/types.d.ts +18 -0
- package/build/discovery/types.js +15 -0
- package/build/discovery/types.js.map +1 -0
- package/build/discovery/well-known.d.ts +21 -0
- package/build/discovery/well-known.js +52 -0
- package/build/discovery/well-known.js.map +1 -0
- package/build/discovery/well-known.test.d.ts +4 -0
- package/build/discovery/well-known.test.js +120 -0
- package/build/discovery/well-known.test.js.map +1 -0
- package/build/mcp/server.d.ts +5 -3
- package/build/mcp/server.js +11 -5
- package/build/mcp/server.js.map +1 -1
- package/build/mcp/tools/email-sending.d.ts +10 -1
- package/build/mcp/tools/email-sending.js +60 -15
- package/build/mcp/tools/email-sending.js.map +1 -1
- package/build/mcp/tools/index.d.ts +10 -1
- package/build/mcp/tools/index.js +4 -3
- package/build/mcp/tools/index.js.map +1 -1
- package/build/signature/converter.d.ts +2 -0
- package/build/signature/converter.js +23 -0
- package/build/signature/converter.js.map +1 -0
- package/build/signature/converter.test.d.ts +1 -0
- package/build/signature/converter.test.js +84 -0
- package/build/signature/converter.test.js.map +1 -0
- package/build/signature/index.d.ts +2 -0
- package/build/signature/index.js +3 -0
- package/build/signature/index.js.map +1 -0
- package/build/signature/loader.d.ts +6 -0
- package/build/signature/loader.js +31 -0
- package/build/signature/loader.js.map +1 -0
- package/build/signature/loader.test.d.ts +1 -0
- package/build/signature/loader.test.js +85 -0
- package/build/signature/loader.test.js.map +1 -0
- package/docs/auto-discovery.md +210 -0
- package/docs/oidc-configuration.md +261 -0
- package/package.json +3 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { parseWwwAuthenticate, discoverOAuthFromResource, } from './oauth-discovery.js';
|
|
3
|
+
describe('parseWwwAuthenticate', () => {
|
|
4
|
+
it('should parse valid Bearer header with issuer', () => {
|
|
5
|
+
const header = 'Bearer issuer="https://auth.example.com"';
|
|
6
|
+
const result = parseWwwAuthenticate(header);
|
|
7
|
+
expect(result).toEqual({
|
|
8
|
+
issuer: 'https://auth.example.com',
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
it('should parse Bearer header with multiple parameters', () => {
|
|
12
|
+
const header = 'Bearer realm="example", scope="openid email", issuer="https://auth.example.com"';
|
|
13
|
+
const result = parseWwwAuthenticate(header);
|
|
14
|
+
expect(result).toEqual({
|
|
15
|
+
realm: 'example',
|
|
16
|
+
scope: 'openid email',
|
|
17
|
+
issuer: 'https://auth.example.com',
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
it('should be case-insensitive for Bearer scheme', () => {
|
|
21
|
+
const header = 'bearer issuer="https://auth.example.com"';
|
|
22
|
+
const result = parseWwwAuthenticate(header);
|
|
23
|
+
expect(result).toEqual({
|
|
24
|
+
issuer: 'https://auth.example.com',
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
it('should return null for non-Bearer header', () => {
|
|
28
|
+
const header = 'Basic realm="example"';
|
|
29
|
+
const result = parseWwwAuthenticate(header);
|
|
30
|
+
expect(result).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
it('should return null for malformed header without parameters', () => {
|
|
33
|
+
const header = 'Bearer';
|
|
34
|
+
const result = parseWwwAuthenticate(header);
|
|
35
|
+
expect(result).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
it('should ignore unknown parameters', () => {
|
|
38
|
+
const header = 'Bearer issuer="https://auth.example.com", unknown="value"';
|
|
39
|
+
const result = parseWwwAuthenticate(header);
|
|
40
|
+
expect(result).toEqual({
|
|
41
|
+
issuer: 'https://auth.example.com',
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('discoverOAuthFromResource', () => {
|
|
46
|
+
const mockFetch = vi.fn();
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
global.fetch = mockFetch;
|
|
49
|
+
vi.clearAllMocks();
|
|
50
|
+
});
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
vi.restoreAllMocks();
|
|
53
|
+
});
|
|
54
|
+
it('should discover issuer from protected-resource metadata', async () => {
|
|
55
|
+
mockFetch.mockResolvedValueOnce({
|
|
56
|
+
ok: true,
|
|
57
|
+
json: async () => ({
|
|
58
|
+
authorization_servers: ['https://auth.example.com'],
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
61
|
+
const result = await discoverOAuthFromResource('https://jmap.example.com/api');
|
|
62
|
+
expect(result).toEqual({
|
|
63
|
+
issuer: 'https://auth.example.com',
|
|
64
|
+
method: 'protected-resource',
|
|
65
|
+
});
|
|
66
|
+
expect(mockFetch).toHaveBeenCalledWith('https://jmap.example.com/.well-known/oauth-protected-resource', expect.objectContaining({ signal: expect.any(AbortSignal) }));
|
|
67
|
+
});
|
|
68
|
+
it('should fallback to WWW-Authenticate on 401 response', async () => {
|
|
69
|
+
// First call: protected-resource fails
|
|
70
|
+
mockFetch.mockResolvedValueOnce({
|
|
71
|
+
ok: false,
|
|
72
|
+
status: 404,
|
|
73
|
+
});
|
|
74
|
+
// Second call: JMAP URL returns 401 with WWW-Authenticate
|
|
75
|
+
mockFetch.mockResolvedValueOnce({
|
|
76
|
+
ok: false,
|
|
77
|
+
status: 401,
|
|
78
|
+
headers: {
|
|
79
|
+
get: (name) => {
|
|
80
|
+
if (name === 'WWW-Authenticate') {
|
|
81
|
+
return 'Bearer issuer="https://auth.example.com"';
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
const result = await discoverOAuthFromResource('https://jmap.example.com/api');
|
|
88
|
+
expect(result).toEqual({
|
|
89
|
+
issuer: 'https://auth.example.com',
|
|
90
|
+
method: 'www-authenticate',
|
|
91
|
+
});
|
|
92
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
93
|
+
});
|
|
94
|
+
it('should return null when no OAuth info available', async () => {
|
|
95
|
+
// First call: protected-resource fails
|
|
96
|
+
mockFetch.mockResolvedValueOnce({
|
|
97
|
+
ok: false,
|
|
98
|
+
status: 404,
|
|
99
|
+
});
|
|
100
|
+
// Second call: JMAP URL returns 200 (no auth required)
|
|
101
|
+
mockFetch.mockResolvedValueOnce({
|
|
102
|
+
ok: true,
|
|
103
|
+
status: 200,
|
|
104
|
+
});
|
|
105
|
+
const result = await discoverOAuthFromResource('https://jmap.example.com/api');
|
|
106
|
+
expect(result).toBeNull();
|
|
107
|
+
});
|
|
108
|
+
it('should return null when WWW-Authenticate has no issuer', async () => {
|
|
109
|
+
// First call: protected-resource fails
|
|
110
|
+
mockFetch.mockResolvedValueOnce({
|
|
111
|
+
ok: false,
|
|
112
|
+
status: 404,
|
|
113
|
+
});
|
|
114
|
+
// Second call: JMAP URL returns 401 with WWW-Authenticate but no issuer
|
|
115
|
+
mockFetch.mockResolvedValueOnce({
|
|
116
|
+
ok: false,
|
|
117
|
+
status: 401,
|
|
118
|
+
headers: {
|
|
119
|
+
get: (name) => {
|
|
120
|
+
if (name === 'WWW-Authenticate') {
|
|
121
|
+
return 'Bearer realm="example"';
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
const result = await discoverOAuthFromResource('https://jmap.example.com/api');
|
|
128
|
+
expect(result).toBeNull();
|
|
129
|
+
});
|
|
130
|
+
it('should handle network errors gracefully', async () => {
|
|
131
|
+
mockFetch.mockRejectedValue(new Error('Network error'));
|
|
132
|
+
const result = await discoverOAuthFromResource('https://jmap.example.com/api');
|
|
133
|
+
expect(result).toBeNull();
|
|
134
|
+
});
|
|
135
|
+
it('should handle invalid URL gracefully', async () => {
|
|
136
|
+
const result = await discoverOAuthFromResource('not-a-url');
|
|
137
|
+
expect(result).toBeNull();
|
|
138
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
139
|
+
});
|
|
140
|
+
it('should respect custom timeout', async () => {
|
|
141
|
+
const abortError = new Error('Aborted');
|
|
142
|
+
abortError.name = 'AbortError';
|
|
143
|
+
mockFetch.mockRejectedValue(abortError);
|
|
144
|
+
const result = await discoverOAuthFromResource('https://jmap.example.com/api', 100);
|
|
145
|
+
expect(result).toBeNull();
|
|
146
|
+
});
|
|
147
|
+
it('should handle protected-resource with empty authorization_servers', async () => {
|
|
148
|
+
mockFetch.mockResolvedValueOnce({
|
|
149
|
+
ok: true,
|
|
150
|
+
json: async () => ({
|
|
151
|
+
authorization_servers: [],
|
|
152
|
+
}),
|
|
153
|
+
});
|
|
154
|
+
// Second call: fallback to WWW-Authenticate
|
|
155
|
+
mockFetch.mockResolvedValueOnce({
|
|
156
|
+
ok: false,
|
|
157
|
+
status: 401,
|
|
158
|
+
headers: {
|
|
159
|
+
get: (name) => {
|
|
160
|
+
if (name === 'WWW-Authenticate') {
|
|
161
|
+
return 'Bearer issuer="https://auth.example.com"';
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
const result = await discoverOAuthFromResource('https://jmap.example.com/api');
|
|
168
|
+
expect(result).toEqual({
|
|
169
|
+
issuer: 'https://auth.example.com',
|
|
170
|
+
method: 'www-authenticate',
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
it('should handle protected-resource with missing authorization_servers', async () => {
|
|
174
|
+
mockFetch.mockResolvedValueOnce({
|
|
175
|
+
ok: true,
|
|
176
|
+
json: async () => ({}),
|
|
177
|
+
});
|
|
178
|
+
// Second call: fallback to WWW-Authenticate
|
|
179
|
+
mockFetch.mockResolvedValueOnce({
|
|
180
|
+
ok: false,
|
|
181
|
+
status: 401,
|
|
182
|
+
headers: {
|
|
183
|
+
get: (name) => {
|
|
184
|
+
if (name === 'WWW-Authenticate') {
|
|
185
|
+
return 'Bearer issuer="https://auth.example.com"';
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
const result = await discoverOAuthFromResource('https://jmap.example.com/api');
|
|
192
|
+
expect(result).toEqual({
|
|
193
|
+
issuer: 'https://auth.example.com',
|
|
194
|
+
method: 'www-authenticate',
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
//# sourceMappingURL=oauth-discovery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-discovery.test.js","sourceRoot":"","sources":["../../src/discovery/oauth-discovery.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery orchestrator - chains DNS SRV, well-known, and OAuth discovery
|
|
3
|
+
* Provides high-level API to discover JMAP and OIDC settings from email address
|
|
4
|
+
*/
|
|
5
|
+
import { JmapDiscoveryResult, OidcDiscoveryResult } from './types.js';
|
|
6
|
+
export interface FullDiscoveryResult {
|
|
7
|
+
jmap: JmapDiscoveryResult;
|
|
8
|
+
oidc?: OidcDiscoveryResult;
|
|
9
|
+
email: string;
|
|
10
|
+
domain: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Extract domain from email address.
|
|
14
|
+
* @throws Error if email format invalid
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractDomain(email: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Discover JMAP and OIDC settings from an email address.
|
|
19
|
+
*
|
|
20
|
+
* Discovery stages:
|
|
21
|
+
* 1. Extract domain from email
|
|
22
|
+
* 2. Try DNS SRV lookup for _jmap._tcp.{domain}
|
|
23
|
+
* 3. If SRV found, construct URL and verify it works
|
|
24
|
+
* 4. If SRV fails, try .well-known/jmap on domain
|
|
25
|
+
* 5. If JMAP found, attempt OAuth discovery on that URL
|
|
26
|
+
*
|
|
27
|
+
* @param email User's email address (e.g., "user@example.com")
|
|
28
|
+
* @returns Full discovery result with JMAP and optional OIDC settings
|
|
29
|
+
* @throws DiscoveryError if JMAP server cannot be discovered
|
|
30
|
+
*/
|
|
31
|
+
export declare function discoverFromEmail(email: string): Promise<FullDiscoveryResult>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovery orchestrator - chains DNS SRV, well-known, and OAuth discovery
|
|
3
|
+
* Provides high-level API to discover JMAP and OIDC settings from email address
|
|
4
|
+
*/
|
|
5
|
+
import { resolveSrvRecord } from './dns-srv.js';
|
|
6
|
+
import { fetchWellKnownJmap, verifyJmapUrl } from './well-known.js';
|
|
7
|
+
import { discoverOAuthFromResource } from './oauth-discovery.js';
|
|
8
|
+
import { DiscoveryError, } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Extract domain from email address.
|
|
11
|
+
* @throws Error if email format invalid
|
|
12
|
+
*/
|
|
13
|
+
export function extractDomain(email) {
|
|
14
|
+
// Split on '@' and take second part
|
|
15
|
+
const parts = email.split('@');
|
|
16
|
+
if (parts.length !== 2) {
|
|
17
|
+
throw new Error('Invalid email format');
|
|
18
|
+
}
|
|
19
|
+
const domain = parts[1];
|
|
20
|
+
// Validate domain has at least one '.'
|
|
21
|
+
if (!domain.includes('.')) {
|
|
22
|
+
throw new Error('Invalid email format');
|
|
23
|
+
}
|
|
24
|
+
return domain;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Discover JMAP and OIDC settings from an email address.
|
|
28
|
+
*
|
|
29
|
+
* Discovery stages:
|
|
30
|
+
* 1. Extract domain from email
|
|
31
|
+
* 2. Try DNS SRV lookup for _jmap._tcp.{domain}
|
|
32
|
+
* 3. If SRV found, construct URL and verify it works
|
|
33
|
+
* 4. If SRV fails, try .well-known/jmap on domain
|
|
34
|
+
* 5. If JMAP found, attempt OAuth discovery on that URL
|
|
35
|
+
*
|
|
36
|
+
* @param email User's email address (e.g., "user@example.com")
|
|
37
|
+
* @returns Full discovery result with JMAP and optional OIDC settings
|
|
38
|
+
* @throws DiscoveryError if JMAP server cannot be discovered
|
|
39
|
+
*/
|
|
40
|
+
export async function discoverFromEmail(email) {
|
|
41
|
+
// Extract domain from email
|
|
42
|
+
const domain = extractDomain(email);
|
|
43
|
+
// Stage 1 - DNS SRV discovery
|
|
44
|
+
const srv = await resolveSrvRecord(domain);
|
|
45
|
+
if (srv) {
|
|
46
|
+
// Construct URL from SRV record
|
|
47
|
+
const url = srv.port === 443
|
|
48
|
+
? `https://${srv.hostname}/.well-known/jmap`
|
|
49
|
+
: `https://${srv.hostname}:${srv.port}/.well-known/jmap`;
|
|
50
|
+
// Verify the URL works
|
|
51
|
+
const verified = await verifyJmapUrl(url);
|
|
52
|
+
if (verified) {
|
|
53
|
+
const jmap = {
|
|
54
|
+
sessionUrl: verified,
|
|
55
|
+
method: 'dns-srv',
|
|
56
|
+
};
|
|
57
|
+
// Stage 4 - OAuth discovery
|
|
58
|
+
const oidc = await discoverOAuthFromResource(verified);
|
|
59
|
+
return {
|
|
60
|
+
jmap,
|
|
61
|
+
oidc: oidc ?? undefined,
|
|
62
|
+
email,
|
|
63
|
+
domain,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Stage 2 - Well-known fallback
|
|
68
|
+
const wellKnown = await fetchWellKnownJmap(domain);
|
|
69
|
+
if (wellKnown) {
|
|
70
|
+
const jmap = {
|
|
71
|
+
sessionUrl: wellKnown,
|
|
72
|
+
method: 'well-known-direct',
|
|
73
|
+
};
|
|
74
|
+
// Stage 4 - OAuth discovery
|
|
75
|
+
const oidc = await discoverOAuthFromResource(wellKnown);
|
|
76
|
+
return {
|
|
77
|
+
jmap,
|
|
78
|
+
oidc: oidc ?? undefined,
|
|
79
|
+
email,
|
|
80
|
+
domain,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Stage 3 - Failure
|
|
84
|
+
throw new DiscoveryError(`Could not discover JMAP server for domain "${domain}". ` +
|
|
85
|
+
'The domain does not have a JMAP SRV record or .well-known/jmap endpoint.', domain, 'well-known');
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../src/discovery/orchestrator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAGL,cAAc,GACf,MAAM,YAAY,CAAC;AASpB;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,oCAAoC;IACpC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAExB,uCAAuC;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAa;IAEb,4BAA4B;IAC5B,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEpC,8BAA8B;IAC9B,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,GAAG,EAAE,CAAC;QACR,gCAAgC;QAChC,MAAM,GAAG,GACP,GAAG,CAAC,IAAI,KAAK,GAAG;YACd,CAAC,CAAC,WAAW,GAAG,CAAC,QAAQ,mBAAmB;YAC5C,CAAC,CAAC,WAAW,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,mBAAmB,CAAC;QAE7D,uBAAuB;QACvB,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,IAAI,GAAwB;gBAChC,UAAU,EAAE,QAAQ;gBACpB,MAAM,EAAE,SAAS;aAClB,CAAC;YAEF,4BAA4B;YAC5B,MAAM,IAAI,GAAG,MAAM,yBAAyB,CAAC,QAAQ,CAAC,CAAC;YAEvD,OAAO;gBACL,IAAI;gBACJ,IAAI,EAAE,IAAI,IAAI,SAAS;gBACvB,KAAK;gBACL,MAAM;aACP,CAAC;QACJ,CAAC;IACH,CAAC;IAED,gCAAgC;IAChC,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACnD,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,IAAI,GAAwB;YAChC,UAAU,EAAE,SAAS;YACrB,MAAM,EAAE,mBAAmB;SAC5B,CAAC;QAEF,4BAA4B;QAC5B,MAAM,IAAI,GAAG,MAAM,yBAAyB,CAAC,SAAS,CAAC,CAAC;QAExD,OAAO;YACL,IAAI;YACJ,IAAI,EAAE,IAAI,IAAI,SAAS;YACvB,KAAK;YACL,MAAM;SACP,CAAC;IACJ,CAAC;IAED,oBAAoB;IACpB,MAAM,IAAI,cAAc,CACtB,8CAA8C,MAAM,KAAK;QACvD,0EAA0E,EAC5E,MAAM,EACN,YAAY,CACb,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for discovery orchestrator
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
5
|
+
import { discoverFromEmail, extractDomain } from './orchestrator.js';
|
|
6
|
+
import { DiscoveryError } from './types.js';
|
|
7
|
+
// Mock the discovery modules
|
|
8
|
+
vi.mock('./dns-srv.js');
|
|
9
|
+
vi.mock('./well-known.js');
|
|
10
|
+
vi.mock('./oauth-discovery.js');
|
|
11
|
+
import { resolveSrvRecord } from './dns-srv.js';
|
|
12
|
+
import { fetchWellKnownJmap, verifyJmapUrl } from './well-known.js';
|
|
13
|
+
import { discoverOAuthFromResource } from './oauth-discovery.js';
|
|
14
|
+
describe('extractDomain', () => {
|
|
15
|
+
it('extracts domain from valid email', () => {
|
|
16
|
+
expect(extractDomain('user@example.com')).toBe('example.com');
|
|
17
|
+
});
|
|
18
|
+
it('extracts domain from email with subdomain', () => {
|
|
19
|
+
expect(extractDomain('user@mail.example.com')).toBe('mail.example.com');
|
|
20
|
+
});
|
|
21
|
+
it('throws on email without @', () => {
|
|
22
|
+
expect(() => extractDomain('userexample.com')).toThrow('Invalid email format');
|
|
23
|
+
});
|
|
24
|
+
it('throws on domain without dot', () => {
|
|
25
|
+
expect(() => extractDomain('user@localhost')).toThrow('Invalid email format');
|
|
26
|
+
});
|
|
27
|
+
it('throws on multiple @ symbols', () => {
|
|
28
|
+
expect(() => extractDomain('user@test@example.com')).toThrow('Invalid email format');
|
|
29
|
+
});
|
|
30
|
+
it('throws on empty domain', () => {
|
|
31
|
+
expect(() => extractDomain('user@')).toThrow('Invalid email format');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
describe('discoverFromEmail', () => {
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
vi.clearAllMocks();
|
|
37
|
+
});
|
|
38
|
+
describe('DNS SRV success path', () => {
|
|
39
|
+
it('discovers via DNS SRV with standard port', async () => {
|
|
40
|
+
// Mock DNS SRV success
|
|
41
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue({
|
|
42
|
+
hostname: 'jmap.example.com',
|
|
43
|
+
port: 443,
|
|
44
|
+
});
|
|
45
|
+
// Mock URL verification
|
|
46
|
+
vi.mocked(verifyJmapUrl).mockResolvedValue('https://jmap.example.com/.well-known/jmap');
|
|
47
|
+
// Mock OAuth discovery
|
|
48
|
+
vi.mocked(discoverOAuthFromResource).mockResolvedValue({
|
|
49
|
+
issuer: 'https://auth.example.com',
|
|
50
|
+
method: 'protected-resource',
|
|
51
|
+
});
|
|
52
|
+
const result = await discoverFromEmail('user@example.com');
|
|
53
|
+
expect(result).toEqual({
|
|
54
|
+
jmap: {
|
|
55
|
+
sessionUrl: 'https://jmap.example.com/.well-known/jmap',
|
|
56
|
+
method: 'dns-srv',
|
|
57
|
+
},
|
|
58
|
+
oidc: {
|
|
59
|
+
issuer: 'https://auth.example.com',
|
|
60
|
+
method: 'protected-resource',
|
|
61
|
+
},
|
|
62
|
+
email: 'user@example.com',
|
|
63
|
+
domain: 'example.com',
|
|
64
|
+
});
|
|
65
|
+
// Verify correct URL was constructed
|
|
66
|
+
expect(verifyJmapUrl).toHaveBeenCalledWith('https://jmap.example.com/.well-known/jmap');
|
|
67
|
+
});
|
|
68
|
+
it('discovers via DNS SRV with non-standard port', async () => {
|
|
69
|
+
// Mock DNS SRV success with non-standard port
|
|
70
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue({
|
|
71
|
+
hostname: 'jmap.example.com',
|
|
72
|
+
port: 8443,
|
|
73
|
+
});
|
|
74
|
+
// Mock URL verification
|
|
75
|
+
vi.mocked(verifyJmapUrl).mockResolvedValue('https://jmap.example.com:8443/.well-known/jmap');
|
|
76
|
+
// Mock OAuth discovery (no OAuth info)
|
|
77
|
+
vi.mocked(discoverOAuthFromResource).mockResolvedValue(null);
|
|
78
|
+
const result = await discoverFromEmail('user@example.com');
|
|
79
|
+
expect(result).toEqual({
|
|
80
|
+
jmap: {
|
|
81
|
+
sessionUrl: 'https://jmap.example.com:8443/.well-known/jmap',
|
|
82
|
+
method: 'dns-srv',
|
|
83
|
+
},
|
|
84
|
+
oidc: undefined,
|
|
85
|
+
email: 'user@example.com',
|
|
86
|
+
domain: 'example.com',
|
|
87
|
+
});
|
|
88
|
+
// Verify URL includes non-standard port
|
|
89
|
+
expect(verifyJmapUrl).toHaveBeenCalledWith('https://jmap.example.com:8443/.well-known/jmap');
|
|
90
|
+
});
|
|
91
|
+
it('discovers without OAuth info', async () => {
|
|
92
|
+
// Mock DNS SRV success
|
|
93
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue({
|
|
94
|
+
hostname: 'jmap.example.com',
|
|
95
|
+
port: 443,
|
|
96
|
+
});
|
|
97
|
+
// Mock URL verification
|
|
98
|
+
vi.mocked(verifyJmapUrl).mockResolvedValue('https://jmap.example.com/.well-known/jmap');
|
|
99
|
+
// Mock OAuth discovery returning null
|
|
100
|
+
vi.mocked(discoverOAuthFromResource).mockResolvedValue(null);
|
|
101
|
+
const result = await discoverFromEmail('user@example.com');
|
|
102
|
+
expect(result.oidc).toBeUndefined();
|
|
103
|
+
expect(result.jmap.sessionUrl).toBe('https://jmap.example.com/.well-known/jmap');
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
describe('Well-known fallback path', () => {
|
|
107
|
+
it('falls back to well-known when DNS SRV returns null', async () => {
|
|
108
|
+
// Mock DNS SRV failure
|
|
109
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue(null);
|
|
110
|
+
// Mock well-known success
|
|
111
|
+
vi.mocked(fetchWellKnownJmap).mockResolvedValue('https://example.com/jmap/session');
|
|
112
|
+
// Mock OAuth discovery
|
|
113
|
+
vi.mocked(discoverOAuthFromResource).mockResolvedValue({
|
|
114
|
+
issuer: 'https://auth.example.com',
|
|
115
|
+
method: 'www-authenticate',
|
|
116
|
+
});
|
|
117
|
+
const result = await discoverFromEmail('user@example.com');
|
|
118
|
+
expect(result).toEqual({
|
|
119
|
+
jmap: {
|
|
120
|
+
sessionUrl: 'https://example.com/jmap/session',
|
|
121
|
+
method: 'well-known-direct',
|
|
122
|
+
},
|
|
123
|
+
oidc: {
|
|
124
|
+
issuer: 'https://auth.example.com',
|
|
125
|
+
method: 'www-authenticate',
|
|
126
|
+
},
|
|
127
|
+
email: 'user@example.com',
|
|
128
|
+
domain: 'example.com',
|
|
129
|
+
});
|
|
130
|
+
// Verify DNS SRV was tried first
|
|
131
|
+
expect(resolveSrvRecord).toHaveBeenCalledWith('example.com');
|
|
132
|
+
// Verify well-known was called
|
|
133
|
+
expect(fetchWellKnownJmap).toHaveBeenCalledWith('example.com');
|
|
134
|
+
});
|
|
135
|
+
it('falls back to well-known when DNS SRV verification fails', async () => {
|
|
136
|
+
// Mock DNS SRV success but verification failure
|
|
137
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue({
|
|
138
|
+
hostname: 'jmap.example.com',
|
|
139
|
+
port: 443,
|
|
140
|
+
});
|
|
141
|
+
vi.mocked(verifyJmapUrl).mockResolvedValue(null);
|
|
142
|
+
// Mock well-known success
|
|
143
|
+
vi.mocked(fetchWellKnownJmap).mockResolvedValue('https://example.com/.well-known/jmap');
|
|
144
|
+
// Mock no OAuth
|
|
145
|
+
vi.mocked(discoverOAuthFromResource).mockResolvedValue(null);
|
|
146
|
+
const result = await discoverFromEmail('user@example.com');
|
|
147
|
+
expect(result.jmap.method).toBe('well-known-direct');
|
|
148
|
+
expect(result.jmap.sessionUrl).toBe('https://example.com/.well-known/jmap');
|
|
149
|
+
// Verify both were tried
|
|
150
|
+
expect(resolveSrvRecord).toHaveBeenCalled();
|
|
151
|
+
expect(fetchWellKnownJmap).toHaveBeenCalled();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
describe('Complete failure path', () => {
|
|
155
|
+
it('throws DiscoveryError when all methods fail', async () => {
|
|
156
|
+
// Mock DNS SRV failure
|
|
157
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue(null);
|
|
158
|
+
// Mock well-known failure
|
|
159
|
+
vi.mocked(fetchWellKnownJmap).mockResolvedValue(null);
|
|
160
|
+
await expect(discoverFromEmail('user@example.com')).rejects.toThrow(DiscoveryError);
|
|
161
|
+
await expect(discoverFromEmail('user@example.com')).rejects.toThrow('Could not discover JMAP server for domain "example.com"');
|
|
162
|
+
});
|
|
163
|
+
it('throws DiscoveryError with correct domain and stage', async () => {
|
|
164
|
+
// Mock all failures
|
|
165
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue(null);
|
|
166
|
+
vi.mocked(fetchWellKnownJmap).mockResolvedValue(null);
|
|
167
|
+
try {
|
|
168
|
+
await discoverFromEmail('user@example.com');
|
|
169
|
+
expect.fail('Should have thrown DiscoveryError');
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
expect(error).toBeInstanceOf(DiscoveryError);
|
|
173
|
+
if (error instanceof DiscoveryError) {
|
|
174
|
+
expect(error.domain).toBe('example.com');
|
|
175
|
+
expect(error.stage).toBe('well-known');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
it('throws on invalid email format', async () => {
|
|
180
|
+
await expect(discoverFromEmail('not-an-email')).rejects.toThrow('Invalid email format');
|
|
181
|
+
// Verify no discovery methods were called
|
|
182
|
+
expect(resolveSrvRecord).not.toHaveBeenCalled();
|
|
183
|
+
expect(fetchWellKnownJmap).not.toHaveBeenCalled();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('OAuth discovery integration', () => {
|
|
187
|
+
it('includes OAuth info when discovered via protected-resource', async () => {
|
|
188
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue({
|
|
189
|
+
hostname: 'jmap.example.com',
|
|
190
|
+
port: 443,
|
|
191
|
+
});
|
|
192
|
+
vi.mocked(verifyJmapUrl).mockResolvedValue('https://jmap.example.com/.well-known/jmap');
|
|
193
|
+
vi.mocked(discoverOAuthFromResource).mockResolvedValue({
|
|
194
|
+
issuer: 'https://auth.example.com',
|
|
195
|
+
clientId: 'optional-client-id',
|
|
196
|
+
method: 'protected-resource',
|
|
197
|
+
});
|
|
198
|
+
const result = await discoverFromEmail('user@example.com');
|
|
199
|
+
expect(result.oidc).toEqual({
|
|
200
|
+
issuer: 'https://auth.example.com',
|
|
201
|
+
clientId: 'optional-client-id',
|
|
202
|
+
method: 'protected-resource',
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
it('includes OAuth info when discovered via www-authenticate', async () => {
|
|
206
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue(null);
|
|
207
|
+
vi.mocked(fetchWellKnownJmap).mockResolvedValue('https://example.com/.well-known/jmap');
|
|
208
|
+
vi.mocked(discoverOAuthFromResource).mockResolvedValue({
|
|
209
|
+
issuer: 'https://auth.example.com',
|
|
210
|
+
method: 'www-authenticate',
|
|
211
|
+
});
|
|
212
|
+
const result = await discoverFromEmail('user@example.com');
|
|
213
|
+
expect(result.oidc).toEqual({
|
|
214
|
+
issuer: 'https://auth.example.com',
|
|
215
|
+
method: 'www-authenticate',
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
it('handles OAuth discovery returning null gracefully', async () => {
|
|
219
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue({
|
|
220
|
+
hostname: 'jmap.example.com',
|
|
221
|
+
port: 443,
|
|
222
|
+
});
|
|
223
|
+
vi.mocked(verifyJmapUrl).mockResolvedValue('https://jmap.example.com/.well-known/jmap');
|
|
224
|
+
vi.mocked(discoverOAuthFromResource).mockResolvedValue(null);
|
|
225
|
+
const result = await discoverFromEmail('user@example.com');
|
|
226
|
+
expect(result.oidc).toBeUndefined();
|
|
227
|
+
expect(result.jmap).toBeDefined();
|
|
228
|
+
});
|
|
229
|
+
it('calls OAuth discovery with correct JMAP URL', async () => {
|
|
230
|
+
const jmapUrl = 'https://jmap.example.com/.well-known/jmap';
|
|
231
|
+
vi.mocked(resolveSrvRecord).mockResolvedValue({
|
|
232
|
+
hostname: 'jmap.example.com',
|
|
233
|
+
port: 443,
|
|
234
|
+
});
|
|
235
|
+
vi.mocked(verifyJmapUrl).mockResolvedValue(jmapUrl);
|
|
236
|
+
vi.mocked(discoverOAuthFromResource).mockResolvedValue(null);
|
|
237
|
+
await discoverFromEmail('user@example.com');
|
|
238
|
+
expect(discoverOAuthFromResource).toHaveBeenCalledWith(jmapUrl);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
//# sourceMappingURL=orchestrator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.test.js","sourceRoot":"","sources":["../../src/discovery/orchestrator.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JMAP server discovery result types
|
|
3
|
+
* Supports DNS SRV, .well-known/jmap, and manual configuration
|
|
4
|
+
*/
|
|
5
|
+
export interface JmapDiscoveryResult {
|
|
6
|
+
sessionUrl: string;
|
|
7
|
+
method: 'dns-srv' | 'well-known-direct' | 'manual';
|
|
8
|
+
}
|
|
9
|
+
export interface OidcDiscoveryResult {
|
|
10
|
+
issuer: string;
|
|
11
|
+
clientId?: string;
|
|
12
|
+
method: 'protected-resource' | 'www-authenticate' | 'well-known-oidc' | 'manual';
|
|
13
|
+
}
|
|
14
|
+
export declare class DiscoveryError extends Error {
|
|
15
|
+
readonly domain: string;
|
|
16
|
+
readonly stage: 'dns' | 'well-known' | 'verification';
|
|
17
|
+
constructor(message: string, domain: string, stage: 'dns' | 'well-known' | 'verification');
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JMAP server discovery result types
|
|
3
|
+
* Supports DNS SRV, .well-known/jmap, and manual configuration
|
|
4
|
+
*/
|
|
5
|
+
export class DiscoveryError extends Error {
|
|
6
|
+
domain;
|
|
7
|
+
stage;
|
|
8
|
+
constructor(message, domain, stage) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.domain = domain;
|
|
11
|
+
this.stage = stage;
|
|
12
|
+
this.name = 'DiscoveryError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/discovery/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAGrB;IACA;IAHlB,YACE,OAAe,EACC,MAAc,EACd,KAA4C;QAE5D,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAQ;QACd,UAAK,GAAL,KAAK,CAAuC;QAG5D,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF"}
|