@nexartis/sentinel-sdk 1.8.1 → 1.9.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/dist/core/client.d.ts +2 -1
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +18 -4
- package/dist/core/client.js.map +1 -1
- package/dist/core/errors.d.ts +25 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +49 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/sveltekit/handlers/index.d.ts.map +1 -1
- package/dist/sveltekit/handlers/index.js +22 -2
- package/dist/sveltekit/handlers/index.js.map +1 -1
- package/package.json +1 -1
package/dist/core/client.d.ts
CHANGED
|
@@ -11,8 +11,9 @@ export declare class SentinelClient {
|
|
|
11
11
|
private backoff;
|
|
12
12
|
startLogin(email: string, options?: {
|
|
13
13
|
redirectContext?: string;
|
|
14
|
+
rememberMe?: boolean;
|
|
14
15
|
}): Promise<StartLoginResponse>;
|
|
15
|
-
pollStatus(transactionId: string): Promise<PollResponse>;
|
|
16
|
+
pollStatus(transactionId: string, clientToken?: string): Promise<PollResponse>;
|
|
16
17
|
claimTokens(transactionId: string, clientToken: string): Promise<ClaimResponse>;
|
|
17
18
|
register(email: string, options?: {
|
|
18
19
|
redirectContext?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,4BAA4B,EAC5B,MAAM,YAAY,CAAC;AAOpB,qBAAa,cAAc;IAC1B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,GAAG,CAAiB;gBAEhB,MAAM,EAAE,cAAc,EAAE,OAAO,GAAE,OAAO,UAAU,CAAC,KAAa;YAQ9D,OAAO;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,4BAA4B,EAC5B,MAAM,YAAY,CAAC;AAOpB,qBAAa,cAAc;IAC1B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,GAAG,CAAiB;gBAEhB,MAAM,EAAE,cAAc,EAAE,OAAO,GAAE,OAAO,UAAU,CAAC,KAAa;YAQ9D,OAAO;IAkErB,2CAA2C;IAC3C,OAAO,CAAC,OAAO;IAKT,UAAU,CACf,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GAC1D,OAAO,CAAC,kBAAkB,CAAC;IAiBxB,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAW9E,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAO/E,QAAQ,CACb,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,GACpC,OAAO,CAAC,gBAAgB,CAAC;IActB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAOnD,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,4BAA4B,CAAC;IAO/E,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAO5D,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAS7D,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAQvE"}
|
package/dist/core/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SentinelTimeoutError, isRetryableError, isRetryableStatus } from './errors.js';
|
|
1
|
+
import { SentinelApiError, SentinelTimeoutError, isRetryableError, isRetryableStatus } from './errors.js';
|
|
2
2
|
import { createLogger } from './logger.js';
|
|
3
3
|
const DEFAULT_TIMEOUT_MS = 8_000;
|
|
4
4
|
const DEFAULT_MAX_RETRIES = 1;
|
|
@@ -40,6 +40,13 @@ export class SentinelClient {
|
|
|
40
40
|
await this.backoff(attempt);
|
|
41
41
|
continue;
|
|
42
42
|
}
|
|
43
|
+
// Fail loud on any remaining non-2xx status. A non-ok body is
|
|
44
|
+
// never returned as a resolved value; retryable statuses were
|
|
45
|
+
// handled above and are only surfaced here once retries are
|
|
46
|
+
// exhausted.
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw await SentinelApiError.fromResponse(response, url);
|
|
49
|
+
}
|
|
43
50
|
return await response.json();
|
|
44
51
|
}
|
|
45
52
|
catch (fetchError) {
|
|
@@ -77,16 +84,23 @@ export class SentinelClient {
|
|
|
77
84
|
if (options?.redirectContext !== undefined) {
|
|
78
85
|
body.redirect_context = options.redirectContext;
|
|
79
86
|
}
|
|
87
|
+
if (options?.rememberMe !== undefined) {
|
|
88
|
+
body.remember_me = options.rememberMe;
|
|
89
|
+
}
|
|
80
90
|
return this.request('/auth/login-passwordless', {
|
|
81
91
|
method: 'POST',
|
|
82
92
|
body: JSON.stringify(body)
|
|
83
93
|
});
|
|
84
94
|
}
|
|
85
|
-
async pollStatus(transactionId) {
|
|
86
|
-
|
|
95
|
+
async pollStatus(transactionId, clientToken) {
|
|
96
|
+
const options = {
|
|
87
97
|
method: 'POST',
|
|
88
98
|
body: JSON.stringify({ transactionId })
|
|
89
|
-
}
|
|
99
|
+
};
|
|
100
|
+
if (clientToken !== undefined) {
|
|
101
|
+
options.headers = { 'Authorization': `Bearer ${clientToken}` };
|
|
102
|
+
}
|
|
103
|
+
return this.request('/auth/poll', options);
|
|
90
104
|
}
|
|
91
105
|
async claimTokens(transactionId, clientToken) {
|
|
92
106
|
return this.request('/auth/claim', {
|
package/dist/core/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"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","sourcesContent":["// src/core/client.ts\nimport type {\n\tSentinelConfig,\n\tStartLoginResponse,\n\tPollResponse,\n\tClaimResponse,\n\tRefreshResponse,\n\tValidateResponse,\n\tCurrentUserResponse,\n\tRegisterResponse,\n\tVerifyResponse,\n\tCompletePasswordlessResponse\n} from './types.js';\nimport { SentinelTimeoutError, isRetryableError, isRetryableStatus } from './errors.js';\nimport { createLogger, type SentinelLogger } from './logger.js';\n\nconst DEFAULT_TIMEOUT_MS = 8_000;\nconst DEFAULT_MAX_RETRIES = 1;\n\nexport class SentinelClient {\n\tprivate config: SentinelConfig;\n\tprivate fetch: typeof globalThis.fetch;\n\tprivate timeoutMs: number;\n\tprivate maxRetries: number;\n\tprivate log: SentinelLogger;\n\n\tconstructor(config: SentinelConfig, fetchFn: typeof globalThis.fetch = fetch) {\n\t\tthis.config = config;\n\t\tthis.fetch = fetchFn;\n\t\tthis.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\t\tthis.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n\t\tthis.log = config.logger ?? createLogger('sentinel-client');\n\t}\n\n\tprivate async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n\t\tconst url = `${this.config.apiUrl}${endpoint}`;\n\t\tconst headers = {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t...options.headers\n\t\t};\n\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n\t\t\ttry {\n\t\t\t\t// Apply per-request timeout via AbortController\n\t\t\t\tconst controller = new AbortController();\n\t\t\t\tconst timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);\n\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await this.fetch(url, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\theaders,\n\t\t\t\t\t\tsignal: controller.signal\n\t\t\t\t\t});\n\n\t\t\t\t\tclearTimeout(timeoutId);\n\n\t\t\t\t\t// If the response status is retryable and we have retries left, retry\n\t\t\t\t\tif (isRetryableStatus(response.status) && attempt < this.maxRetries) {\n\t\t\t\t\t\tlastError = new Error(`HTTP ${response.status} from ${url}`);\n\t\t\t\t\t\tawait this.backoff(attempt);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn await response.json();\n\t\t\t\t} catch (fetchError) {\n\t\t\t\t\tclearTimeout(timeoutId);\n\n\t\t\t\t\t// Convert AbortError to SentinelTimeoutError\n\t\t\t\t\tif (fetchError instanceof DOMException && fetchError.name === 'AbortError') {\n\t\t\t\t\t\tthrow new SentinelTimeoutError(url, this.timeoutMs);\n\t\t\t\t\t}\n\t\t\t\t\tthrow fetchError;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error;\n\n\t\t\t\t// Only retry retryable errors\n\t\t\t\tif (attempt < this.maxRetries && isRetryableError(error)) {\n\t\t\t\t\tawait this.backoff(attempt);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\t// Should not reach here, but just in case\n\t\tthrow lastError;\n\t}\n\n\t/** Exponential backoff: 1s, 2s, 4s, ... */\n\tprivate backoff(attempt: number): Promise<void> {\n\t\tconst delay = Math.min(1000 * Math.pow(2, attempt), 8000);\n\t\treturn new Promise(resolve => setTimeout(resolve, delay));\n\t}\n\n\tasync startLogin(\n\t\temail: string,\n\t\toptions?: { redirectContext?: string }\n\t): Promise<StartLoginResponse> {\n\t\tconst body: Record<string, unknown> = {\n\t\t\temail,\n\t\t\temail_endpoint: this.config.baseUrl\n\t\t};\n\t\tif (options?.redirectContext !== undefined) {\n\t\t\tbody.redirect_context = options.redirectContext;\n\t\t}\n\t\treturn this.request<StartLoginResponse>('/auth/login-passwordless', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify(body)\n\t\t});\n\t}\n\n\tasync pollStatus(transactionId: string): Promise<PollResponse> {\n\t\treturn this.request<PollResponse>('/auth/poll', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ transactionId })\n\t\t});\n\t}\n\n\tasync claimTokens(transactionId: string, clientToken: string): Promise<ClaimResponse> {\n\t\treturn this.request<ClaimResponse>('/auth/claim', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ transactionId, clientToken })\n\t\t});\n\t}\n\n\tasync register(\n\t\temail: string,\n\t\toptions?: { redirectContext?: string }\n\t): Promise<RegisterResponse> {\n\t\tconst body: Record<string, unknown> = {\n\t\t\temail,\n\t\t\temail_endpoint: this.config.baseUrl\n\t\t};\n\t\tif (options?.redirectContext !== undefined) {\n\t\t\tbody.redirect_context = options.redirectContext;\n\t\t}\n\t\treturn this.request<RegisterResponse>('/auth/register', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify(body)\n\t\t});\n\t}\n\n\tasync verifyEmail(token: string): Promise<VerifyResponse> {\n\t\treturn this.request<VerifyResponse>('/auth/verify', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ verification_token: token })\n\t\t});\n\t}\n\n\tasync completePasswordless(magicToken: string): Promise<CompletePasswordlessResponse> {\n\t\treturn this.request<CompletePasswordlessResponse>('/auth/complete-passwordless', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ magic_token: magicToken })\n\t\t});\n\t}\n\n\tasync refreshToken(refreshToken: string): Promise<RefreshResponse> {\n\t\treturn this.request<RefreshResponse>('/auth/refresh', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ refresh_token: refreshToken })\n\t\t});\n\t}\n\n\tasync validateToken(accessToken: string): Promise<ValidateResponse> {\n\t\treturn this.request<ValidateResponse>('/auth/validate', {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Authorization': `Bearer ${accessToken}`\n\t\t\t}\n\t\t});\n\t}\n\n\tasync getCurrentUser(accessToken: string): Promise<CurrentUserResponse> {\n\t\treturn this.request<CurrentUserResponse>('/users/me', {\n\t\t\tmethod: 'GET',\n\t\t\theaders: {\n\t\t\t\t'Authorization': `Bearer ${accessToken}`\n\t\t\t}\n\t\t});\n\t}\n}\n\n"]}
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/core/client.ts"],"names":[],"mappings":"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","sourcesContent":["// src/core/client.ts\nimport type {\n\tSentinelConfig,\n\tStartLoginResponse,\n\tPollResponse,\n\tClaimResponse,\n\tRefreshResponse,\n\tValidateResponse,\n\tCurrentUserResponse,\n\tRegisterResponse,\n\tVerifyResponse,\n\tCompletePasswordlessResponse\n} from './types.js';\nimport { SentinelApiError, SentinelTimeoutError, isRetryableError, isRetryableStatus } from './errors.js';\nimport { createLogger, type SentinelLogger } from './logger.js';\n\nconst DEFAULT_TIMEOUT_MS = 8_000;\nconst DEFAULT_MAX_RETRIES = 1;\n\nexport class SentinelClient {\n\tprivate config: SentinelConfig;\n\tprivate fetch: typeof globalThis.fetch;\n\tprivate timeoutMs: number;\n\tprivate maxRetries: number;\n\tprivate log: SentinelLogger;\n\n\tconstructor(config: SentinelConfig, fetchFn: typeof globalThis.fetch = fetch) {\n\t\tthis.config = config;\n\t\tthis.fetch = fetchFn;\n\t\tthis.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\t\tthis.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;\n\t\tthis.log = config.logger ?? createLogger('sentinel-client');\n\t}\n\n\tprivate async request<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n\t\tconst url = `${this.config.apiUrl}${endpoint}`;\n\t\tconst headers = {\n\t\t\t'Content-Type': 'application/json',\n\t\t\t...options.headers\n\t\t};\n\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n\t\t\ttry {\n\t\t\t\t// Apply per-request timeout via AbortController\n\t\t\t\tconst controller = new AbortController();\n\t\t\t\tconst timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);\n\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await this.fetch(url, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\theaders,\n\t\t\t\t\t\tsignal: controller.signal\n\t\t\t\t\t});\n\n\t\t\t\t\tclearTimeout(timeoutId);\n\n\t\t\t\t\t// If the response status is retryable and we have retries left, retry\n\t\t\t\t\tif (isRetryableStatus(response.status) && attempt < this.maxRetries) {\n\t\t\t\t\t\tlastError = new Error(`HTTP ${response.status} from ${url}`);\n\t\t\t\t\t\tawait this.backoff(attempt);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Fail loud on any remaining non-2xx status. A non-ok body is\n\t\t\t\t\t// never returned as a resolved value; retryable statuses were\n\t\t\t\t\t// handled above and are only surfaced here once retries are\n\t\t\t\t\t// exhausted.\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow await SentinelApiError.fromResponse(response, url);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn await response.json();\n\t\t\t\t} catch (fetchError) {\n\t\t\t\t\tclearTimeout(timeoutId);\n\n\t\t\t\t\t// Convert AbortError to SentinelTimeoutError\n\t\t\t\t\tif (fetchError instanceof DOMException && fetchError.name === 'AbortError') {\n\t\t\t\t\t\tthrow new SentinelTimeoutError(url, this.timeoutMs);\n\t\t\t\t\t}\n\t\t\t\t\tthrow fetchError;\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error;\n\n\t\t\t\t// Only retry retryable errors\n\t\t\t\tif (attempt < this.maxRetries && isRetryableError(error)) {\n\t\t\t\t\tawait this.backoff(attempt);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\t// Should not reach here, but just in case\n\t\tthrow lastError;\n\t}\n\n\t/** Exponential backoff: 1s, 2s, 4s, ... */\n\tprivate backoff(attempt: number): Promise<void> {\n\t\tconst delay = Math.min(1000 * Math.pow(2, attempt), 8000);\n\t\treturn new Promise(resolve => setTimeout(resolve, delay));\n\t}\n\n\tasync startLogin(\n\t\temail: string,\n\t\toptions?: { redirectContext?: string; rememberMe?: boolean }\n\t): Promise<StartLoginResponse> {\n\t\tconst body: Record<string, unknown> = {\n\t\t\temail,\n\t\t\temail_endpoint: this.config.baseUrl\n\t\t};\n\t\tif (options?.redirectContext !== undefined) {\n\t\t\tbody.redirect_context = options.redirectContext;\n\t\t}\n\t\tif (options?.rememberMe !== undefined) {\n\t\t\tbody.remember_me = options.rememberMe;\n\t\t}\n\t\treturn this.request<StartLoginResponse>('/auth/login-passwordless', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify(body)\n\t\t});\n\t}\n\n\tasync pollStatus(transactionId: string, clientToken?: string): Promise<PollResponse> {\n\t\tconst options: RequestInit = {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ transactionId })\n\t\t};\n\t\tif (clientToken !== undefined) {\n\t\t\toptions.headers = { 'Authorization': `Bearer ${clientToken}` };\n\t\t}\n\t\treturn this.request<PollResponse>('/auth/poll', options);\n\t}\n\n\tasync claimTokens(transactionId: string, clientToken: string): Promise<ClaimResponse> {\n\t\treturn this.request<ClaimResponse>('/auth/claim', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ transactionId, clientToken })\n\t\t});\n\t}\n\n\tasync register(\n\t\temail: string,\n\t\toptions?: { redirectContext?: string }\n\t): Promise<RegisterResponse> {\n\t\tconst body: Record<string, unknown> = {\n\t\t\temail,\n\t\t\temail_endpoint: this.config.baseUrl\n\t\t};\n\t\tif (options?.redirectContext !== undefined) {\n\t\t\tbody.redirect_context = options.redirectContext;\n\t\t}\n\t\treturn this.request<RegisterResponse>('/auth/register', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify(body)\n\t\t});\n\t}\n\n\tasync verifyEmail(token: string): Promise<VerifyResponse> {\n\t\treturn this.request<VerifyResponse>('/auth/verify', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ verification_token: token })\n\t\t});\n\t}\n\n\tasync completePasswordless(magicToken: string): Promise<CompletePasswordlessResponse> {\n\t\treturn this.request<CompletePasswordlessResponse>('/auth/complete-passwordless', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ magic_token: magicToken })\n\t\t});\n\t}\n\n\tasync refreshToken(refreshToken: string): Promise<RefreshResponse> {\n\t\treturn this.request<RefreshResponse>('/auth/refresh', {\n\t\t\tmethod: 'POST',\n\t\t\tbody: JSON.stringify({ refresh_token: refreshToken })\n\t\t});\n\t}\n\n\tasync validateToken(accessToken: string): Promise<ValidateResponse> {\n\t\treturn this.request<ValidateResponse>('/auth/validate', {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Authorization': `Bearer ${accessToken}`\n\t\t\t}\n\t\t});\n\t}\n\n\tasync getCurrentUser(accessToken: string): Promise<CurrentUserResponse> {\n\t\treturn this.request<CurrentUserResponse>('/users/me', {\n\t\t\tmethod: 'GET',\n\t\t\theaders: {\n\t\t\t\t'Authorization': `Bearer ${accessToken}`\n\t\t\t}\n\t\t});\n\t}\n}\n\n"]}
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ export declare enum ErrorCode {
|
|
|
5
5
|
NETWORK_ERROR = "NETWORK_ERROR",
|
|
6
6
|
NO_REFRESH_TOKEN = "NO_REFRESH_TOKEN",
|
|
7
7
|
CLAIM_FAILED = "CLAIM_FAILED",
|
|
8
|
-
CONFIGURATION_ERROR = "CONFIGURATION_ERROR"
|
|
8
|
+
CONFIGURATION_ERROR = "CONFIGURATION_ERROR",
|
|
9
|
+
API_ERROR = "API_ERROR"
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* Sentinel backend numeric error codes.
|
|
@@ -32,6 +33,29 @@ export declare class SentinelError extends Error {
|
|
|
32
33
|
originalError?: unknown | undefined;
|
|
33
34
|
constructor(code: ErrorCode, message: string, originalError?: unknown | undefined);
|
|
34
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Thrown when the Sentinel API responds with a non-2xx status.
|
|
38
|
+
*
|
|
39
|
+
* Carries the HTTP `status` and the parsed response `body` (JSON when the
|
|
40
|
+
* response advertises JSON, otherwise the raw text, or `null` when the body
|
|
41
|
+
* is empty or unreadable) so callers can branch on the backend error code
|
|
42
|
+
* without re-reading the response.
|
|
43
|
+
*
|
|
44
|
+
* Fail-loud contract: a non-2xx response is never returned as a resolved
|
|
45
|
+
* value. Retryable statuses (502/503/504) are still retried first; this error
|
|
46
|
+
* is only thrown once retries are exhausted or the status is non-retryable.
|
|
47
|
+
*/
|
|
48
|
+
export declare class SentinelApiError extends SentinelError {
|
|
49
|
+
status: number;
|
|
50
|
+
body: unknown;
|
|
51
|
+
constructor(status: number, body: unknown, url: string);
|
|
52
|
+
/**
|
|
53
|
+
* Build a SentinelApiError from a non-ok Response, reading the body once.
|
|
54
|
+
* Falls back to raw text when the body is not JSON, and to null when the
|
|
55
|
+
* body cannot be read at all.
|
|
56
|
+
*/
|
|
57
|
+
static fromResponse(response: Response, url: string): Promise<SentinelApiError>;
|
|
58
|
+
}
|
|
35
59
|
/**
|
|
36
60
|
* Thrown when a fetch to the Sentinel API times out.
|
|
37
61
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAEA,oBAAY,SAAS;IACpB,cAAc,mBAAmB;IACjC,aAAa,kBAAkB;IAC/B,aAAa,kBAAkB;IAC/B,aAAa,kBAAkB;IAC/B,gBAAgB,qBAAqB;IACrC,YAAY,iBAAiB;IAC7B,mBAAmB,wBAAwB;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAEA,oBAAY,SAAS;IACpB,cAAc,mBAAmB;IACjC,aAAa,kBAAkB;IAC/B,aAAa,kBAAkB;IAC/B,aAAa,kBAAkB;IAC/B,gBAAgB,qBAAqB;IACrC,YAAY,iBAAiB;IAC7B,mBAAmB,wBAAwB;IAC3C,SAAS,cAAc;CACvB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;CAWf,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAEjF,qBAAa,aAAc,SAAQ,KAAK;IAE/B,IAAI,EAAE,SAAS;IAEf,aAAa,CAAC,EAAE,OAAO;gBAFvB,IAAI,EAAE,SAAS,EACtB,OAAO,EAAE,MAAM,EACR,aAAa,CAAC,EAAE,OAAO,YAAA;CAK/B;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,gBAAiB,SAAQ,aAAa;IAE1C,MAAM,EAAE,MAAM;IACd,IAAI,EAAE,OAAO;gBADb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACpB,GAAG,EAAE,MAAM;IAUZ;;;;OAIG;WACU,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAgBrF;AAED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,aAAa;gBAC1C,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAO1C;AAED,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,aAA2B,CAAC;AAEhE,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQxD;AAED,iDAAiD;AACjD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD"}
|
package/dist/core/errors.js
CHANGED
|
@@ -8,6 +8,7 @@ export var ErrorCode;
|
|
|
8
8
|
ErrorCode["NO_REFRESH_TOKEN"] = "NO_REFRESH_TOKEN";
|
|
9
9
|
ErrorCode["CLAIM_FAILED"] = "CLAIM_FAILED";
|
|
10
10
|
ErrorCode["CONFIGURATION_ERROR"] = "CONFIGURATION_ERROR";
|
|
11
|
+
ErrorCode["API_ERROR"] = "API_ERROR";
|
|
11
12
|
})(ErrorCode || (ErrorCode = {}));
|
|
12
13
|
/**
|
|
13
14
|
* Sentinel backend numeric error codes.
|
|
@@ -38,6 +39,54 @@ export class SentinelError extends Error {
|
|
|
38
39
|
this.name = 'SentinelError';
|
|
39
40
|
}
|
|
40
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Thrown when the Sentinel API responds with a non-2xx status.
|
|
44
|
+
*
|
|
45
|
+
* Carries the HTTP `status` and the parsed response `body` (JSON when the
|
|
46
|
+
* response advertises JSON, otherwise the raw text, or `null` when the body
|
|
47
|
+
* is empty or unreadable) so callers can branch on the backend error code
|
|
48
|
+
* without re-reading the response.
|
|
49
|
+
*
|
|
50
|
+
* Fail-loud contract: a non-2xx response is never returned as a resolved
|
|
51
|
+
* value. Retryable statuses (502/503/504) are still retried first; this error
|
|
52
|
+
* is only thrown once retries are exhausted or the status is non-retryable.
|
|
53
|
+
*/
|
|
54
|
+
export class SentinelApiError extends SentinelError {
|
|
55
|
+
status;
|
|
56
|
+
body;
|
|
57
|
+
constructor(status, body, url) {
|
|
58
|
+
const detail = body && typeof body === 'object' && 'message' in body
|
|
59
|
+
? `: ${String(body.message)}`
|
|
60
|
+
: '';
|
|
61
|
+
super(ErrorCode.API_ERROR, `Sentinel API request to ${url} failed with HTTP ${status}${detail}`);
|
|
62
|
+
this.status = status;
|
|
63
|
+
this.body = body;
|
|
64
|
+
this.name = 'SentinelApiError';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Build a SentinelApiError from a non-ok Response, reading the body once.
|
|
68
|
+
* Falls back to raw text when the body is not JSON, and to null when the
|
|
69
|
+
* body cannot be read at all.
|
|
70
|
+
*/
|
|
71
|
+
static async fromResponse(response, url) {
|
|
72
|
+
let body = null;
|
|
73
|
+
try {
|
|
74
|
+
const text = await response.text();
|
|
75
|
+
if (text.length > 0) {
|
|
76
|
+
try {
|
|
77
|
+
body = JSON.parse(text);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
body = text;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
body = null;
|
|
86
|
+
}
|
|
87
|
+
return new SentinelApiError(response.status, body, url);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
41
90
|
/**
|
|
42
91
|
* Thrown when a fetch to the Sentinel API times out.
|
|
43
92
|
*/
|
package/dist/core/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAqB;AAErB,MAAM,CAAN,IAAY,
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"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","sourcesContent":["// src/core/errors.ts\n\nexport enum ErrorCode {\n\tUSER_NOT_FOUND = 'USER_NOT_FOUND',\n\tTOKEN_EXPIRED = 'TOKEN_EXPIRED',\n\tINVALID_TOKEN = 'INVALID_TOKEN',\n\tNETWORK_ERROR = 'NETWORK_ERROR',\n\tNO_REFRESH_TOKEN = 'NO_REFRESH_TOKEN',\n\tCLAIM_FAILED = 'CLAIM_FAILED',\n\tCONFIGURATION_ERROR = 'CONFIGURATION_ERROR',\n\tAPI_ERROR = 'API_ERROR'\n}\n\n/**\n * Sentinel backend numeric error codes.\n * These are emitted in the `code` field of Sentinel API responses.\n * Use these constants instead of magic numbers when checking result codes.\n *\n * Source: Sentinel backend `ErrorCode` enum (Hono / Cloudflare Worker).\n */\nexport const SentinelCode = {\n\tSUCCESS: 0,\n\tVALIDATION_ERROR: 1001,\n\tACCOUNT_NOT_VERIFIED: 2001,\n\tACCOUNT_DISABLED: 2002,\n\tINVALID_TOKEN: 2003,\n\tEXPIRED_TOKEN: 2004,\n\tTOKEN_REUSE_DETECTED: 2005, // One-time token already consumed (e.g., email client prefetch)\n\tTOKEN_REVOKED: 2006,\n\tACCOUNT_NOT_EXIST: 2009,\n\tINVALID_TRANSACTION: 2011\n} as const;\n\nexport type SentinelCodeValue = (typeof SentinelCode)[keyof typeof SentinelCode];\n\nexport class SentinelError extends Error {\n\tconstructor(\n\t\tpublic code: ErrorCode,\n\t\tmessage: string,\n\t\tpublic originalError?: unknown\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'SentinelError';\n\t}\n}\n\n/**\n * Thrown when the Sentinel API responds with a non-2xx status.\n *\n * Carries the HTTP `status` and the parsed response `body` (JSON when the\n * response advertises JSON, otherwise the raw text, or `null` when the body\n * is empty or unreadable) so callers can branch on the backend error code\n * without re-reading the response.\n *\n * Fail-loud contract: a non-2xx response is never returned as a resolved\n * value. Retryable statuses (502/503/504) are still retried first; this error\n * is only thrown once retries are exhausted or the status is non-retryable.\n */\nexport class SentinelApiError extends SentinelError {\n\tconstructor(\n\t\tpublic status: number,\n\t\tpublic body: unknown,\n\t\turl: string\n\t) {\n\t\tconst detail =\n\t\t\tbody && typeof body === 'object' && 'message' in body\n\t\t\t\t? `: ${String((body as Record<string, unknown>).message)}`\n\t\t\t\t: '';\n\t\tsuper(ErrorCode.API_ERROR, `Sentinel API request to ${url} failed with HTTP ${status}${detail}`);\n\t\tthis.name = 'SentinelApiError';\n\t}\n\n\t/**\n\t * Build a SentinelApiError from a non-ok Response, reading the body once.\n\t * Falls back to raw text when the body is not JSON, and to null when the\n\t * body cannot be read at all.\n\t */\n\tstatic async fromResponse(response: Response, url: string): Promise<SentinelApiError> {\n\t\tlet body: unknown = null;\n\t\ttry {\n\t\t\tconst text = await response.text();\n\t\t\tif (text.length > 0) {\n\t\t\t\ttry {\n\t\t\t\t\tbody = JSON.parse(text);\n\t\t\t\t} catch {\n\t\t\t\t\tbody = text;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\tbody = null;\n\t\t}\n\t\treturn new SentinelApiError(response.status, body, url);\n\t}\n}\n\n/**\n * Thrown when a fetch to the Sentinel API times out.\n */\nexport class SentinelTimeoutError extends SentinelError {\n\tconstructor(url: string, timeoutMs: number) {\n\t\tsuper(\n\t\t\tErrorCode.NETWORK_ERROR,\n\t\t\t`Sentinel API request to ${url} timed out after ${timeoutMs}ms`\n\t\t);\n\t\tthis.name = 'SentinelTimeoutError';\n\t}\n}\n\n/** HTTP status codes that are safe to retry (transient server errors). */\nexport const RETRYABLE_HTTP_STATUSES = new Set([502, 503, 504]);\n\n/** Check whether an error is retryable (network failure or transient HTTP error). */\nexport function isRetryableError(error: unknown): boolean {\n\t// Timeout errors are retryable\n\tif (error instanceof SentinelTimeoutError) return true;\n\t// Network errors (Failed to fetch, etc.)\n\tif (error instanceof TypeError) return true;\n\t// DOMException from AbortController\n\tif (error instanceof DOMException && error.name === 'AbortError') return true;\n\treturn false;\n}\n\n/** Check whether an HTTP status is retryable. */\nexport function isRetryableStatus(status: number): boolean {\n\treturn RETRYABLE_HTTP_STATUSES.has(status);\n}\n\n"]}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { SentinelClient } from './client.js';
|
|
2
|
-
export { SentinelError, SentinelTimeoutError, ErrorCode, SentinelCode, isRetryableError, isRetryableStatus } from './errors.js';
|
|
2
|
+
export { SentinelError, SentinelApiError, SentinelTimeoutError, ErrorCode, SentinelCode, isRetryableError, isRetryableStatus } from './errors.js';
|
|
3
3
|
export type { SentinelCodeValue } from './errors.js';
|
|
4
4
|
export { validateToken, isTokenExpired, decodeTokenLocally } from './auth-utils.js';
|
|
5
5
|
export { createJwksVerifier, __resetJwksCacheForTests } from './jwks.js';
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAClJ,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACzE,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACvF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACvD,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACtE,YAAY,EACX,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,4BAA4B,EAC5B,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,sBAAsB,EACtB,qBAAqB,EACrB,MAAM,YAAY,CAAC"}
|
package/dist/core/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/core/index.ts
|
|
2
2
|
export { SentinelClient } from './client.js';
|
|
3
|
-
export { SentinelError, SentinelTimeoutError, ErrorCode, SentinelCode, isRetryableError, isRetryableStatus } from './errors.js';
|
|
3
|
+
export { SentinelError, SentinelApiError, SentinelTimeoutError, ErrorCode, SentinelCode, isRetryableError, isRetryableStatus } from './errors.js';
|
|
4
4
|
export { validateToken, isTokenExpired, decodeTokenLocally } from './auth-utils.js';
|
|
5
5
|
export { createJwksVerifier, __resetJwksCacheForTests } from './jwks.js';
|
|
6
6
|
export { createLogger, noopLogger } from './logger.js';
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAElJ,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AAEzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC","sourcesContent":["// src/core/index.ts\nexport { SentinelClient } from './client.js';\nexport { SentinelError, SentinelApiError, SentinelTimeoutError, ErrorCode, SentinelCode, isRetryableError, isRetryableStatus } from './errors.js';\nexport type { SentinelCodeValue } from './errors.js';\nexport { validateToken, isTokenExpired, decodeTokenLocally } from './auth-utils.js';\nexport { createJwksVerifier, __resetJwksCacheForTests } from './jwks.js';\nexport type { JwksVerifier, JwksVerifierOptions, TokenVerifyOutcome } from './jwks.js';\nexport { createLogger, noopLogger } from './logger.js';\nexport type { SentinelLogger, LogLevel, LogEntry } from './logger.js';\nexport type {\n\tSentinelConfig,\n\tSentinelResponse,\n\tSentinelResponseBase,\n\tStartLoginResponse,\n\tPollResponse,\n\tClaimResponse,\n\tRefreshResponse,\n\tValidateResponse,\n\tRegisterResponse,\n\tVerifyResponse,\n\tCompletePasswordlessResponse,\n\tDecodedToken,\n\tUserLocals,\n\tSentinelHandleOptions,\n\tAuthHandlerOptions,\n\tBeforeLoginContext,\n\tBeforeLoginReject,\n\tAfterLoginContext,\n\tAfterClaimContext,\n\tErrorContext,\n\tResolveRedirectContext,\n\tLogoutRedirectContext\n} from './types.js';\n\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sveltekit/handlers/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sveltekit/handlers/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAgB,MAAM,eAAe,CAAC;AAGlE,OAAO,KAAK,EACX,kBAAkB,EAGlB,MAAM,qBAAqB,CAAC;AAqjB7B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,kBAAuB,GAAG;IAAE,GAAG,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,cAAc,CAAA;CAAE,CA8ClH"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/sveltekit/handlers/index.ts
|
|
2
2
|
import { json, redirect } from '@sveltejs/kit';
|
|
3
3
|
import { SentinelClient } from '../../core/client.js';
|
|
4
|
+
import { SentinelApiError } from '../../core/errors.js';
|
|
4
5
|
import { decodeTokenLocally } from '../../core/auth-utils.js';
|
|
5
6
|
import { createLogger } from '../../core/logger.js';
|
|
6
7
|
import { setSessionCookies, clearAuthCookies, COOKIE_NAMES, COOKIE_OPTIONS } from '../cookies.js';
|
|
@@ -113,7 +114,14 @@ async function handleStartLogin(event, resolved) {
|
|
|
113
114
|
}
|
|
114
115
|
const config = getConfigFromPlatform(event.platform);
|
|
115
116
|
const sentinel = new SentinelClient(config, event.fetch);
|
|
116
|
-
const
|
|
117
|
+
const startLoginOpts = {};
|
|
118
|
+
if (typeof redirectContext === 'string') {
|
|
119
|
+
startLoginOpts.redirectContext = redirectContext;
|
|
120
|
+
}
|
|
121
|
+
if (typeof rememberMe === 'boolean') {
|
|
122
|
+
startLoginOpts.rememberMe = rememberMe;
|
|
123
|
+
}
|
|
124
|
+
const result = await sentinel.startLogin(trimmedEmail, startLoginOpts);
|
|
117
125
|
// cookiesSet requires both a success signal AND valid tokens to prevent
|
|
118
126
|
// setting undefined/null cookie values if the API response is malformed.
|
|
119
127
|
const cookiesSet = Boolean((result.code === 0 || result.success) &&
|
|
@@ -154,6 +162,17 @@ async function handleStartLogin(event, resolved) {
|
|
|
154
162
|
if (opts.onError)
|
|
155
163
|
await opts.onError(event, { handler: 'start-login', error });
|
|
156
164
|
clearLoginCookies(event.cookies);
|
|
165
|
+
// Fail loud on configuration / client errors so misconfigured origins or
|
|
166
|
+
// invalid requests are surfaced immediately instead of masking as success.
|
|
167
|
+
if (error instanceof SentinelApiError && error.status === 400) {
|
|
168
|
+
const body = error.body;
|
|
169
|
+
if (body && typeof body === 'object' && (body.code === 4003 || body.code === 'EMAIL_ENDPOINT_UNAPPROVED')) {
|
|
170
|
+
return json({
|
|
171
|
+
success: false,
|
|
172
|
+
error: body.message || 'Email endpoint host is not approved'
|
|
173
|
+
}, { status: 400 });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
157
176
|
return json({
|
|
158
177
|
success: true,
|
|
159
178
|
message: 'If this email is registered, you will receive a magic link shortly.',
|
|
@@ -166,12 +185,13 @@ async function handlePoll(event, resolved) {
|
|
|
166
185
|
const { loginCookieOptions, log, opts } = resolved;
|
|
167
186
|
try {
|
|
168
187
|
const transactionId = event.cookies.get('login_transaction_id');
|
|
188
|
+
const clientToken = event.cookies.get('login_client_token');
|
|
169
189
|
if (!transactionId) {
|
|
170
190
|
return json({ status: 'expired' }, { status: 400 });
|
|
171
191
|
}
|
|
172
192
|
const config = getConfigFromPlatform(event.platform);
|
|
173
193
|
const sentinel = new SentinelClient(config, event.fetch);
|
|
174
|
-
const result = await sentinel.pollStatus(transactionId);
|
|
194
|
+
const result = await sentinel.pollStatus(transactionId, clientToken);
|
|
175
195
|
if (result.code === 0 || result.success) {
|
|
176
196
|
if (result.status === 'completed') {
|
|
177
197
|
event.cookies.set('claim_transaction_id', transactionId, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sveltekit/handlers/index.ts"],"names":[],"mappings":"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","sourcesContent":["// src/sveltekit/handlers/index.ts\nimport { json, redirect } from '@sveltejs/kit';\nimport type { RequestHandler, RequestEvent } from '@sveltejs/kit';\nimport { SentinelClient } from '../../core/client.js';\nimport type {\n\tAuthHandlerOptions,\n\tDecodedToken,\n\tUserLocals\n} from '../../core/types.js';\nimport { decodeTokenLocally } from '../../core/auth-utils.js';\nimport { createLogger, type SentinelLogger } from '../../core/logger.js';\nimport { setSessionCookies, clearAuthCookies, COOKIE_NAMES, COOKIE_OPTIONS } from '../cookies.js';\nimport { getConfigFromPlatform, clearLoginCookies } from '../utils.js';\n\nconst DEFAULT_LOGIN_COOKIE_TTL_SECONDS = 600; // 10 minutes\nconst DEFAULT_LOGIN_REDIRECT = '/auth';\nconst MAX_EMAIL_LENGTH = 254; // RFC 5321\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/** Resolved options with defaults applied. Computed once in createAuthHandlers. */\ninterface ResolvedOptions {\n\tloginCookieOptions: ReturnType<typeof buildLoginCookieOptions>;\n\tloginRedirect: string;\n\tredirectCookie: boolean;\n\tlog: SentinelLogger;\n\topts: AuthHandlerOptions;\n}\n\n/**\n * True if `path` is a safe relative path: starts with `/`, is not `//x` or\n * `/\\x` (protocol-relative), carries no scheme prefix (http:, javascript:, data:),\n * and contains no whitespace or control characters (defence-in-depth against\n * header-injection via CR/LF/tab smuggled through a `Location` header).\n */\nfunction isSafeRelativePath(path: string): boolean {\n\tif (typeof path !== 'string' || path.length === 0) return false;\n\tif (!path.startsWith('/')) return false;\n\tif (/^[/\\\\]{2}/.test(path)) return false;\n\tif (/^\\/[a-z][a-z0-9+.\\-]*:/i.test(path)) return false;\n\t// Reject any ASCII control char (incl. CR/LF/TAB) or whitespace.\n\tif (/[\\s\\u0000-\\u001f\\u007f]/.test(path)) return false;\n\treturn true;\n}\n\n/** Sanitise a redirect path to prevent open-redirect via env misconfiguration. */\nfunction safeRedirectPath(raw: string | undefined): string {\n\tconst path = (raw || '/').trim();\n\tif (!isSafeRelativePath(path)) return '/';\n\treturn path;\n}\n\n/** Build UserLocals from a freshly-issued access token, or null if the token won't decode. */\nfunction userLocalsFromAccessToken(accessToken: string): UserLocals | null {\n\tconst decoded: DecodedToken | null = decodeTokenLocally(accessToken);\n\tif (!decoded) return null;\n\treturn {\n\t\tisAuthenticated: true,\n\t\tid: decoded.sub,\n\t\temail: decoded.email,\n\t\troles: decoded.roles || []\n\t};\n}\n\nfunction buildLoginCookieOptions(ttlSeconds: number) {\n\treturn {\n\t\tpath: '/',\n\t\thttpOnly: true,\n\t\tsecure: true,\n\t\tsameSite: 'lax' as const,\n\t\tmaxAge: ttlSeconds\n\t};\n}\n\n/** Validate email: present, non-empty string, max 254 chars, basic format. */\nfunction validateEmail(email: unknown): string | null {\n\tif (!email || typeof email !== 'string') return 'Email is required';\n\tconst trimmed = email.trim();\n\tif (!trimmed) return 'Email is required';\n\tif (trimmed.length > MAX_EMAIL_LENGTH) return 'Email exceeds maximum length';\n\tif (!EMAIL_REGEX.test(trimmed)) return 'Invalid email format';\n\treturn null; // valid\n}\n\n// Start Login Handler\nasync function handleStartLogin(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { loginCookieOptions, log, opts } = resolved;\n\ttry {\n\t\tconst body = await event.request.json();\n\t\tconst { email, rememberMe, redirectTo, redirectContext } = body;\n\n\t\t// Built-in basic email validation\n\t\tconst emailError = validateEmail(email);\n\t\tif (emailError) {\n\t\t\treturn json({ success: false, error: emailError }, { status: 400 });\n\t\t}\n\t\tconst trimmedEmail = (email as string).trim();\n\n\t\t// onBeforeLogin hook — custom validation, rate limiting, etc.\n\t\tif (opts.onBeforeLogin) {\n\t\t\tconst rejection = await opts.onBeforeLogin(event, {\n\t\t\t\temail: trimmedEmail,\n\t\t\t\trememberMe: !!rememberMe,\n\t\t\t\tredirectTo\n\t\t\t});\n\t\t\tif (rejection) {\n\t\t\t\t// 1.5.0: if the rejection carries a safe relative `redirect`, 303\n\t\t\t\t// to that branded destination instead of returning a JSON error.\n\t\t\t\t// 303 (See Other) converts the POST into a GET on the client.\n\t\t\t\tif (rejection.redirect && isSafeRelativePath(rejection.redirect)) {\n\t\t\t\t\tclearLoginCookies(event.cookies);\n\t\t\t\t\treturn new Response(null, {\n\t\t\t\t\t\tstatus: 303,\n\t\t\t\t\t\theaders: { Location: rejection.redirect }\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (rejection.redirect) {\n\t\t\t\t\tlog.warn('onBeforeLogin returned unsafe redirect — falling back to JSON', {\n\t\t\t\t\t\tredirect: rejection.redirect\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Clear stale login cookies on JSON reject so a rejected login never\n\t\t\t\t// leaves a half-open transaction behind. Mirrors the 303 path above.\n\t\t\t\tclearLoginCookies(event.cookies);\n\t\t\t\treturn json({ success: false, error: rejection.error }, { status: rejection.status ?? 400 });\n\t\t\t}\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.startLogin(\n\t\t\ttrimmedEmail,\n\t\t\ttypeof redirectContext === 'string' ? { redirectContext } : undefined\n\t\t);\n\n\t\t// cookiesSet requires both a success signal AND valid tokens to prevent\n\t\t// setting undefined/null cookie values if the API response is malformed.\n\t\tconst cookiesSet = Boolean(\n\t\t\t(result.code === 0 || result.success) &&\n\t\t\tresult.transactionId &&\n\t\t\tresult.clientToken\n\t\t);\n\n\t\tif (cookiesSet) {\n\t\t\tevent.cookies.set('login_transaction_id', result.transactionId!, loginCookieOptions);\n\t\t\tevent.cookies.set('login_client_token', result.clientToken!, loginCookieOptions);\n\t\t\tevent.cookies.set('login_remember_me', rememberMe ? 'true' : 'false', loginCookieOptions);\n\n\t\t\t// Built-in redirectTo cookie — stores where to send user after login\n\t\t\tif (resolved.redirectCookie && redirectTo) {\n\t\t\t\tconst safeTarget = safeRedirectPath(redirectTo);\n\t\t\t\tif (safeTarget !== '/') {\n\t\t\t\t\tevent.cookies.set('login_redirect_to', safeTarget, loginCookieOptions);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// onAfterLogin hook — audit logging, analytics, etc.\n\t\t\tif (opts.onAfterLogin) {\n\t\t\t\tawait opts.onAfterLogin(event, {\n\t\t\t\t\temail: trimmedEmail,\n\t\t\t\t\ttransactionId: result.transactionId!\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\tlog.error('Start login issue', { code: result.code, error: result.error });\n\t\t\tclearLoginCookies(event.cookies);\n\t\t}\n\n\t\t// Always return same message to prevent user enumeration.\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: 'If this email is registered, you will receive a magic link shortly.',\n\t\t\tpollReady: cookiesSet\n\t\t});\n\t} catch (error) {\n\t\tlog.error('Start login error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'start-login', error });\n\t\tclearLoginCookies(event.cookies);\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: 'If this email is registered, you will receive a magic link shortly.',\n\t\t\tpollReady: false\n\t\t});\n\t}\n}\n\n// Poll Handler\nasync function handlePoll(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { loginCookieOptions, log, opts } = resolved;\n\ttry {\n\t\tconst transactionId = event.cookies.get('login_transaction_id');\n\n\t\tif (!transactionId) {\n\t\t\treturn json({ status: 'expired' }, { status: 400 });\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.pollStatus(transactionId);\n\n\t\tif (result.code === 0 || result.success) {\n\t\t\tif (result.status === 'completed') {\n\t\t\t\tevent.cookies.set('claim_transaction_id', transactionId, {\n\t\t\t\t\t...loginCookieOptions,\n\t\t\t\t\tmaxAge: 60 // 1 minute\n\t\t\t\t});\n\n\t\t\t\treturn json({\n\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\tredirectTo: '/api/auth/claim',\n\t\t\t\t\t...(result.redirect_context != null\n\t\t\t\t\t\t? { redirect_context: result.redirect_context }\n\t\t\t\t\t\t: {})\n\t\t\t\t});\n\t\t\t} else if (result.status === 'pending') {\n\t\t\t\treturn json({ status: 'pending' });\n\t\t\t} else {\n\t\t\t\treturn json({ status: 'expired' });\n\t\t\t}\n\t\t} else {\n\t\t\treturn json({ status: 'error' }, { status: 400 });\n\t\t}\n\t} catch (error) {\n\t\tlog.error('Poll status error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'poll', error });\n\t\treturn json({ status: 'error' }, { status: 500 });\n\t}\n}\n\n/**\n * Compute the built-in redirect candidate (before `resolveRedirect` runs).\n * Order: login_redirect_to cookie → SENTINEL_POST_AUTH_REDIRECT env → '/'.\n *\n * Separated from `finalizePostClaimRedirect` so the `ResolveRedirectContext`\n * can surface both the `requestedRedirect` and the `defaultRedirect` cleanly.\n */\nfunction computeBuiltinClaimRedirect(\n\tevent: RequestEvent,\n\tresolved: ResolvedOptions\n): { requested?: string; fallback: string } {\n\tlet requested: string | undefined;\n\tif (resolved.redirectCookie) {\n\t\tconst redirectTo = event.cookies.get('login_redirect_to');\n\t\tif (redirectTo) {\n\t\t\t// Clear it — single use, regardless of which redirect path wins.\n\t\t\tevent.cookies.delete('login_redirect_to', { path: '/' });\n\t\t\trequested = safeRedirectPath(redirectTo);\n\t\t}\n\t}\n\tconst envRedirect = (event.platform as Record<string, unknown>)?.env as\n\t\t| Record<string, string>\n\t\t| undefined;\n\tconst fallback = envRedirect?.SENTINEL_POST_AUTH_REDIRECT\n\t\t? safeRedirectPath(envRedirect.SENTINEL_POST_AUTH_REDIRECT)\n\t\t: '/';\n\treturn { requested, fallback };\n}\n\n/**\n * Finalise the post-claim destination:\n * 1. Compute built-in requested + fallback candidates.\n * 2. If `opts.resolveRedirect` is configured, call it; use the returned path\n * when it is a safe relative path. Throws / empty / unsafe → fallback chain.\n * 3. Otherwise prefer `requested` then `fallback`.\n */\nasync function finalizePostClaimRedirect(\n\tevent: RequestEvent,\n\tresolved: ResolvedOptions,\n\taccessToken: string,\n\trememberMe: boolean\n): Promise<string> {\n\tconst { requested, fallback } = computeBuiltinClaimRedirect(event, resolved);\n\tconst defaultRedirect = requested || fallback;\n\n\tif (!resolved.opts.resolveRedirect) {\n\t\treturn defaultRedirect;\n\t}\n\n\tconst user = userLocalsFromAccessToken(accessToken);\n\tif (!user) {\n\t\tresolved.log.warn('resolveRedirect skipped — access token failed to decode');\n\t\treturn defaultRedirect;\n\t}\n\n\ttry {\n\t\tconst candidate = await resolved.opts.resolveRedirect(event, {\n\t\t\tuser,\n\t\t\taccessToken,\n\t\t\trememberMe,\n\t\t\trequestedRedirect: requested,\n\t\t\tdefaultRedirect\n\t\t});\n\t\tif (typeof candidate === 'string' && isSafeRelativePath(candidate)) {\n\t\t\treturn candidate;\n\t\t}\n\t\tresolved.log.warn('resolveRedirect returned invalid path — using default', {\n\t\t\tcandidate: typeof candidate === 'string' ? candidate : typeof candidate\n\t\t});\n\t} catch (err) {\n\t\tresolved.log.error('resolveRedirect threw — using default', { error: String(err) });\n\t}\n\treturn defaultRedirect;\n}\n\n// Claim Handler (POST)\nasync function handleClaimPost(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { log, opts } = resolved;\n\ttry {\n\t\tconst transactionId = event.cookies.get('claim_transaction_id');\n\t\tconst clientToken = event.cookies.get('login_client_token');\n\t\tconst rememberMe = event.cookies.get('login_remember_me') === 'true';\n\n\t\tif (!transactionId) {\n\t\t\treturn json({ success: false, error: 'No transaction ID' }, { status: 400 });\n\t\t}\n\t\tif (!clientToken) {\n\t\t\treturn json({ success: false, error: 'No client token' }, { status: 400 });\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.claimTokens(transactionId, clientToken);\n\n\t\tif ((result.code === 0 || result.success) && result.access_token && result.refresh_token) {\n\t\t\tconst postAuthRedirect = await finalizePostClaimRedirect(\n\t\t\t\tevent,\n\t\t\t\tresolved,\n\t\t\t\tresult.access_token,\n\t\t\t\trememberMe\n\t\t\t);\n\t\t\tsetSessionCookies(event.cookies, result.access_token, result.refresh_token, rememberMe);\n\t\t\tclearLoginCookies(event.cookies);\n\n\t\t\t// onAfterClaim hook — user creation, profile sync, analytics\n\t\t\tif (opts.onAfterClaim) {\n\t\t\t\tawait opts.onAfterClaim(event, {\n\t\t\t\t\taccessToken: result.access_token,\n\t\t\t\t\trefreshToken: result.refresh_token,\n\t\t\t\t\tredirectTo: postAuthRedirect\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn json({ success: true, redirectTo: postAuthRedirect });\n\t\t} else {\n\t\t\treturn json({ success: false, error: result.error || 'Failed to claim tokens' }, { status: 400 });\n\t\t}\n\t} catch (error) {\n\t\tlog.error('Claim tokens error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'claim', error });\n\t\treturn json({ success: false, error: 'Internal server error' }, { status: 500 });\n\t}\n}\n\n// Claim Handler (GET)\nasync function handleClaimGet(event: RequestEvent, resolved: ResolvedOptions): Promise<never> {\n\tconst { loginRedirect, opts } = resolved;\n\tconst transactionId = event.cookies.get('claim_transaction_id');\n\tconst clientToken = event.cookies.get('login_client_token');\n\tconst rememberMe = event.cookies.get('login_remember_me') === 'true';\n\n\tif (!transactionId || !clientToken) {\n\t\tthrow redirect(302, `${loginRedirect}?error=no_transaction`);\n\t}\n\n\tconst config = getConfigFromPlatform(event.platform);\n\tconst sentinel = new SentinelClient(config, event.fetch);\n\tconst result = await sentinel.claimTokens(transactionId, clientToken);\n\n\tif ((result.code === 0 || result.success) && result.access_token && result.refresh_token) {\n\t\tconst postAuthRedirect = await finalizePostClaimRedirect(\n\t\t\tevent,\n\t\t\tresolved,\n\t\t\tresult.access_token,\n\t\t\trememberMe\n\t\t);\n\t\tsetSessionCookies(event.cookies, result.access_token, result.refresh_token, rememberMe);\n\t\tclearLoginCookies(event.cookies);\n\n\t\tif (opts.onAfterClaim) {\n\t\t\tawait opts.onAfterClaim(event, {\n\t\t\t\taccessToken: result.access_token,\n\t\t\t\trefreshToken: result.refresh_token,\n\t\t\t\tredirectTo: postAuthRedirect\n\t\t\t});\n\t\t}\n\n\t\tthrow redirect(303, postAuthRedirect);\n\t}\n\n\tthrow redirect(302, `${loginRedirect}?error=claim_failed`);\n}\n\n/**\n * Resolve the post-logout destination.\n *\n * - `logoutRedirect` absent → fall back to `loginRedirect` (preserves 1.4.x behaviour).\n * - `logoutRedirect` string → validate; unsafe → fall back to `loginRedirect`.\n * - `logoutRedirect` function → await; validate; throw/empty/unsafe → fall back to `loginRedirect`.\n */\nasync function resolveLogoutDestination(\n\tevent: RequestEvent,\n\tresolved: ResolvedOptions\n): Promise<string> {\n\tconst fallback = resolved.loginRedirect;\n\tconst opt = resolved.opts.logoutRedirect;\n\tif (opt === undefined) return fallback;\n\n\tif (typeof opt === 'string') {\n\t\treturn isSafeRelativePath(opt) ? opt : fallback;\n\t}\n\n\tif (typeof opt === 'function') {\n\t\t// Best-effort user context: decode the still-present access token before\n\t\t// cookies get cleared. Callers typing user as possibly null lines up with\n\t\t// the reality that the token may be missing or malformed.\n\t\tconst accessToken = event.cookies.get(COOKIE_NAMES.ACCESS_TOKEN);\n\t\tconst user = accessToken ? userLocalsFromAccessToken(accessToken) : null;\n\t\ttry {\n\t\t\tconst candidate = await opt(event, { user });\n\t\t\tif (typeof candidate === 'string' && isSafeRelativePath(candidate)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t\tresolved.log.warn('logoutRedirect returned invalid path — using fallback', {\n\t\t\t\tcandidate: typeof candidate === 'string' ? candidate : typeof candidate\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tresolved.log.error('logoutRedirect threw — using fallback', { error: String(err) });\n\t\t}\n\t}\n\treturn fallback;\n}\n\n// Logout Handler\nasync function handleLogout(event: RequestEvent, resolved: ResolvedOptions): Promise<never> {\n\tconst destination = await resolveLogoutDestination(event, resolved);\n\tclearAuthCookies(event.cookies);\n\tthrow redirect(303, destination);\n}\n\n// Refresh Handler\nasync function handleRefresh(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { log, opts } = resolved;\n\ttry {\n\t\tconst refreshToken = event.cookies.get(COOKIE_NAMES.REFRESH_TOKEN);\n\n\t\tif (!refreshToken) {\n\t\t\treturn json({ success: false, error: 'No refresh token' }, { status: 401 });\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.refreshToken(refreshToken);\n\n\t\tif (result.code === 0 || result.success) {\n\t\t\tif (result.access_token) {\n\t\t\t\tevent.cookies.set(COOKIE_NAMES.ACCESS_TOKEN, result.access_token, COOKIE_OPTIONS.ACCESS_TOKEN);\n\n\t\t\t\tif (result.refresh_token) {\n\t\t\t\t\tevent.cookies.set(COOKIE_NAMES.REFRESH_TOKEN, result.refresh_token, COOKIE_OPTIONS.REFRESH_TOKEN_LONG);\n\t\t\t\t}\n\n\t\t\t\treturn json({ success: true });\n\t\t\t} else {\n\t\t\t\treturn json({ success: false, error: 'No access token returned' }, { status: 400 });\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warn('Token refresh failed - returning error without clearing cookies');\n\t\t\treturn json({ success: false, error: 'Refresh token expired' }, { status: 401 });\n\t\t}\n\t} catch (error) {\n\t\tlog.error('Refresh token error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'refresh', error });\n\t\treturn json({ success: false, error: 'Internal server error' }, { status: 500 });\n\t}\n}\n\n// Register Handler\nasync function handleRegister(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { log, opts } = resolved;\n\ttry {\n\t\tlet email: string;\n\t\tlet redirectContext: string | undefined;\n\t\tconst contentType = event.request.headers.get('content-type') || '';\n\n\t\tif (contentType.includes('application/json')) {\n\t\t\tconst body = await event.request.json();\n\t\t\temail = body.email;\n\t\t\tif (typeof body.redirectContext === 'string') redirectContext = body.redirectContext;\n\t\t} else if (contentType.includes('application/x-www-form-urlencoded') || contentType.includes('multipart/form-data')) {\n\t\t\tconst formData = await event.request.formData();\n\t\t\temail = formData.get('email') as string;\n\t\t\tconst rc = formData.get('redirectContext');\n\t\t\tif (typeof rc === 'string') redirectContext = rc;\n\t\t} else {\n\t\t\tconst body = await event.request.json();\n\t\t\temail = body.email;\n\t\t\tif (typeof body.redirectContext === 'string') redirectContext = body.redirectContext;\n\t\t}\n\n\t\t// Use shared email validation\n\t\tconst emailError = validateEmail(email);\n\t\tif (emailError) {\n\t\t\treturn json({ success: false, error: emailError }, { status: 400 });\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.register(\n\t\t\t(email as string).trim(),\n\t\t\tredirectContext !== undefined ? { redirectContext } : undefined\n\t\t);\n\n\t\tif (result.code !== 0 && !result.success) {\n\t\t\tlog.error('Registration issue', { code: result.code, error: result.error });\n\t\t}\n\n\t\t// Always return same message to prevent user enumeration\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: 'If this email is not already registered, you will receive a verification link shortly.'\n\t\t});\n\t} catch (error) {\n\t\tlog.error('Registration error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'register', error });\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: 'If this email is not already registered, you will receive a verification link shortly.'\n\t\t});\n\t}\n}\n\n// Verify Email Handler\nasync function handleVerify(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { log, opts } = resolved;\n\ttry {\n\t\tconst { token } = await event.request.json();\n\n\t\tif (!token) {\n\t\t\treturn json({ success: false, error: 'Verification token is required' }, { status: 400 });\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.verifyEmail(token);\n\n\t\treturn json(result);\n\t} catch (error) {\n\t\tlog.error('Verify email error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'verify', error });\n\t\treturn json({ success: false, error: 'Internal server error' }, { status: 500 });\n\t}\n}\n\n/**\n * Creates all auth API route handlers for catch-all route pattern.\n *\n * @param options - Optional configuration for auth handlers.\n *\n * Usage:\n * ```typescript\n * // src/routes/api/auth/[...path]/+server.ts\n * import { createAuthHandlers } from '@nexartis/sentinel-sdk/sveltekit/handlers';\n * export const { GET, POST } = createAuthHandlers({\n * loginCookieTtlSeconds: 1800,\n * loginRedirect: '/auth',\n * redirectCookie: true,\n * onBeforeLogin: async (event, ctx) => {\n * // custom rate limiting, validation, etc.\n * },\n * onAfterClaim: async (event, ctx) => {\n * // create user record, sync profile, etc.\n * }\n * });\n * ```\n */\nexport function createAuthHandlers(options: AuthHandlerOptions = {}): { GET: RequestHandler; POST: RequestHandler } {\n\tconst resolved: ResolvedOptions = {\n\t\tloginCookieOptions: buildLoginCookieOptions(options.loginCookieTtlSeconds ?? DEFAULT_LOGIN_COOKIE_TTL_SECONDS),\n\t\tloginRedirect: options.loginRedirect ?? DEFAULT_LOGIN_REDIRECT,\n\t\tredirectCookie: options.redirectCookie ?? true,\n\t\tlog: options.logger ?? createLogger('sentinel-handlers'),\n\t\topts: options\n\t};\n\n\tconst POST: RequestHandler = async (event) => {\n\t\tconst path = event.params.path || '';\n\n\t\tswitch (path) {\n\t\t\tcase 'start-login':\n\t\t\t\treturn handleStartLogin(event, resolved);\n\t\t\tcase 'poll':\n\t\t\t\treturn handlePoll(event, resolved);\n\t\t\tcase 'claim':\n\t\t\t\treturn handleClaimPost(event, resolved);\n\t\t\tcase 'logout':\n\t\t\t\treturn handleLogout(event, resolved);\n\t\t\tcase 'refresh':\n\t\t\t\treturn handleRefresh(event, resolved);\n\t\t\tcase 'register':\n\t\t\t\treturn handleRegister(event, resolved);\n\t\t\tcase 'verify':\n\t\t\t\treturn handleVerify(event, resolved);\n\t\t\tdefault:\n\t\t\t\treturn json({ error: 'Not found' }, { status: 404 });\n\t\t}\n\t};\n\n\tconst GET: RequestHandler = async (event) => {\n\t\tconst path = event.params.path || '';\n\n\t\tswitch (path) {\n\t\t\tcase 'claim':\n\t\t\t\treturn handleClaimGet(event, resolved);\n\t\t\tcase 'logout':\n\t\t\t\treturn handleLogout(event, resolved);\n\t\t\tdefault:\n\t\t\t\treturn json({ error: 'Not found' }, { status: 404 });\n\t\t}\n\t};\n\n\treturn { GET, POST };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sveltekit/handlers/index.ts"],"names":[],"mappings":"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","sourcesContent":["// src/sveltekit/handlers/index.ts\nimport { json, redirect } from '@sveltejs/kit';\nimport type { RequestHandler, RequestEvent } from '@sveltejs/kit';\nimport { SentinelClient } from '../../core/client.js';\nimport { SentinelApiError } from '../../core/errors.js';\nimport type {\n\tAuthHandlerOptions,\n\tDecodedToken,\n\tUserLocals\n} from '../../core/types.js';\nimport { decodeTokenLocally } from '../../core/auth-utils.js';\nimport { createLogger, type SentinelLogger } from '../../core/logger.js';\nimport { setSessionCookies, clearAuthCookies, COOKIE_NAMES, COOKIE_OPTIONS } from '../cookies.js';\nimport { getConfigFromPlatform, clearLoginCookies } from '../utils.js';\n\nconst DEFAULT_LOGIN_COOKIE_TTL_SECONDS = 600; // 10 minutes\nconst DEFAULT_LOGIN_REDIRECT = '/auth';\nconst MAX_EMAIL_LENGTH = 254; // RFC 5321\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/** Resolved options with defaults applied. Computed once in createAuthHandlers. */\ninterface ResolvedOptions {\n\tloginCookieOptions: ReturnType<typeof buildLoginCookieOptions>;\n\tloginRedirect: string;\n\tredirectCookie: boolean;\n\tlog: SentinelLogger;\n\topts: AuthHandlerOptions;\n}\n\n/**\n * True if `path` is a safe relative path: starts with `/`, is not `//x` or\n * `/\\x` (protocol-relative), carries no scheme prefix (http:, javascript:, data:),\n * and contains no whitespace or control characters (defence-in-depth against\n * header-injection via CR/LF/tab smuggled through a `Location` header).\n */\nfunction isSafeRelativePath(path: string): boolean {\n\tif (typeof path !== 'string' || path.length === 0) return false;\n\tif (!path.startsWith('/')) return false;\n\tif (/^[/\\\\]{2}/.test(path)) return false;\n\tif (/^\\/[a-z][a-z0-9+.\\-]*:/i.test(path)) return false;\n\t// Reject any ASCII control char (incl. CR/LF/TAB) or whitespace.\n\tif (/[\\s\\u0000-\\u001f\\u007f]/.test(path)) return false;\n\treturn true;\n}\n\n/** Sanitise a redirect path to prevent open-redirect via env misconfiguration. */\nfunction safeRedirectPath(raw: string | undefined): string {\n\tconst path = (raw || '/').trim();\n\tif (!isSafeRelativePath(path)) return '/';\n\treturn path;\n}\n\n/** Build UserLocals from a freshly-issued access token, or null if the token won't decode. */\nfunction userLocalsFromAccessToken(accessToken: string): UserLocals | null {\n\tconst decoded: DecodedToken | null = decodeTokenLocally(accessToken);\n\tif (!decoded) return null;\n\treturn {\n\t\tisAuthenticated: true,\n\t\tid: decoded.sub,\n\t\temail: decoded.email,\n\t\troles: decoded.roles || []\n\t};\n}\n\nfunction buildLoginCookieOptions(ttlSeconds: number) {\n\treturn {\n\t\tpath: '/',\n\t\thttpOnly: true,\n\t\tsecure: true,\n\t\tsameSite: 'lax' as const,\n\t\tmaxAge: ttlSeconds\n\t};\n}\n\n/** Validate email: present, non-empty string, max 254 chars, basic format. */\nfunction validateEmail(email: unknown): string | null {\n\tif (!email || typeof email !== 'string') return 'Email is required';\n\tconst trimmed = email.trim();\n\tif (!trimmed) return 'Email is required';\n\tif (trimmed.length > MAX_EMAIL_LENGTH) return 'Email exceeds maximum length';\n\tif (!EMAIL_REGEX.test(trimmed)) return 'Invalid email format';\n\treturn null; // valid\n}\n\n// Start Login Handler\nasync function handleStartLogin(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { loginCookieOptions, log, opts } = resolved;\n\ttry {\n\t\tconst body = await event.request.json();\n\t\tconst { email, rememberMe, redirectTo, redirectContext } = body;\n\n\t\t// Built-in basic email validation\n\t\tconst emailError = validateEmail(email);\n\t\tif (emailError) {\n\t\t\treturn json({ success: false, error: emailError }, { status: 400 });\n\t\t}\n\t\tconst trimmedEmail = (email as string).trim();\n\n\t\t// onBeforeLogin hook — custom validation, rate limiting, etc.\n\t\tif (opts.onBeforeLogin) {\n\t\t\tconst rejection = await opts.onBeforeLogin(event, {\n\t\t\t\temail: trimmedEmail,\n\t\t\t\trememberMe: !!rememberMe,\n\t\t\t\tredirectTo\n\t\t\t});\n\t\t\tif (rejection) {\n\t\t\t\t// 1.5.0: if the rejection carries a safe relative `redirect`, 303\n\t\t\t\t// to that branded destination instead of returning a JSON error.\n\t\t\t\t// 303 (See Other) converts the POST into a GET on the client.\n\t\t\t\tif (rejection.redirect && isSafeRelativePath(rejection.redirect)) {\n\t\t\t\t\tclearLoginCookies(event.cookies);\n\t\t\t\t\treturn new Response(null, {\n\t\t\t\t\t\tstatus: 303,\n\t\t\t\t\t\theaders: { Location: rejection.redirect }\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (rejection.redirect) {\n\t\t\t\t\tlog.warn('onBeforeLogin returned unsafe redirect — falling back to JSON', {\n\t\t\t\t\t\tredirect: rejection.redirect\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Clear stale login cookies on JSON reject so a rejected login never\n\t\t\t\t// leaves a half-open transaction behind. Mirrors the 303 path above.\n\t\t\t\tclearLoginCookies(event.cookies);\n\t\t\t\treturn json({ success: false, error: rejection.error }, { status: rejection.status ?? 400 });\n\t\t\t}\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst startLoginOpts: { redirectContext?: string; rememberMe?: boolean } = {};\n\t\tif (typeof redirectContext === 'string') {\n\t\t\tstartLoginOpts.redirectContext = redirectContext;\n\t\t}\n\t\tif (typeof rememberMe === 'boolean') {\n\t\t\tstartLoginOpts.rememberMe = rememberMe;\n\t\t}\n\t\tconst result = await sentinel.startLogin(trimmedEmail, startLoginOpts);\n\n\t\t// cookiesSet requires both a success signal AND valid tokens to prevent\n\t\t// setting undefined/null cookie values if the API response is malformed.\n\t\tconst cookiesSet = Boolean(\n\t\t\t(result.code === 0 || result.success) &&\n\t\t\tresult.transactionId &&\n\t\t\tresult.clientToken\n\t\t);\n\n\t\tif (cookiesSet) {\n\t\t\tevent.cookies.set('login_transaction_id', result.transactionId!, loginCookieOptions);\n\t\t\tevent.cookies.set('login_client_token', result.clientToken!, loginCookieOptions);\n\t\t\tevent.cookies.set('login_remember_me', rememberMe ? 'true' : 'false', loginCookieOptions);\n\n\t\t\t// Built-in redirectTo cookie — stores where to send user after login\n\t\t\tif (resolved.redirectCookie && redirectTo) {\n\t\t\t\tconst safeTarget = safeRedirectPath(redirectTo);\n\t\t\t\tif (safeTarget !== '/') {\n\t\t\t\t\tevent.cookies.set('login_redirect_to', safeTarget, loginCookieOptions);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// onAfterLogin hook — audit logging, analytics, etc.\n\t\t\tif (opts.onAfterLogin) {\n\t\t\t\tawait opts.onAfterLogin(event, {\n\t\t\t\t\temail: trimmedEmail,\n\t\t\t\t\ttransactionId: result.transactionId!\n\t\t\t\t});\n\t\t\t}\n\t\t} else {\n\t\t\tlog.error('Start login issue', { code: result.code, error: result.error });\n\t\t\tclearLoginCookies(event.cookies);\n\t\t}\n\n\t\t// Always return same message to prevent user enumeration.\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: 'If this email is registered, you will receive a magic link shortly.',\n\t\t\tpollReady: cookiesSet\n\t\t});\n\t} catch (error) {\n\t\tlog.error('Start login error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'start-login', error });\n\t\tclearLoginCookies(event.cookies);\n\n\t\t// Fail loud on configuration / client errors so misconfigured origins or\n\t\t// invalid requests are surfaced immediately instead of masking as success.\n\t\tif (error instanceof SentinelApiError && error.status === 400) {\n\t\t\tconst body = error.body as Record<string, any> | undefined;\n\t\t\tif (body && typeof body === 'object' && (body.code === 4003 || body.code === 'EMAIL_ENDPOINT_UNAPPROVED')) {\n\t\t\t\treturn json(\n\t\t\t\t\t{\n\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\terror: body.message || 'Email endpoint host is not approved'\n\t\t\t\t\t},\n\t\t\t\t\t{ status: 400 }\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: 'If this email is registered, you will receive a magic link shortly.',\n\t\t\tpollReady: false\n\t\t});\n\t}\n}\n\n// Poll Handler\nasync function handlePoll(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { loginCookieOptions, log, opts } = resolved;\n\ttry {\n\t\tconst transactionId = event.cookies.get('login_transaction_id');\n\t\tconst clientToken = event.cookies.get('login_client_token');\n\n\t\tif (!transactionId) {\n\t\t\treturn json({ status: 'expired' }, { status: 400 });\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.pollStatus(transactionId, clientToken);\n\n\t\tif (result.code === 0 || result.success) {\n\t\t\tif (result.status === 'completed') {\n\t\t\t\tevent.cookies.set('claim_transaction_id', transactionId, {\n\t\t\t\t\t...loginCookieOptions,\n\t\t\t\t\tmaxAge: 60 // 1 minute\n\t\t\t\t});\n\n\t\t\t\treturn json({\n\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\tredirectTo: '/api/auth/claim',\n\t\t\t\t\t...(result.redirect_context != null\n\t\t\t\t\t\t? { redirect_context: result.redirect_context }\n\t\t\t\t\t\t: {})\n\t\t\t\t});\n\t\t\t} else if (result.status === 'pending') {\n\t\t\t\treturn json({ status: 'pending' });\n\t\t\t} else {\n\t\t\t\treturn json({ status: 'expired' });\n\t\t\t}\n\t\t} else {\n\t\t\treturn json({ status: 'error' }, { status: 400 });\n\t\t}\n\t} catch (error) {\n\t\tlog.error('Poll status error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'poll', error });\n\t\treturn json({ status: 'error' }, { status: 500 });\n\t}\n}\n\n/**\n * Compute the built-in redirect candidate (before `resolveRedirect` runs).\n * Order: login_redirect_to cookie → SENTINEL_POST_AUTH_REDIRECT env → '/'.\n *\n * Separated from `finalizePostClaimRedirect` so the `ResolveRedirectContext`\n * can surface both the `requestedRedirect` and the `defaultRedirect` cleanly.\n */\nfunction computeBuiltinClaimRedirect(\n\tevent: RequestEvent,\n\tresolved: ResolvedOptions\n): { requested?: string; fallback: string } {\n\tlet requested: string | undefined;\n\tif (resolved.redirectCookie) {\n\t\tconst redirectTo = event.cookies.get('login_redirect_to');\n\t\tif (redirectTo) {\n\t\t\t// Clear it — single use, regardless of which redirect path wins.\n\t\t\tevent.cookies.delete('login_redirect_to', { path: '/' });\n\t\t\trequested = safeRedirectPath(redirectTo);\n\t\t}\n\t}\n\tconst envRedirect = (event.platform as Record<string, unknown>)?.env as\n\t\t| Record<string, string>\n\t\t| undefined;\n\tconst fallback = envRedirect?.SENTINEL_POST_AUTH_REDIRECT\n\t\t? safeRedirectPath(envRedirect.SENTINEL_POST_AUTH_REDIRECT)\n\t\t: '/';\n\treturn { requested, fallback };\n}\n\n/**\n * Finalise the post-claim destination:\n * 1. Compute built-in requested + fallback candidates.\n * 2. If `opts.resolveRedirect` is configured, call it; use the returned path\n * when it is a safe relative path. Throws / empty / unsafe → fallback chain.\n * 3. Otherwise prefer `requested` then `fallback`.\n */\nasync function finalizePostClaimRedirect(\n\tevent: RequestEvent,\n\tresolved: ResolvedOptions,\n\taccessToken: string,\n\trememberMe: boolean\n): Promise<string> {\n\tconst { requested, fallback } = computeBuiltinClaimRedirect(event, resolved);\n\tconst defaultRedirect = requested || fallback;\n\n\tif (!resolved.opts.resolveRedirect) {\n\t\treturn defaultRedirect;\n\t}\n\n\tconst user = userLocalsFromAccessToken(accessToken);\n\tif (!user) {\n\t\tresolved.log.warn('resolveRedirect skipped — access token failed to decode');\n\t\treturn defaultRedirect;\n\t}\n\n\ttry {\n\t\tconst candidate = await resolved.opts.resolveRedirect(event, {\n\t\t\tuser,\n\t\t\taccessToken,\n\t\t\trememberMe,\n\t\t\trequestedRedirect: requested,\n\t\t\tdefaultRedirect\n\t\t});\n\t\tif (typeof candidate === 'string' && isSafeRelativePath(candidate)) {\n\t\t\treturn candidate;\n\t\t}\n\t\tresolved.log.warn('resolveRedirect returned invalid path — using default', {\n\t\t\tcandidate: typeof candidate === 'string' ? candidate : typeof candidate\n\t\t});\n\t} catch (err) {\n\t\tresolved.log.error('resolveRedirect threw — using default', { error: String(err) });\n\t}\n\treturn defaultRedirect;\n}\n\n// Claim Handler (POST)\nasync function handleClaimPost(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { log, opts } = resolved;\n\ttry {\n\t\tconst transactionId = event.cookies.get('claim_transaction_id');\n\t\tconst clientToken = event.cookies.get('login_client_token');\n\t\tconst rememberMe = event.cookies.get('login_remember_me') === 'true';\n\n\t\tif (!transactionId) {\n\t\t\treturn json({ success: false, error: 'No transaction ID' }, { status: 400 });\n\t\t}\n\t\tif (!clientToken) {\n\t\t\treturn json({ success: false, error: 'No client token' }, { status: 400 });\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.claimTokens(transactionId, clientToken);\n\n\t\tif ((result.code === 0 || result.success) && result.access_token && result.refresh_token) {\n\t\t\tconst postAuthRedirect = await finalizePostClaimRedirect(\n\t\t\t\tevent,\n\t\t\t\tresolved,\n\t\t\t\tresult.access_token,\n\t\t\t\trememberMe\n\t\t\t);\n\t\t\tsetSessionCookies(event.cookies, result.access_token, result.refresh_token, rememberMe);\n\t\t\tclearLoginCookies(event.cookies);\n\n\t\t\t// onAfterClaim hook — user creation, profile sync, analytics\n\t\t\tif (opts.onAfterClaim) {\n\t\t\t\tawait opts.onAfterClaim(event, {\n\t\t\t\t\taccessToken: result.access_token,\n\t\t\t\t\trefreshToken: result.refresh_token,\n\t\t\t\t\tredirectTo: postAuthRedirect\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn json({ success: true, redirectTo: postAuthRedirect });\n\t\t} else {\n\t\t\treturn json({ success: false, error: result.error || 'Failed to claim tokens' }, { status: 400 });\n\t\t}\n\t} catch (error) {\n\t\tlog.error('Claim tokens error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'claim', error });\n\t\treturn json({ success: false, error: 'Internal server error' }, { status: 500 });\n\t}\n}\n\n// Claim Handler (GET)\nasync function handleClaimGet(event: RequestEvent, resolved: ResolvedOptions): Promise<never> {\n\tconst { loginRedirect, opts } = resolved;\n\tconst transactionId = event.cookies.get('claim_transaction_id');\n\tconst clientToken = event.cookies.get('login_client_token');\n\tconst rememberMe = event.cookies.get('login_remember_me') === 'true';\n\n\tif (!transactionId || !clientToken) {\n\t\tthrow redirect(302, `${loginRedirect}?error=no_transaction`);\n\t}\n\n\tconst config = getConfigFromPlatform(event.platform);\n\tconst sentinel = new SentinelClient(config, event.fetch);\n\tconst result = await sentinel.claimTokens(transactionId, clientToken);\n\n\tif ((result.code === 0 || result.success) && result.access_token && result.refresh_token) {\n\t\tconst postAuthRedirect = await finalizePostClaimRedirect(\n\t\t\tevent,\n\t\t\tresolved,\n\t\t\tresult.access_token,\n\t\t\trememberMe\n\t\t);\n\t\tsetSessionCookies(event.cookies, result.access_token, result.refresh_token, rememberMe);\n\t\tclearLoginCookies(event.cookies);\n\n\t\tif (opts.onAfterClaim) {\n\t\t\tawait opts.onAfterClaim(event, {\n\t\t\t\taccessToken: result.access_token,\n\t\t\t\trefreshToken: result.refresh_token,\n\t\t\t\tredirectTo: postAuthRedirect\n\t\t\t});\n\t\t}\n\n\t\tthrow redirect(303, postAuthRedirect);\n\t}\n\n\tthrow redirect(302, `${loginRedirect}?error=claim_failed`);\n}\n\n/**\n * Resolve the post-logout destination.\n *\n * - `logoutRedirect` absent → fall back to `loginRedirect` (preserves 1.4.x behaviour).\n * - `logoutRedirect` string → validate; unsafe → fall back to `loginRedirect`.\n * - `logoutRedirect` function → await; validate; throw/empty/unsafe → fall back to `loginRedirect`.\n */\nasync function resolveLogoutDestination(\n\tevent: RequestEvent,\n\tresolved: ResolvedOptions\n): Promise<string> {\n\tconst fallback = resolved.loginRedirect;\n\tconst opt = resolved.opts.logoutRedirect;\n\tif (opt === undefined) return fallback;\n\n\tif (typeof opt === 'string') {\n\t\treturn isSafeRelativePath(opt) ? opt : fallback;\n\t}\n\n\tif (typeof opt === 'function') {\n\t\t// Best-effort user context: decode the still-present access token before\n\t\t// cookies get cleared. Callers typing user as possibly null lines up with\n\t\t// the reality that the token may be missing or malformed.\n\t\tconst accessToken = event.cookies.get(COOKIE_NAMES.ACCESS_TOKEN);\n\t\tconst user = accessToken ? userLocalsFromAccessToken(accessToken) : null;\n\t\ttry {\n\t\t\tconst candidate = await opt(event, { user });\n\t\t\tif (typeof candidate === 'string' && isSafeRelativePath(candidate)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t\tresolved.log.warn('logoutRedirect returned invalid path — using fallback', {\n\t\t\t\tcandidate: typeof candidate === 'string' ? candidate : typeof candidate\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tresolved.log.error('logoutRedirect threw — using fallback', { error: String(err) });\n\t\t}\n\t}\n\treturn fallback;\n}\n\n// Logout Handler\nasync function handleLogout(event: RequestEvent, resolved: ResolvedOptions): Promise<never> {\n\tconst destination = await resolveLogoutDestination(event, resolved);\n\tclearAuthCookies(event.cookies);\n\tthrow redirect(303, destination);\n}\n\n// Refresh Handler\nasync function handleRefresh(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { log, opts } = resolved;\n\ttry {\n\t\tconst refreshToken = event.cookies.get(COOKIE_NAMES.REFRESH_TOKEN);\n\n\t\tif (!refreshToken) {\n\t\t\treturn json({ success: false, error: 'No refresh token' }, { status: 401 });\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.refreshToken(refreshToken);\n\n\t\tif (result.code === 0 || result.success) {\n\t\t\tif (result.access_token) {\n\t\t\t\tevent.cookies.set(COOKIE_NAMES.ACCESS_TOKEN, result.access_token, COOKIE_OPTIONS.ACCESS_TOKEN);\n\n\t\t\t\tif (result.refresh_token) {\n\t\t\t\t\tevent.cookies.set(COOKIE_NAMES.REFRESH_TOKEN, result.refresh_token, COOKIE_OPTIONS.REFRESH_TOKEN_LONG);\n\t\t\t\t}\n\n\t\t\t\treturn json({ success: true });\n\t\t\t} else {\n\t\t\t\treturn json({ success: false, error: 'No access token returned' }, { status: 400 });\n\t\t\t}\n\t\t} else {\n\t\t\tlog.warn('Token refresh failed - returning error without clearing cookies');\n\t\t\treturn json({ success: false, error: 'Refresh token expired' }, { status: 401 });\n\t\t}\n\t} catch (error) {\n\t\tlog.error('Refresh token error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'refresh', error });\n\t\treturn json({ success: false, error: 'Internal server error' }, { status: 500 });\n\t}\n}\n\n// Register Handler\nasync function handleRegister(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { log, opts } = resolved;\n\ttry {\n\t\tlet email: string;\n\t\tlet redirectContext: string | undefined;\n\t\tconst contentType = event.request.headers.get('content-type') || '';\n\n\t\tif (contentType.includes('application/json')) {\n\t\t\tconst body = await event.request.json();\n\t\t\temail = body.email;\n\t\t\tif (typeof body.redirectContext === 'string') redirectContext = body.redirectContext;\n\t\t} else if (contentType.includes('application/x-www-form-urlencoded') || contentType.includes('multipart/form-data')) {\n\t\t\tconst formData = await event.request.formData();\n\t\t\temail = formData.get('email') as string;\n\t\t\tconst rc = formData.get('redirectContext');\n\t\t\tif (typeof rc === 'string') redirectContext = rc;\n\t\t} else {\n\t\t\tconst body = await event.request.json();\n\t\t\temail = body.email;\n\t\t\tif (typeof body.redirectContext === 'string') redirectContext = body.redirectContext;\n\t\t}\n\n\t\t// Use shared email validation\n\t\tconst emailError = validateEmail(email);\n\t\tif (emailError) {\n\t\t\treturn json({ success: false, error: emailError }, { status: 400 });\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.register(\n\t\t\t(email as string).trim(),\n\t\t\tredirectContext !== undefined ? { redirectContext } : undefined\n\t\t);\n\n\t\tif (result.code !== 0 && !result.success) {\n\t\t\tlog.error('Registration issue', { code: result.code, error: result.error });\n\t\t}\n\n\t\t// Always return same message to prevent user enumeration\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: 'If this email is not already registered, you will receive a verification link shortly.'\n\t\t});\n\t} catch (error) {\n\t\tlog.error('Registration error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'register', error });\n\t\treturn json({\n\t\t\tsuccess: true,\n\t\t\tmessage: 'If this email is not already registered, you will receive a verification link shortly.'\n\t\t});\n\t}\n}\n\n// Verify Email Handler\nasync function handleVerify(event: RequestEvent, resolved: ResolvedOptions): Promise<Response> {\n\tconst { log, opts } = resolved;\n\ttry {\n\t\tconst { token } = await event.request.json();\n\n\t\tif (!token) {\n\t\t\treturn json({ success: false, error: 'Verification token is required' }, { status: 400 });\n\t\t}\n\n\t\tconst config = getConfigFromPlatform(event.platform);\n\t\tconst sentinel = new SentinelClient(config, event.fetch);\n\t\tconst result = await sentinel.verifyEmail(token);\n\n\t\treturn json(result);\n\t} catch (error) {\n\t\tlog.error('Verify email error', { error: String(error) });\n\t\tif (opts.onError) await opts.onError(event, { handler: 'verify', error });\n\t\treturn json({ success: false, error: 'Internal server error' }, { status: 500 });\n\t}\n}\n\n/**\n * Creates all auth API route handlers for catch-all route pattern.\n *\n * @param options - Optional configuration for auth handlers.\n *\n * Usage:\n * ```typescript\n * // src/routes/api/auth/[...path]/+server.ts\n * import { createAuthHandlers } from '@nexartis/sentinel-sdk/sveltekit/handlers';\n * export const { GET, POST } = createAuthHandlers({\n * loginCookieTtlSeconds: 1800,\n * loginRedirect: '/auth',\n * redirectCookie: true,\n * onBeforeLogin: async (event, ctx) => {\n * // custom rate limiting, validation, etc.\n * },\n * onAfterClaim: async (event, ctx) => {\n * // create user record, sync profile, etc.\n * }\n * });\n * ```\n */\nexport function createAuthHandlers(options: AuthHandlerOptions = {}): { GET: RequestHandler; POST: RequestHandler } {\n\tconst resolved: ResolvedOptions = {\n\t\tloginCookieOptions: buildLoginCookieOptions(options.loginCookieTtlSeconds ?? DEFAULT_LOGIN_COOKIE_TTL_SECONDS),\n\t\tloginRedirect: options.loginRedirect ?? DEFAULT_LOGIN_REDIRECT,\n\t\tredirectCookie: options.redirectCookie ?? true,\n\t\tlog: options.logger ?? createLogger('sentinel-handlers'),\n\t\topts: options\n\t};\n\n\tconst POST: RequestHandler = async (event) => {\n\t\tconst path = event.params.path || '';\n\n\t\tswitch (path) {\n\t\t\tcase 'start-login':\n\t\t\t\treturn handleStartLogin(event, resolved);\n\t\t\tcase 'poll':\n\t\t\t\treturn handlePoll(event, resolved);\n\t\t\tcase 'claim':\n\t\t\t\treturn handleClaimPost(event, resolved);\n\t\t\tcase 'logout':\n\t\t\t\treturn handleLogout(event, resolved);\n\t\t\tcase 'refresh':\n\t\t\t\treturn handleRefresh(event, resolved);\n\t\t\tcase 'register':\n\t\t\t\treturn handleRegister(event, resolved);\n\t\t\tcase 'verify':\n\t\t\t\treturn handleVerify(event, resolved);\n\t\t\tdefault:\n\t\t\t\treturn json({ error: 'Not found' }, { status: 404 });\n\t\t}\n\t};\n\n\tconst GET: RequestHandler = async (event) => {\n\t\tconst path = event.params.path || '';\n\n\t\tswitch (path) {\n\t\t\tcase 'claim':\n\t\t\t\treturn handleClaimGet(event, resolved);\n\t\t\tcase 'logout':\n\t\t\t\treturn handleLogout(event, resolved);\n\t\t\tdefault:\n\t\t\t\treturn json({ error: 'Not found' }, { status: 404 });\n\t\t}\n\t};\n\n\treturn { GET, POST };\n}\n"]}
|
package/package.json
CHANGED