@draftlab/auth 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/allow.d.ts +58 -1
  2. package/dist/allow.js +61 -2
  3. package/dist/client.d.ts +2 -3
  4. package/dist/client.js +2 -2
  5. package/dist/core.d.ts +128 -8
  6. package/dist/core.js +496 -12
  7. package/dist/error.d.ts +242 -1
  8. package/dist/error.js +235 -1
  9. package/dist/index.d.ts +1 -8
  10. package/dist/index.js +1 -12
  11. package/dist/keys.d.ts +1 -1
  12. package/dist/keys.js +138 -3
  13. package/dist/pkce.js +160 -1
  14. package/dist/provider/code.d.ts +227 -3
  15. package/dist/provider/code.js +27 -14
  16. package/dist/provider/facebook.d.ts +2 -3
  17. package/dist/provider/facebook.js +1 -5
  18. package/dist/provider/github.d.ts +2 -3
  19. package/dist/provider/github.js +1 -5
  20. package/dist/provider/google.d.ts +2 -3
  21. package/dist/provider/google.js +1 -5
  22. package/dist/provider/oauth2.d.ts +175 -3
  23. package/dist/provider/oauth2.js +153 -5
  24. package/dist/provider/password.d.ts +384 -3
  25. package/dist/provider/password.js +4 -4
  26. package/dist/provider/provider.d.ts +226 -2
  27. package/dist/random.js +85 -1
  28. package/dist/storage/memory.d.ts +2 -2
  29. package/dist/storage/memory.js +1 -1
  30. package/dist/storage/storage.d.ts +161 -1
  31. package/dist/storage/storage.js +60 -1
  32. package/dist/storage/turso.d.ts +1 -1
  33. package/dist/storage/turso.js +1 -1
  34. package/dist/storage/unstorage.d.ts +2 -2
  35. package/dist/storage/unstorage.js +2 -2
  36. package/dist/subject.d.ts +61 -2
  37. package/dist/themes/theme.d.ts +208 -1
  38. package/dist/themes/theme.js +118 -1
  39. package/dist/ui/base.d.ts +22 -35
  40. package/dist/ui/base.js +388 -3
  41. package/dist/ui/code.d.ts +22 -137
  42. package/dist/ui/code.js +199 -161
  43. package/dist/ui/form.d.ts +8 -6
  44. package/dist/ui/form.js +57 -1
  45. package/dist/ui/icon.d.ts +7 -84
  46. package/dist/ui/icon.js +69 -2
  47. package/dist/ui/password.d.ts +30 -37
  48. package/dist/ui/password.js +340 -237
  49. package/dist/ui/select.d.ts +19 -218
  50. package/dist/ui/select.js +91 -4
  51. package/dist/util.d.ts +71 -1
  52. package/dist/util.js +106 -1
  53. package/package.json +5 -3
  54. package/dist/allow-CixonwTW.d.ts +0 -59
  55. package/dist/allow-DX5cehSc.js +0 -63
  56. package/dist/base-DRutbxgL.js +0 -422
  57. package/dist/code-DJxdFR7p.d.ts +0 -212
  58. package/dist/core-BZHEAefX.d.ts +0 -129
  59. package/dist/core-CDM5o4rs.js +0 -498
  60. package/dist/error-CWAdNAzm.d.ts +0 -243
  61. package/dist/error-DgAKK7b2.js +0 -237
  62. package/dist/form-6XKM_cOk.js +0 -61
  63. package/dist/icon-Ci5uqGB_.js +0 -192
  64. package/dist/keys-EEfxEGfO.js +0 -140
  65. package/dist/oauth2-B7-6Z7Lc.js +0 -155
  66. package/dist/oauth2-CXHukHf2.d.ts +0 -176
  67. package/dist/password-C4KLmO0O.d.ts +0 -385
  68. package/dist/pkce-276Za_rZ.js +0 -162
  69. package/dist/provider-tndlqCzp.d.ts +0 -227
  70. package/dist/random-SXMYlaVr.js +0 -87
  71. package/dist/select-BjySLL8I.js +0 -280
  72. package/dist/storage-BEaqEPNQ.js +0 -62
  73. package/dist/storage-CxKerLlc.d.ts +0 -162
  74. package/dist/subject-DMIMVtaT.d.ts +0 -62
  75. package/dist/theme-C9by7VXf.d.ts +0 -209
  76. package/dist/theme-CswaLtbW.js +0 -120
  77. package/dist/util-CSdHUFOo.js +0 -108
  78. package/dist/util-DbSKG1Xm.d.ts +0 -72
