fastify-txstate 3.6.9 → 4.0.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/lib/oauth.js ADDED
@@ -0,0 +1,272 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { decodeJwt } from 'jose';
3
+ import { htmlEncode, isBlank, isNotBlank } from 'txstate-utils';
4
+ import { apiBaseUrl, uiBaseUrl } from "./server.js";
5
+ import { accessTokenCookieName, getOAuthDiscovery, getOAuthIssuerUrls, init, oauthCookieName, refreshCookieName, registeredExceptRoutes, registeredOptionalRoutes, toInternalUrl, wrapRefreshToken } from "./jwt-auth.js";
6
+ /**
7
+ * Register cookie-based OAuth login/logout endpoints. Uses the authorization code flow
8
+ * with PKCE (S256) to exchange a code for tokens, then stores the ID token in an HttpOnly
9
+ * cookie. The access token and refresh token are stored in separate cookies (optionally
10
+ * encrypted via OAUTH_COOKIE_SECRET) so that the ID token can be transparently refreshed
11
+ * by jwtAuthenticate when it expires, and the access token is available at
12
+ * `req.auth.accessToken` for calling provider APIs.
13
+ *
14
+ * Requires OAUTH_COOKIE_CLIENT_ID environment variable. OAUTH_COOKIE_CLIENT_SECRET is
15
+ * optional — PKCE provides the security for the code exchange, but some providers require
16
+ * a client secret even with PKCE. OAUTH_COOKIE_SECRET is optional — if set, the access
17
+ * token and refresh token cookies are encrypted with AES-256-GCM; if not, they are stored
18
+ * as plaintext (still HttpOnly and Secure).
19
+ *
20
+ * Trusted issuers are configured via OAUTH_URLS or JWT_TRUSTED_ISSUERS (see jwt-auth.ts).
21
+ *
22
+ * Registers three routes:
23
+ * - `/.oauthRedirect` - Redirects to the OAuth provider's login page. The client passes
24
+ * `requestedUrl` (required) which is sent to the provider as the `state` parameter,
25
+ * round-tripped back, and used as the redirect destination after login.
26
+ * - `/.oauthCallback` - Handles the provider's redirect, exchanges the code for tokens
27
+ * using the PKCE code verifier. Sets the ID token (or JWT access token as fallback),
28
+ * access token, and refresh token as cookies.
29
+ * - `/.oauthLogout` - Clears all OAuth cookies and redirects to the provider's logout
30
+ * endpoint if available.
31
+ */
32
+ export function registerOAuthCookieRoutes(app, options) {
33
+ const clientId = process.env.OAUTH_COOKIE_CLIENT_ID;
34
+ if (!clientId)
35
+ throw new Error('OAUTH_COOKIE_CLIENT_ID environment variable must be set when using registerOAuthCookieRoutes.');
36
+ init();
37
+ const clientSecret = process.env.OAUTH_COOKIE_CLIENT_SECRET;
38
+ registeredExceptRoutes.add('/.oauthCallback');
39
+ registeredExceptRoutes.add('/.oauthRedirect');
40
+ registeredOptionalRoutes.add('/.oauthLogout');
41
+ const callbackPath = '/.oauthCallback';
42
+ const pkceVerifierCookieName = oauthCookieName + '_pkce';
43
+ const pkceVerifierCookieRegex = new RegExp(`${pkceVerifierCookieName}=([A-Za-z0-9_-]+)`, 'v');
44
+ const issuerCookieName = oauthCookieName + '_iss';
45
+ const issuerCookieRegex = new RegExp(`${issuerCookieName}=([^;]+)`, 'v');
46
+ // flush any pending cookies queued during token refresh by jwtAuthenticate
47
+ app.addHook('onSend', async (req, res) => {
48
+ if (req.pendingOAuthCookies?.length) {
49
+ for (const cookie of req.pendingOAuthCookies)
50
+ void res.header('Set-Cookie', cookie);
51
+ }
52
+ });
53
+ app.get('/.oauthRedirect', {
54
+ schema: {
55
+ querystring: {
56
+ type: 'object',
57
+ properties: {
58
+ requestedUrl: { type: 'string', format: 'uri' },
59
+ scope: { type: 'string' },
60
+ issuer: { type: 'string', format: 'uri' }
61
+ },
62
+ required: ['requestedUrl'],
63
+ additionalProperties: false
64
+ }
65
+ }
66
+ }, async (req, res) => {
67
+ if (req.originChecker && !req.originChecker.check(req.query.requestedUrl, req.hostname)) {
68
+ void res.status(403);
69
+ return 'Requested URL failed origin check.';
70
+ }
71
+ const issuerUrls = getOAuthIssuerUrls();
72
+ if (!issuerUrls.length)
73
+ throw new Error('No OAuth issuers are configured. Set OAUTH_URLS or include oauth issuers in JWT_TRUSTED_ISSUERS.');
74
+ // if multiple issuers and no issuer specified, show a login selection page
75
+ if (!req.query.issuer && issuerUrls.length > 1 && options?.loginPage) {
76
+ const issuers = issuerUrls.map(iss => {
77
+ const redirectUrl = new URL(apiBaseUrl(req) + '/.oauthRedirect');
78
+ redirectUrl.searchParams.set('requestedUrl', req.query.requestedUrl);
79
+ if (req.query.scope)
80
+ redirectUrl.searchParams.set('scope', req.query.scope);
81
+ redirectUrl.searchParams.set('issuer', iss);
82
+ return { issuerUrl: iss, redirectHref: redirectUrl.toString() };
83
+ });
84
+ void res.type('text/html');
85
+ return options.loginPage(issuers);
86
+ }
87
+ const issuerUrl = req.query.issuer && issuerUrls.includes(req.query.issuer)
88
+ ? req.query.issuer
89
+ : issuerUrls[0];
90
+ const discovery = await getOAuthDiscovery(issuerUrl);
91
+ if (!discovery?.authorization_endpoint)
92
+ throw new Error(`OAuth issuer ${issuerUrl} does not have an authorization endpoint.`);
93
+ const codeVerifier = randomBytes(32).toString('base64url');
94
+ const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');
95
+ const redirectUri = apiBaseUrl(req) + callbackPath;
96
+ const authUrl = new URL(discovery.authorization_endpoint);
97
+ authUrl.searchParams.set('response_type', 'code');
98
+ authUrl.searchParams.set('client_id', clientId);
99
+ authUrl.searchParams.set('redirect_uri', redirectUri);
100
+ const isGoogle = discovery.authorization_endpoint.includes('accounts.google.com');
101
+ const scopeParts = new Set((req.query.scope ?? 'openid').split(' '));
102
+ for (const s of options?.scopes ?? [])
103
+ scopeParts.add(s);
104
+ if (!isGoogle && !req.query.scope)
105
+ scopeParts.add('offline_access');
106
+ authUrl.searchParams.set('scope', [...scopeParts].join(' '));
107
+ authUrl.searchParams.set('state', req.query.requestedUrl);
108
+ authUrl.searchParams.set('code_challenge', codeChallenge);
109
+ authUrl.searchParams.set('code_challenge_method', 'S256');
110
+ if (isGoogle) {
111
+ authUrl.searchParams.set('access_type', 'offline');
112
+ authUrl.searchParams.set('prompt', 'consent');
113
+ }
114
+ // store the code verifier and chosen issuer in short-lived HttpOnly cookies
115
+ void res.header('Set-Cookie', `${pkceVerifierCookieName}=${codeVerifier}; Path=${callbackPath}; Secure; HttpOnly; SameSite=Lax; Max-Age=600`);
116
+ void res.header('Set-Cookie', `${issuerCookieName}=${encodeURIComponent(issuerUrl)}; Path=${callbackPath}; Secure; HttpOnly; SameSite=Lax; Max-Age=600`);
117
+ return await res.redirect(authUrl.toString());
118
+ });
119
+ app.get(callbackPath, {
120
+ schema: {
121
+ querystring: {
122
+ type: 'object',
123
+ properties: {
124
+ code: { type: 'string' },
125
+ state: { type: 'string' }
126
+ },
127
+ required: ['code', 'state'],
128
+ additionalProperties: false
129
+ }
130
+ }
131
+ }, async (req, res) => {
132
+ const verifierMatch = req.headers.cookie?.match(pkceVerifierCookieRegex);
133
+ if (!verifierMatch) {
134
+ void res.status(403);
135
+ return 'Missing PKCE code verifier. The login flow may have expired.';
136
+ }
137
+ const codeVerifier = verifierMatch[1];
138
+ const issuerUrls = getOAuthIssuerUrls();
139
+ const issuerMatch = req.headers.cookie?.match(issuerCookieRegex);
140
+ const issuerUrl = issuerMatch ? decodeURIComponent(issuerMatch[1]) : issuerUrls[0];
141
+ const discovery = await getOAuthDiscovery(issuerUrl);
142
+ if (!discovery?.token_endpoint)
143
+ throw new Error(`OAuth issuer ${issuerUrl} does not have a token endpoint.`);
144
+ const redirectUri = apiBaseUrl(req) + callbackPath;
145
+ const body = {
146
+ grant_type: 'authorization_code',
147
+ code: req.query.code,
148
+ redirect_uri: redirectUri,
149
+ client_id: clientId,
150
+ code_verifier: codeVerifier
151
+ };
152
+ if (clientSecret)
153
+ body.client_secret = clientSecret;
154
+ const tokenResp = await fetch(toInternalUrl(discovery.token_endpoint), {
155
+ method: 'POST',
156
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
157
+ body: new URLSearchParams(body)
158
+ });
159
+ if (!tokenResp.ok) {
160
+ req.log.error(`OAuth token exchange failed: ${tokenResp.status} ${await tokenResp.text()}`);
161
+ void res.status(502);
162
+ return 'OAuth token exchange failed.';
163
+ }
164
+ const tokens = await tokenResp.json();
165
+ // prefer id_token, fall back to access_token if it's a JWT (some providers
166
+ // like Okta and Microsoft issue JWT access tokens)
167
+ let sessionToken = tokens.id_token;
168
+ if (!sessionToken && tokens.access_token) {
169
+ try {
170
+ decodeJwt(tokens.access_token);
171
+ sessionToken = tokens.access_token;
172
+ }
173
+ catch { /* not a JWT, can't use it */ }
174
+ }
175
+ if (!sessionToken) {
176
+ req.log.error('OAuth token response did not include a usable JWT (no id_token and access_token is not a JWT).');
177
+ void res.status(502);
178
+ return 'OAuth provider did not return a usable JWT.';
179
+ }
180
+ const destination = isNotBlank(req.query.state) ? req.query.state : uiBaseUrl(req);
181
+ const cookies = [
182
+ `${oauthCookieName}=${sessionToken}; Path=/; Secure; HttpOnly; SameSite=Lax`,
183
+ `${pkceVerifierCookieName}=; Path=${callbackPath}; Secure; HttpOnly; SameSite=Lax; Expires=Thu, 01 Jan 1970 00:00:00 GMT`,
184
+ `${issuerCookieName}=; Path=${callbackPath}; Secure; HttpOnly; SameSite=Lax; Expires=Thu, 01 Jan 1970 00:00:00 GMT`
185
+ ];
186
+ if (tokens.access_token) {
187
+ cookies.push(`${accessTokenCookieName}=${wrapRefreshToken(tokens.access_token)}; Path=/; Secure; HttpOnly; SameSite=Lax`);
188
+ }
189
+ if (tokens.refresh_token) {
190
+ cookies.push(`${refreshCookieName}=${wrapRefreshToken(tokens.refresh_token)}; Path=/; Secure; HttpOnly; SameSite=Lax`);
191
+ }
192
+ for (const cookie of cookies)
193
+ void res.header('Set-Cookie', cookie);
194
+ void res.type('text/html');
195
+ return `<!DOCTYPE html>
196
+ <html lang="en">
197
+ <head>
198
+ <meta charset="UTF-8">
199
+ <meta http-equiv="refresh" content="0; url=${htmlEncode(destination)}">
200
+ <title>Logging in...</title>
201
+ </head>
202
+ <body>
203
+ </body>
204
+ </html>`;
205
+ });
206
+ app.get('/.oauthLogout', {
207
+ schema: {
208
+ querystring: {
209
+ type: 'object',
210
+ properties: {
211
+ returnUrl: { type: 'string', format: 'uri' }
212
+ },
213
+ additionalProperties: false
214
+ },
215
+ headers: {
216
+ type: 'object',
217
+ properties: {
218
+ cookie: { type: 'string', pattern: `${oauthCookieName}=[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+` }
219
+ },
220
+ required: ['cookie']
221
+ }
222
+ }
223
+ }, async (req, res) => {
224
+ if (req.query.returnUrl && req.originChecker && !req.originChecker.check(req.query.returnUrl, req.hostname)) {
225
+ void res.status(403);
226
+ return 'Return URL failed origin check.';
227
+ }
228
+ const postLogoutDestination = req.query.returnUrl ?? uiBaseUrl(req);
229
+ let redirectUrl = postLogoutDestination;
230
+ if (req.auth?.issuerConfig?.logoutUrl) {
231
+ const logoutUrl = new URL(req.auth.issuerConfig.logoutUrl);
232
+ if (isNotBlank(req.auth.token))
233
+ logoutUrl.searchParams.set('id_token_hint', req.auth.token);
234
+ logoutUrl.searchParams.set('post_logout_redirect_uri', postLogoutDestination);
235
+ redirectUrl = logoutUrl.toString();
236
+ }
237
+ const cookies = [
238
+ `${oauthCookieName}=; Path=/; Secure; HttpOnly; SameSite=Lax; Expires=Thu, 01 Jan 1970 00:00:00 GMT`,
239
+ `${accessTokenCookieName}=; Path=/; Secure; HttpOnly; SameSite=Lax; Expires=Thu, 01 Jan 1970 00:00:00 GMT`,
240
+ `${refreshCookieName}=; Path=/; Secure; HttpOnly; SameSite=Lax; Expires=Thu, 01 Jan 1970 00:00:00 GMT`
241
+ ];
242
+ for (const cookie of cookies)
243
+ void res.header('Set-Cookie', cookie);
244
+ return `<!DOCTYPE html>
245
+ <html lang="en">
246
+ <head>
247
+ <meta charset="UTF-8">
248
+ <meta http-equiv="refresh" content="0; url=${htmlEncode(redirectUrl)}">
249
+ <title>Logging out...</title>
250
+ </head>
251
+ <body>
252
+ </body>
253
+ </html>`;
254
+ });
255
+ }
256
+ /**
257
+ * This function is available for server-side view code instead of a client-side application
258
+ * using a framework. It will automatically redirect the user through the OAuth login flow
259
+ * (via /.oauthRedirect, which must be registered by registerOAuthCookieRoutes) and return
260
+ * true if they are not authenticated. Otherwise it simply returns false.
261
+ */
262
+ export async function requireCookieAuthOAuth(req, res) {
263
+ if (isBlank(req.auth?.username)) {
264
+ const redirectUrl = new URL(apiBaseUrl(req) + '/.oauthRedirect');
265
+ redirectUrl.searchParams.set('requestedUrl', apiBaseUrl(req) + req.originalUrl);
266
+ void res.redirect(redirectUrl.toString());
267
+ return true;
268
+ }
269
+ else {
270
+ return false;
271
+ }
272
+ }
@@ -1,9 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.postFormData = postFormData;
4
- const node_stream_1 = require("node:stream");
5
- const web_1 = require("node:stream/web");
6
- async function postFormData(url, fields, headers = {}) {
1
+ import { Readable } from 'node:stream';
2
+ import { ReadableStream } from 'node:stream/web';
3
+ export async function postFormData(url, fields, headers = {}) {
7
4
  const encoder = new TextEncoder();
8
5
  const boundary = `${Math.random().toString(36).substring(2, 15)}${Math.random().toString(36).substring(2, 15)}`;
9
6
  const footer = `--${boundary}--\r\n`;
@@ -18,7 +15,7 @@ async function postFormData(url, fields, headers = {}) {
18
15
  }
19
16
  let i = 0;
20
17
  let part = 'header';
21
- const stream = new web_1.ReadableStream({
18
+ const stream = new ReadableStream({
22
19
  async pull(controller) {
23
20
  if (i === chunks.length) {
24
21
  controller.enqueue(encoder.encode(footer));
@@ -31,27 +28,26 @@ async function postFormData(url, fields, headers = {}) {
31
28
  part = 'content';
32
29
  }
33
30
  else if (part === 'content') {
34
- const { value, done } = await chunk.contentReader.read();
35
- if (done)
31
+ const result = await chunk.contentReader.read();
32
+ if (result.done)
36
33
  part = 'footer';
37
34
  else
38
- controller.enqueue(value);
35
+ controller.enqueue(result.value);
39
36
  }
40
- else if (part === 'footer') {
37
+ else {
41
38
  controller.enqueue(encoder.encode(chunk.footer));
42
- i++;
39
+ i += 1;
43
40
  part = 'header';
44
41
  }
45
42
  }
46
43
  },
47
44
  cancel() {
48
45
  for (const chunk of chunks) {
49
- if (chunk.contentReader.cancel) {
50
- chunk.contentReader.cancel();
51
- }
46
+ void chunk.contentReader.cancel();
52
47
  }
53
48
  }
54
49
  });
50
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- duplex and node ReadableStream aren't in standard RequestInit types
55
51
  return await fetch(url, {
56
52
  method: 'POST',
57
53
  headers,
@@ -74,12 +70,12 @@ class FormDataChunk {
74
70
  if (isFileField(field)) {
75
71
  this.header += `; filename="${field.filename ?? field.name}"\r\nContent-Type: ${field.filetype ?? 'application/octet-stream'}`;
76
72
  this.contentsize = field.filesize;
77
- this.contentReader = (field.value instanceof node_stream_1.Readable ? web_1.ReadableStream.from(field.value) : field.value).getReader();
73
+ this.contentReader = (field.value instanceof Readable ? ReadableStream.from(field.value) : field.value).getReader();
78
74
  }
79
75
  else {
80
76
  const encoded = encoder.encode(field.value);
81
77
  this.contentsize = encoded.length;
82
- this.contentReader = new web_1.ReadableStream({
78
+ this.contentReader = new ReadableStream({
83
79
  start: controller => {
84
80
  controller.enqueue(encoded);
85
81
  controller.close();
@@ -1,12 +1,22 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
1
  import { type FastifyDynamicSwaggerOptions } from '@fastify/swagger';
4
2
  import { type FastifySwaggerUiOptions } from '@fastify/swagger-ui';
5
3
  import type { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts';
6
- import { type FastifyInstance, type FastifyRequest, type FastifyReply, type FastifyServerOptions, type FastifyLoggerOptions, type FastifyBaseLogger, type RawServerDefault } from 'fastify';
4
+ import { type FastifyInstance, type FastifyRequest, type FastifyReply, type FastifyServerOptions, type FastifyBaseLogger, type RawServerDefault } from 'fastify';
5
+ import pino from 'pino';
7
6
  import http from 'node:http';
8
7
  import type http2 from 'node:http2';
9
8
  type ErrorHandler = (error: Error, req: FastifyRequest, res: FastifyReply) => Promise<void>;
9
+ /**
10
+ * Get the base URL for this API. Uses PUBLIC_URL if set, otherwise derives
11
+ * from the request's protocol and hostname.
12
+ */
13
+ export declare function apiBaseUrl(req: FastifyRequest): string;
14
+ /**
15
+ * Get the base URL for the UI that this API serves. Uses UI_URL if set,
16
+ * otherwise assumes the API lives at a subpath (e.g. /api) and the UI is
17
+ * one level up. Falls back to the API base URL if there's no parent path.
18
+ */
19
+ export declare function uiBaseUrl(req: FastifyRequest): string;
10
20
  export interface FastifyTxStateAuthInfo {
11
21
  /**
12
22
  * The primary identifier for the user that is making the request, after processing
@@ -27,6 +37,13 @@ export interface FastifyTxStateAuthInfo {
27
37
  * If all else fails, you can sha256 the session token with a salt.
28
38
  */
29
39
  sessionId: string;
40
+ /**
41
+ * The date that the session was created, if available. This is useful for considering
42
+ * tokens before a certain date as invalid. For instance, if you want a logout action
43
+ * to invalidate all tokens created until that point, you can compare this field against
44
+ * the last time they logged out.
45
+ */
46
+ sessionCreatedAt?: Date;
30
47
  /**
31
48
  * Some authentication systems allow administrators to impersonate regular users, so that
32
49
  * they can see what that user sees and troubleshoot issues. We still want to log the administrator
@@ -45,6 +62,39 @@ export interface FastifyTxStateAuthInfo {
45
62
  * this field can help log requests that are authenticated with the other application's token.
46
63
  */
47
64
  clientId?: string;
65
+ /**
66
+ * A string that designates the current session as one that has limited authorization. The application will
67
+ * be responsible for checking this field and restricting appropriately.
68
+ *
69
+ * For example, a user who authenticated via non-standard mechanism might be given a scope of 'altlogin' and
70
+ * only a portion of the application's functionality would be available to them.
71
+ */
72
+ scope?: string;
73
+ /**
74
+ * The token or key that was used to authenticate the request. This is useful for
75
+ * making sub-requests to other APIs that can authenticate with the same token.
76
+ */
77
+ token: string;
78
+ /**
79
+ * The issuer configuration for the token, if applicable. This helps you generate
80
+ * a proper logout url in multi-issuer environments.
81
+ */
82
+ issuerConfig?: IssuerConfig;
83
+ /**
84
+ * The OAuth access token, if available. This is useful when your API needs to make
85
+ * requests to the provider's APIs on behalf of the user (e.g. Google Drive, Microsoft
86
+ * Graph). Only populated when using cookie-based OAuth with a provider that returns
87
+ * an access token during the code exchange.
88
+ */
89
+ accessToken?: string;
90
+ }
91
+ export interface IssuerConfig {
92
+ iss: string;
93
+ url?: string;
94
+ publicKey?: string;
95
+ secret?: string;
96
+ validateUrl?: URL;
97
+ logoutUrl?: URL;
48
98
  }
49
99
  export interface FastifyTxStateOptions extends Partial<FastifyServerOptions> {
50
100
  https?: http2.SecureServerOptions;
@@ -82,39 +132,24 @@ export interface FastifyTxStateOptions extends Partial<FastifyServerOptions> {
82
132
  declare module 'fastify' {
83
133
  interface FastifyRequest {
84
134
  auth?: FastifyTxStateAuthInfo;
135
+ originChecker?: OriginChecker;
85
136
  }
86
137
  interface FastifyReply {
87
138
  extraLogInfo: any;
88
139
  }
89
140
  }
90
- export declare const devLogger: {
91
- level: string;
92
- info: (msg: any) => void;
93
- error: {
94
- (...data: any[]): void;
95
- (message?: any, ...optionalParams: any[]): void;
96
- };
97
- debug: {
98
- (...data: any[]): void;
99
- (message?: any, ...optionalParams: any[]): void;
100
- };
101
- fatal: {
102
- (...data: any[]): void;
103
- (message?: any, ...optionalParams: any[]): void;
104
- };
105
- warn: {
106
- (...data: any[]): void;
107
- (message?: any, ...optionalParams: any[]): void;
108
- };
109
- trace: {
110
- (...data: any[]): void;
111
- (message?: any, ...optionalParams: any[]): void;
112
- };
113
- silent: (msg: any) => void;
114
- child(bindings: any, options?: any): any;
115
- };
116
- export declare const prodLogger: FastifyLoggerOptions;
117
- export type FastifyInstanceTyped = FastifyInstance<RawServerDefault, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, FastifyBaseLogger, JsonSchemaToTsProvider>;
141
+ export declare const devLogger: pino.Logger<never, boolean>;
142
+ export declare const prodLogger: pino.Logger<never, boolean>;
143
+ export type FastifyInstanceTyped = FastifyInstance<RawServerDefault, http.IncomingMessage, http.ServerResponse, FastifyBaseLogger, JsonSchemaToTsProvider>;
144
+ export declare class OriginChecker {
145
+ protected validOrigins: Record<string, boolean>;
146
+ protected validOriginHosts: Record<string, boolean>;
147
+ protected validOriginSuffixes: Set<string>;
148
+ setValidOrigins(origins: string[]): void;
149
+ setValidOriginHosts(hosts: string[]): void;
150
+ setValidOriginSuffixes(suffixes: string[]): void;
151
+ check(hostname: string, requestHostname?: string): boolean;
152
+ }
118
153
  export type TxServer = Server;
119
154
  export default class Server {
120
155
  protected config: FastifyTxStateOptions & {
@@ -129,9 +164,8 @@ export default class Server {
129
164
  } | undefined>;
130
165
  protected shuttingDown: boolean;
131
166
  protected sigHandler: (signal: any) => void;
132
- protected validOrigins: Record<string, boolean>;
133
- protected validOriginHosts: Record<string, boolean>;
134
- protected validOriginSuffixes: Set<string>;
167
+ protected originChecker: OriginChecker;
168
+ protected swaggerEndpoint: string | undefined;
135
169
  app: FastifyInstanceTyped;
136
170
  constructor(config?: FastifyTxStateOptions & {
137
171
  http2?: true;
@@ -150,6 +184,4 @@ export default class Server {
150
184
  }): Promise<void>;
151
185
  close(softSeconds?: number): Promise<void>;
152
186
  }
153
- export * from './analytics';
154
- export * from './error';
155
- export * from './unified-auth';
187
+ export {};