@@ -1,233 +1,34 @@
1
+ import { Theme } from "../themes/theme.js";
2
+
1
3
  //#region src/ui/select.d.ts
4
+
2
5
  /**
3
- * Pre-built UI component for OAuth provider selection.
4
- * Displays a clean selection interface where users can choose their preferred authentication method.
5
- *
6
- * ## Features
7
- *
8
- * - **Multiple providers**: Support for popular OAuth providers with built-in icons
9
- * - **Customizable displays**: Override provider names and copy text
10
- * - **Conditional visibility**: Hide specific providers using configuration
11
- * - **Type safety**: Full TypeScript support with autocomplete for known providers
12
- * - **Accessibility**: Proper ARIA labels and semantic markup
13
- *
14
- * ## Usage
15
- *
16
- * ```ts
17
- * import { Select } from "@draftauth/core/ui/select"
18
- *
19
- * export default issuer({
20
- * select: Select({
21
- * copy: {
22
- * button_provider: "Continuar com"
23
- * },
24
- * displays: {
25
- * code: "Código PIN",
26
- * github: "Conta GitHub"
27
- * },
28
- * providers: {
29
- * github: {
30
- * hide: true
31
- * },
32
- * google: {
33
- * display: "Google"
34
- * }
35
- * }
36
- * })
37
- * // ...
38
- * })
39
- * ```
40
- *
41
- * ## Provider Configuration
42
- *
43
- * Each provider can be individually configured:
44
- *
45
- * - **hide**: Boolean to control visibility
46
- * - **display**: Custom display name override
47
- *
48
- * Global display overrides are applied through the `displays` property, which affects
49
- * all providers of that type unless specifically overridden at the provider level.
50
- *
51
- * @packageDocumentation
52
- */
53
- /**
54
- * Default copy text used throughout the select UI.
55
- * These values are used when no custom copy is provided.
56
- */
57
- declare const DEFAULT_COPY: {
58
- /**
59
- * Copy for the provider button prefix text.
60
- * @example "Continue with GitHub" where "Continue with" is this value
61
- */
62
- button_provider: string;
63
- };
64
- /**
65
- * Default display names for all known provider types.
66
- * These provide consistent naming across the application and serve as fallbacks
67
- * when no custom display name is configured.
68
- */
69
- declare const DEFAULT_DISPLAY: {
70
- steam: string;
71
- twitch: string;
72
- google: string;
73
- github: string;
74
- apple: string;
75
- code: string;
76
- x: string;
77
- facebook: string;
78
- microsoft: string;
79
- slack: string;
80
- password: string;
81
- };
82
- /**
83
- * Customizable copy text for the Select UI component.
84
- * Allows overriding default text to support internationalization or custom branding.
85
- */
86
- type SelectCopy = typeof DEFAULT_COPY;
87
- /**
88
- * Union type of all known provider types that have default display names and icons.
89
- * These types get autocompletion support in the displays configuration.
90
- */
91
- type KnownProviderType = keyof typeof DEFAULT_DISPLAY;
92
- /**
93
- * Configuration options for individual providers in the select UI.
94
- * Each provider can be individually controlled and customized.
6
+ * Provider configuration for the select UI
95
7
  */
96
8
  interface ProviderConfig {
97
- /**
98
- * Whether to hide the provider from the select UI.
99
- * When true, the provider will not appear in the selection interface.
100
- *
101
- * @default false
102
- */
9
+ /** Whether to hide the provider from the select UI */
103
10
  hide?: boolean;
104
- /**
105
- * Custom display name for this specific provider instance.
106
- * Overrides both the default display name and any global display override.
107
- *
108
- * @example "My Custom Google Login"
109
- */
11
+ /** Custom display name for this provider */
110
12
  display?: string;
111
13
  }
112
14
  /**
113
- * Configuration properties for the Select UI component.
114
- * Provides comprehensive customization of appearance and behavior.
15
+ * Props for the Select component
115
16
  */
116
17
  interface SelectProps {
117
- /**
118
- * Provider-specific configurations mapped by provider key.
119
- * Each key corresponds to a provider instance, allowing individual customization.
120
- *
121
- * @example
122
- * ```ts
123
- * {
124
- * github: {
125
- * hide: true
126
- * },
127
- * google: {
128
- * display: "Google Account"
129
- * },
130
- * "custom-provider": {
131
- * display: "Custom OAuth"
132
- * }
133
- * }
134
- * ```
135
- */
18
+ /** Provider-specific configurations */
136
19
  providers?: Record<string, ProviderConfig>;
137
- /**
138
- * Custom copy text overrides for UI elements.
139
- * Useful for internationalization and custom branding.
140
- *
141
- * @example
142
- * ```ts
143
- * {
144
- * button_provider: "Sign in with"
145
- * }
146
- * ```
147
- */
148
- copy?: Partial<SelectCopy>;
149
- /**
150
- * Global display name overrides for provider types.
151
- *
152
- * This allows you to override the default display names globally for all providers
153
- * of a specific type. For known provider types, you get full autocompletion support.
154
- * Custom provider types are also supported.
155
- *
156
- * The priority order for display names is:
157
- * 1. Provider-specific `display` in `providers` config
158
- * 2. Type-specific `displays` override (this property)
159
- * 3. Default display name from `DEFAULT_DISPLAY`
160
- * 4. Provider type string as fallback
161
- *
162
- * @example
163
- * ```ts
164
- * {
165
- * displays: {
166
- * code: "PIN Code", // ✅ Autocomplete available
167
- * github: "GitHub Account", // ✅ Autocomplete available
168
- * customType: "Custom" // ✅ Also works for unknown types
169
- * }
170
- * }
171
- * ```
172
- */
173
- displays?: Partial<Record<KnownProviderType, string>> & Record<string, string>;
20
+ /** Custom copy text overrides */
21
+ copy?: {
22
+ button_provider?: string;
23
+ };
24
+ /** Global display name overrides for provider types */
25
+ displays?: Record<string, string>;
26
+ /** Theme configuration */
27
+ theme?: Theme;
174
28
  }
175
29
  /**
176
- * Creates a provider selection UI component for OAuth authentication.
177
- *
178
- * This component generates a complete authentication provider selection interface that:
179
- * - Displays available OAuth providers as clickable buttons
180
- * - Includes appropriate icons for recognized providers
181
- * - Supports custom theming and internationalization
182
- * - Provides accessible markup with proper ARIA attributes
183
- * - Handles provider visibility and custom display names
184
- *
185
- * @param props - Configuration options for customizing the select UI
186
- * @returns An async function that generates the HTML response for the selection interface
187
- *
188
- * @example
189
- * ```ts
190
- * // Basic usage with defaults
191
- * const selectUI = Select()
192
- *
193
- * // Customized with provider configuration
194
- * const selectUI = Select({
195
- * copy: {
196
- * button_provider: "Sign in with"
197
- * },
198
- * displays: {
199
- * github: "GitHub Account",
200
- * code: "Email Code"
201
- * },
202
- * providers: {
203
- * facebook: { hide: true },
204
- * google: { display: "Google Workspace" }
205
- * }
206
- * })
207
- *
208
- * // Use in issuer configuration
209
- * export default issuer({
210
- * select: selectUI,
211
- * // ... other configuration
212
- * })
213
- * ```
214
- *
215
- * ## Provider Resolution Logic
216
- *
217
- * Display names are resolved in the following priority order:
218
- * 1. Provider-specific `display` property
219
- * 2. Global `displays` type override
220
- * 3. Default display name from `DEFAULT_DISPLAY`
221
- * 4. Provider type string as final fallback
222
- *
223
- * ## Accessibility Features
224
- *
225
- * The generated UI includes:
226
- * - Semantic HTML structure
227
- * - Proper button roles and keyboard navigation
228
- * - Icon accessibility with `aria-hidden="true"`
229
- * - Screen reader friendly provider names
30
+ * Creates a provider selection UI
230
31
  */
231
- declare const Select: (props?: SelectProps) => (providers: Record<string, string>, _req: Request) => Promise<Response>;
32
+ declare const Select: (props?: SelectProps) => (providers: Record<string, string>) => Promise<Response>;
232
33
  //#endregion
233
- export { KnownProviderType, ProviderConfig, Select, SelectCopy, SelectProps };
34
+ export { ProviderConfig, Select, SelectProps };
package/dist/ui/select.js CHANGED
@@ -1,6 +1,93 @@
1
- import "../theme-CswaLtbW.js";
2
- import "../base-DRutbxgL.js";
3
- import "../icon-Ci5uqGB_.js";
4
- import { Select } from "../select-BjySLL8I.js";
1
+ import { Layout, renderToHTML } from "./base.js";
2
+ import { ICON_GITHUB, ICON_GOOGLE } from "./icon.js";
3
+ import { jsx, jsxs } from "preact/jsx-runtime";
5
4
 
5
+ //#region src/ui/select.tsx
6
+ /**
7
+ * Icon components for providers
8
+ */
9
+ const PROVIDER_ICONS = {
10
+ github: ICON_GITHUB,
11
+ google: ICON_GOOGLE,
12
+ code: () => /* @__PURE__ */ jsx("svg", {
13
+ width: "20",
14
+ height: "20",
15
+ viewBox: "0 0 52 52",
16
+ fill: "currentColor",
17
+ "aria-label": "Code authentication",
18
+ role: "img",
19
+ children: /* @__PURE__ */ jsx("path", { d: "M8.55,36.91A6.55,6.55,0,1,1,2,43.45,6.54,6.54,0,0,1,8.55,36.91Zm17.45,0a6.55,6.55,0,1,1-6.55,6.54A6.55,6.55,0,0,1,26,36.91Zm17.45,0a6.55,6.55,0,1,1-6.54,6.54A6.54,6.54,0,0,1,43.45,36.91ZM8.55,19.45A6.55,6.55,0,1,1,2,26,6.55,6.55,0,0,1,8.55,19.45Zm17.45,0A6.55,6.55,0,1,1,19.45,26,6.56,6.56,0,0,1,26,19.45Zm17.45,0A6.55,6.55,0,1,1,36.91,26,6.55,6.55,0,0,1,43.45,19.45ZM8.55,2A6.55,6.55,0,1,1,2,8.55,6.54,6.54,0,0,1,8.55,2ZM26,2a6.55,6.55,0,1,1-6.55,6.55A6.55,6.55,0,0,1,26,2ZM43.45,2a6.55,6.55,0,1,1-6.54,6.55A6.55,6.55,0,0,1,43.45,2Z" })
20
+ })
21
+ };
22
+ /**
23
+ * Default display names for provider types
24
+ */
25
+ const DEFAULT_DISPLAYS = {
26
+ github: "GitHub",
27
+ google: "Google",
28
+ code: "Code",
29
+ password: "Password"
30
+ };
31
+ /**
32
+ * Individual provider button component
33
+ */
34
+ const ProviderButton = ({ providerKey, providerType, config, displays, buttonText }) => {
35
+ const displayName = config?.display || displays[providerType] || DEFAULT_DISPLAYS[providerType] || providerType;
36
+ const IconComponent = PROVIDER_ICONS[providerKey] || PROVIDER_ICONS[providerType];
37
+ return /* @__PURE__ */ jsxs("a", {
38
+ href: `./${providerKey}/authorize`,
39
+ "data-component": "button",
40
+ "data-color": "ghost",
41
+ "aria-label": `${buttonText} ${displayName}`,
42
+ children: [
43
+ IconComponent && /* @__PURE__ */ jsx("i", {
44
+ "data-slot": "icon",
45
+ children: /* @__PURE__ */ jsx(IconComponent, {})
46
+ }),
47
+ buttonText,
48
+ " ",
49
+ displayName
50
+ ]
51
+ });
52
+ };
53
+ /**
54
+ * Main provider selection component
55
+ */
56
+ const ProviderSelect = ({ providers, config = {}, theme }) => {
57
+ const buttonText = config.copy?.button_provider || "Continue with";
58
+ const displays = {
59
+ ...DEFAULT_DISPLAYS,
60
+ ...config.displays
61
+ };
62
+ const visibleProviders = Object.entries(providers).filter(([key]) => !config.providers?.[key]?.hide);
63
+ return /* @__PURE__ */ jsx(Layout, {
64
+ theme,
65
+ title: "Sign In",
66
+ children: /* @__PURE__ */ jsx("div", {
67
+ "data-component": "form",
68
+ children: visibleProviders.map(([key, type]) => /* @__PURE__ */ jsx(ProviderButton, {
69
+ providerKey: key,
70
+ providerType: type,
71
+ config: config.providers?.[key],
72
+ displays,
73
+ buttonText
74
+ }, key))
75
+ })
76
+ });
77
+ };
78
+ /**
79
+ * Creates a provider selection UI
80
+ */
81
+ const Select = (props = {}) => {
82
+ return async (providers) => {
83
+ const html = renderToHTML(/* @__PURE__ */ jsx(ProviderSelect, {
84
+ providers,
85
+ config: props,
86
+ theme: props.theme
87
+ }));
88
+ return new Response(html, { headers: { "Content-Type": "text/html" } });
89
+ };
90
+ };
91
+
92
+ //#endregion
6
93
  export { Select };
package/dist/util.d.ts CHANGED
@@ -1,2 +1,72 @@
1
- import { Prettify, getRelativeUrl, isDomainMatch, lazy } from "./util-DbSKG1Xm.js";
1
+ import { RouterContext } from "@draftlab/auth-router/types";
2
+
3
+ //#region src/util.d.ts
4
+
5
+ /**
6
+ * Utility type that flattens complex types for better IntelliSense display.
7
+ * Converts intersections and complex mapped types into cleaner object types.
8
+ *
9
+ * @template T - The type to prettify
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * type Complex = { a: string } & { b: number }
14
+ * type Clean = Prettify<Complex> // { a: string; b: number }
15
+ * ```
16
+ */
17
+ type Prettify<T> = { [K in keyof T]: T[K] } & {};
18
+ /**
19
+ * Constructs a complete URL relative to the current request context.
20
+ * Handles proxy headers (x-forwarded-*) to ensure correct URL generation
21
+ * in containerized and load-balanced environments.
22
+ *
23
+ * @param ctx - Router context containing request information
24
+ * @param path - Relative path to append to the base URL
25
+ * @returns Complete URL string with proper protocol, host, and port
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const callbackUrl = getRelativeUrl(ctx, "/callback")
30
+ * // Returns: "https://myapp.com/auth/callback"
31
+ * ```
32
+ */
33
+ declare const getRelativeUrl: (ctx: RouterContext, path: string) => string;
34
+ /**
35
+ * Determines if two domain names are considered a match for security purposes.
36
+ * Uses effective TLD+1 matching to allow subdomains while preventing
37
+ * unauthorized cross-domain requests.
38
+ *
39
+ * @param a - First domain name to compare
40
+ * @param b - Second domain name to compare
41
+ * @returns True if domains are considered a security match
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * isDomainMatch("app.example.com", "auth.example.com") // true
46
+ * isDomainMatch("example.com", "evil.com") // false
47
+ * isDomainMatch("app.co.uk", "auth.co.uk") // true (handles two-part TLD)
48
+ * ```
49
+ */
50
+ declare const isDomainMatch: (a: string, b: string) => boolean;
51
+ /**
52
+ * Creates a lazy-evaluated function that caches the result of the first execution.
53
+ * Subsequent calls return the cached value without re-executing the function.
54
+ *
55
+ * @template T - The return type of the lazy function
56
+ * @param fn - Function to execute lazily
57
+ * @returns Function that returns the cached result
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * const expensiveOperation = lazy(() => {
62
+ * // Computing... (only logs once)
63
+ * return heavyComputation()
64
+ * })
65
+ *
66
+ * const result1 = expensiveOperation() // Executes and caches
67
+ * const result2 = expensiveOperation() // Returns cached value
68
+ * ```
69
+ */
70
+ declare const lazy: <T>(fn: () => T) => (() => T);
71
+ //#endregion
2
72
  export { Prettify, getRelativeUrl, isDomainMatch, lazy };
package/dist/util.js CHANGED
@@ -1,3 +1,108 @@
1
- import { getRelativeUrl, isDomainMatch, lazy } from "./util-CSdHUFOo.js";
1
+ //#region src/util.ts
2
+ /**
3
+ * Constructs a complete URL relative to the current request context.
4
+ * Handles proxy headers (x-forwarded-*) to ensure correct URL generation
5
+ * in containerized and load-balanced environments.
6
+ *
7
+ * @param ctx - Router context containing request information
8
+ * @param path - Relative path to append to the base URL
9
+ * @returns Complete URL string with proper protocol, host, and port
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const callbackUrl = getRelativeUrl(ctx, "/callback")
14
+ * // Returns: "https://myapp.com/auth/callback"
15
+ * ```
16
+ */
17
+ const getRelativeUrl = (ctx, path) => {
18
+ const result = new URL(path, ctx.request.url);
19
+ result.host = ctx.header("x-forwarded-host") || result.host;
20
+ result.protocol = ctx.header("x-forwarded-proto") || result.protocol;
21
+ result.port = ctx.header("x-forwarded-port") || result.port;
22
+ return result.toString();
23
+ };
24
+ /**
25
+ * List of known two-part top-level domains that require special handling
26
+ * for domain matching. These domains have an additional level that should
27
+ * be considered when determining effective domain boundaries.
28
+ */
29
+ const twoPartTlds = [
30
+ "co.uk",
31
+ "co.jp",
32
+ "co.kr",
33
+ "co.nz",
34
+ "co.za",
35
+ "co.in",
36
+ "com.au",
37
+ "com.br",
38
+ "com.cn",
39
+ "com.mx",
40
+ "com.tw",
41
+ "net.au",
42
+ "org.uk",
43
+ "ne.jp",
44
+ "ac.uk",
45
+ "gov.uk",
46
+ "edu.au",
47
+ "gov.au"
48
+ ];
49
+ /**
50
+ * Determines if two domain names are considered a match for security purposes.
51
+ * Uses effective TLD+1 matching to allow subdomains while preventing
52
+ * unauthorized cross-domain requests.
53
+ *
54
+ * @param a - First domain name to compare
55
+ * @param b - Second domain name to compare
56
+ * @returns True if domains are considered a security match
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * isDomainMatch("app.example.com", "auth.example.com") // true
61
+ * isDomainMatch("example.com", "evil.com") // false
62
+ * isDomainMatch("app.co.uk", "auth.co.uk") // true (handles two-part TLD)
63
+ * ```
64
+ */
65
+ const isDomainMatch = (a, b) => {
66
+ if (a === b) return true;
67
+ const partsA = a.split(".");
68
+ const partsB = b.split(".");
69
+ const hasTwoPartTld = twoPartTlds.some((tld) => a.endsWith(`.${tld}`) || b.endsWith(`.${tld}`));
70
+ const numParts = hasTwoPartTld ? -3 : -2;
71
+ const min = Math.min(partsA.length, partsB.length, numParts);
72
+ const tailA = partsA.slice(min).join(".");
73
+ const tailB = partsB.slice(min).join(".");
74
+ return tailA === tailB;
75
+ };
76
+ /**
77
+ * Creates a lazy-evaluated function that caches the result of the first execution.
78
+ * Subsequent calls return the cached value without re-executing the function.
79
+ *
80
+ * @template T - The return type of the lazy function
81
+ * @param fn - Function to execute lazily
82
+ * @returns Function that returns the cached result
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * const expensiveOperation = lazy(() => {
87
+ * // Computing... (only logs once)
88
+ * return heavyComputation()
89
+ * })
90
+ *
91
+ * const result1 = expensiveOperation() // Executes and caches
92
+ * const result2 = expensiveOperation() // Returns cached value
93
+ * ```
94
+ */
95
+ const lazy = (fn) => {
96
+ let value;
97
+ let hasValue = false;
98
+ return () => {
99
+ if (!hasValue) {
100
+ value = fn();
101
+ hasValue = true;
102
+ }
103
+ return value;
104
+ };
105
+ };
2
106
 
107
+ //#endregion
3
108
  export { getRelativeUrl, isDomainMatch, lazy };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftlab/auth",
3
- "version": "0.0.3",
3
+ "version": "0.1.0",
4
4
  "type": "module",
5
5
  "description": "Core implementation for @draftlab/auth",
6
6
  "author": "Matheus Pergoli",
@@ -37,7 +37,7 @@
37
37
  ],
38
38
  "license": "MIT",
39
39
  "devDependencies": {
40
- "@types/node": "^24.0.12",
40
+ "@types/node": "^24.0.13",
41
41
  "tsdown": "^0.12.9",
42
42
  "typescript": "^5.8.3",
43
43
  "@draftlab/tsconfig": "0.1.0"
@@ -57,7 +57,9 @@
57
57
  "dependencies": {
58
58
  "@standard-schema/spec": "^1.0.0",
59
59
  "jose": "^6.0.11",
60
- "@draftlab/auth-router": "0.0.1"
60
+ "preact": "^10.26.9",
61
+ "preact-render-to-string": "^6.5.13",
62
+ "@draftlab/auth-router": "0.0.2"
61
63
  },
62
64
  "engines": {
63
65
  "node": ">=18"
@@ -1,59 +0,0 @@
1
- //#region src/allow.d.ts
2
- /**
3
- * Client authorization validation utilities.
4
- * Provides security checks to determine if OAuth authorization requests should be permitted
5
- * based on redirect URI validation and domain matching policies.
6
- */
7
- /**
8
- * Input parameters for authorization allow checks.
9
- * Contains all necessary information to validate if a client request should be permitted.
10
- */
11
- interface AllowCheckInput {
12
- /** The client ID of the application requesting authorization */
13
- readonly clientID: string;
14
- /** The redirect URI where the user will be sent after authorization */
15
- readonly redirectURI: string;
16
- /** Optional audience parameter for the authorization request */
17
- readonly audience?: string;
18
- }
19
- /**
20
- * Default authorization check that validates client requests based on redirect URI security.
21
- *
22
- * ## Security Policy
23
- * - **Localhost**: Always allowed (for development)
24
- * - **Same domain**: Redirect URI must match request origin at TLD+1 level
25
- * - **Cross-domain**: Rejected for security
26
- *
27
- * This prevents unauthorized applications from hijacking authorization codes by using
28
- * malicious redirect URIs that don't belong to the legitimate client application.
29
- *
30
- * @param input - Client request details including ID and redirect URI
31
- * @param req - The original HTTP request for domain comparison
32
- * @returns Promise resolving to true if the request should be allowed
33
- *
34
- * @example
35
- * ```ts
36
- * // Allowed: localhost development
37
- * await defaultAllowCheck({
38
- * clientID: "dev-app",
39
- * redirectURI: "http://localhost:3000/callback"
40
- * }, request) // → true
41
- *
42
- * // Allowed: same domain
43
- * // Request from: https://myapp.com
44
- * await defaultAllowCheck({
45
- * clientID: "web-app",
46
- * redirectURI: "https://auth.myapp.com/callback"
47
- * }, request) // → true
48
- *
49
- * // Rejected: different domain
50
- * // Request from: https://myapp.com
51
- * await defaultAllowCheck({
52
- * clientID: "malicious-app",
53
- * redirectURI: "https://evil.com/steal-codes"
54
- * }, request) // → false
55
- * ```
56
- */
57
- declare const defaultAllowCheck: (input: AllowCheckInput, req: Request) => Promise<boolean>;
58
- //#endregion
59
- export { AllowCheckInput, defaultAllowCheck };
@@ -1,63 +0,0 @@
1
- import { isDomainMatch } from "./util-CSdHUFOo.js";
2
-
3
- //#region src/allow.ts
4
- /**
5
- * Default authorization check that validates client requests based on redirect URI security.
6
- *
7
- * ## Security Policy
8
- * - **Localhost**: Always allowed (for development)
9
- * - **Same domain**: Redirect URI must match request origin at TLD+1 level
10
- * - **Cross-domain**: Rejected for security
11
- *
12
- * This prevents unauthorized applications from hijacking authorization codes by using
13
- * malicious redirect URIs that don't belong to the legitimate client application.
14
- *
15
- * @param input - Client request details including ID and redirect URI
16
- * @param req - The original HTTP request for domain comparison
17
- * @returns Promise resolving to true if the request should be allowed
18
- *
19
- * @example
20
- * ```ts
21
- * // Allowed: localhost development
22
- * await defaultAllowCheck({
23
- * clientID: "dev-app",
24
- * redirectURI: "http://localhost:3000/callback"
25
- * }, request) // → true
26
- *
27
- * // Allowed: same domain
28
- * // Request from: https://myapp.com
29
- * await defaultAllowCheck({
30
- * clientID: "web-app",
31
- * redirectURI: "https://auth.myapp.com/callback"
32
- * }, request) // → true
33
- *
34
- * // Rejected: different domain
35
- * // Request from: https://myapp.com
36
- * await defaultAllowCheck({
37
- * clientID: "malicious-app",
38
- * redirectURI: "https://evil.com/steal-codes"
39
- * }, request) // → false
40
- * ```
41
- */
42
- const defaultAllowCheck = (input, req) => {
43
- return Promise.resolve((() => {
44
- let redirectHostname;
45
- try {
46
- redirectHostname = new URL(input.redirectURI).hostname;
47
- } catch {
48
- return false;
49
- }
50
- if (redirectHostname === "localhost" || redirectHostname === "127.0.0.1") return true;
51
- let currentHostname;
52
- try {
53
- const forwardedHost = req.headers.get("x-forwarded-host");
54
- currentHostname = forwardedHost ? new URL(`https://${forwardedHost}`).hostname : new URL(req.url).hostname;
55
- } catch {
56
- return false;
57
- }
58
- return isDomainMatch(redirectHostname, currentHostname);
59
- })());
60
- };
61
-
62
- //#endregion
63
- export { defaultAllowCheck };