hazo_auth 10.2.0 → 10.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,6 +54,16 @@ HAZO_AUTH_OAUTH_KEY_V1=$(openssl rand -base64 32)
54
54
  npm install hazo_secure
55
55
  ```
56
56
 
57
+ ```js
58
+ // 4. Add hazo_secure to serverExternalPackages in next.config.mjs / next.config.js
59
+ // so the bundler leaves the literal import("hazo_secure/crypto") as a native
60
+ // external import. Without this, Google sign-in fails with
61
+ // GoogleTokenStorageUnconfigured even when hazo_secure is installed.
62
+ const nextConfig = {
63
+ serverExternalPackages: ["hazo_secure", /* ...existing entries */],
64
+ };
65
+ ```
66
+
57
67
  **Client Usage:**
58
68
  ```tsx
59
69
  import { requestGoogleScopes } from "hazo_auth/client";
@@ -1340,6 +1350,10 @@ Google OAuth adds one new dependency:
1340
1350
  HAZO_AUTH_OAUTH_KEY_V1=$(openssl rand -base64 32)
1341
1351
  ```
1342
1352
  3. Install the optional peer: `npm install hazo_secure`
1353
+ 4. Add `hazo_secure` to `serverExternalPackages` in `next.config.mjs` / `next.config.js`. The
1354
+ service loads encryption via a literal `import("hazo_secure/crypto")`; if the bundler inlines
1355
+ `hazo_secure` instead of treating it as an external import, Google sign-in fails with
1356
+ `GoogleTokenStorageUnconfigured` even when the peer is installed and keys are set.
1343
1357
 
1344
1358
  **Usage:**
1345
1359
 
@@ -13,6 +13,7 @@ const nextConfig = {
13
13
  serverExternalPackages: [
14
14
  "hazo_notify", "argon2",
15
15
  "hazo_core", "hazo_config", // required for Next 16 + Turbopack
16
+ "hazo_secure", // required if using Google API token storage (getGoogleToken)
16
17
  ],
17
18
  };
18
19
  ```
@@ -1138,13 +1139,17 @@ ls app/api/hazo_auth/set_password/route.ts
1138
1139
  Only needed if you use `requestGoogleScopes` / `getGoogleToken` for Google API access beyond sign-in:
1139
1140
 
1140
1141
  1. Install optional peer: `npm install hazo_secure`
1141
- 2. Run migration: `npm run migrate -- migrations/021_hazo_google_oauth_tokens.sql`
1142
- 3. Set env vars:
1142
+ 2. Add `hazo_secure` to `serverExternalPackages` in `next.config.mjs` / `next.config.js`. The
1143
+ service loads encryption via a literal `import("hazo_secure/crypto")`; if the bundler inlines
1144
+ `hazo_secure` rather than leaving it as a native external import, Google sign-in fails with
1145
+ `GoogleTokenStorageUnconfigured` even when the peer is installed and keys are configured.
1146
+ 3. Run migration: `npm run migrate -- migrations/021_hazo_google_oauth_tokens.sql`
1147
+ 4. Set env vars:
1143
1148
  ```env
1144
1149
  HAZO_AUTH_OAUTH_KEY_CURRENT=v1
1145
1150
  HAZO_AUTH_OAUTH_KEY_V1=<base64-32-bytes from: openssl rand -base64 32>
1146
1151
  ```
1147
- 4. Wire the new routes in your Next.js app (same pattern as other hazo_auth routes):
1152
+ 5. Wire the new routes in your Next.js app (same pattern as other hazo_auth routes):
1148
1153
  ```ts
1149
1154
  // app/api/hazo_auth/google/token/route.ts
1150
1155
  export { GET as googleTokenGET, DELETE as googleTokenDELETE } from "hazo_auth/server/routes";
@@ -3,7 +3,6 @@
3
3
  import { createCrudService } from "hazo_connect/server";
4
4
  import { get_hazo_connect_instance } from "../hazo_connect_instance.server.js";
5
5
  import { create_app_logger } from "../app_logger.js";
6
- import { optional_import } from "hazo_core";
7
6
  import { randomUUID } from "crypto";
8
7
 
9
8
  // section: errors
@@ -70,10 +69,32 @@ function makeKeyProvider(
70
69
  });
71
70
  }
72
71
 
72
+ /**
73
+ * Indirection around the hazo_secure/crypto dynamic import.
74
+ *
75
+ * The import specifier MUST be a string literal. hazo_secure is an optional
76
+ * peer dep, so this used to go through hazo_core's `optional_import(pkg)` — but
77
+ * that performs a dynamic `import(variable)`, which Turbopack/webpack cannot
78
+ * statically resolve when consumers bundle their server. The import then fails
79
+ * unconditionally and sign-in dies on GoogleTokenStorageUnconfigured even when
80
+ * hazo_secure is installed. A literal `import("hazo_secure/crypto")` is left as
81
+ * a native external import and resolves at runtime (consumers must list
82
+ * hazo_secure in serverExternalPackages so it isn't bundled).
83
+ *
84
+ * Wrapped in an object so tests can stub `_cryptoLoader.load` without touching
85
+ * the literal specifier. Not part of the public API (underscore-prefixed).
86
+ */
87
+ export const _cryptoLoader = {
88
+ load: (): Promise<typeof import("hazo_secure/crypto")> => import("hazo_secure/crypto"),
89
+ };
90
+
73
91
  async function load_crypto_module() {
74
- const cryptoModule = await optional_import<typeof import("hazo_secure/crypto")>(
75
- "hazo_secure/crypto"
76
- );
92
+ let cryptoModule: typeof import("hazo_secure/crypto") | null;
93
+ try {
94
+ cryptoModule = await _cryptoLoader.load();
95
+ } catch {
96
+ cryptoModule = null;
97
+ }
77
98
  if (!cryptoModule) throw new GoogleTokenStorageUnconfigured();
78
99
  return cryptoModule;
79
100
  }
@@ -21,6 +21,24 @@ export type GoogleTokenStatus = {
21
21
  scopes: string;
22
22
  expires_at: string | null;
23
23
  };
24
+ /**
25
+ * Indirection around the hazo_secure/crypto dynamic import.
26
+ *
27
+ * The import specifier MUST be a string literal. hazo_secure is an optional
28
+ * peer dep, so this used to go through hazo_core's `optional_import(pkg)` — but
29
+ * that performs a dynamic `import(variable)`, which Turbopack/webpack cannot
30
+ * statically resolve when consumers bundle their server. The import then fails
31
+ * unconditionally and sign-in dies on GoogleTokenStorageUnconfigured even when
32
+ * hazo_secure is installed. A literal `import("hazo_secure/crypto")` is left as
33
+ * a native external import and resolves at runtime (consumers must list
34
+ * hazo_secure in serverExternalPackages so it isn't bundled).
35
+ *
36
+ * Wrapped in an object so tests can stub `_cryptoLoader.load` without touching
37
+ * the literal specifier. Not part of the public API (underscore-prefixed).
38
+ */
39
+ export declare const _cryptoLoader: {
40
+ load: () => Promise<typeof import("hazo_secure/crypto")>;
41
+ };
24
42
  /**
25
43
  * Stores (inserts or updates) Google OAuth tokens for a user, encrypting sensitive fields.
26
44
  * Throws GoogleTokenStorageUnconfigured if hazo_secure/crypto is unavailable or keys are missing.
@@ -1 +1 @@
1
- {"version":3,"file":"google_token_service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/google_token_service.ts"],"names":[],"mappings":"AAUA,qBAAa,8BAA+B,SAAQ,KAAK;;CAOxD;AAID,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,eAAe,GAAG,oBAAoB,GAAG,gBAAgB,GAAG,cAAc,CAAA;CAAE,CAAC;AAErG,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AA6CF;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,IAAI,CAAC,CA0Ef;AAID;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CA6I5B;AAID;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA6D1C;AAID;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAexF"}
1
+ {"version":3,"file":"google_token_service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/google_token_service.ts"],"names":[],"mappings":"AASA,qBAAa,8BAA+B,SAAQ,KAAK;;CAOxD;AAID,MAAM,MAAM,2BAA2B,GAAG;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,eAAe,GAAG,oBAAoB,GAAG,gBAAgB,GAAG,cAAc,CAAA;CAAE,CAAC;AAErG,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAmCF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa;gBACd,OAAO,CAAC,cAAc,oBAAoB,CAAC,CAAC;CACvD,CAAC;AAeF;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,IAAI,CAAC,CA0Ef;AAID;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CA6I5B;AAID;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA6D1C;AAID;;;GAGG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAexF"}
@@ -3,7 +3,6 @@
3
3
  import { createCrudService } from "hazo_connect/server";
4
4
  import { get_hazo_connect_instance } from "../hazo_connect_instance.server.js";
5
5
  import { create_app_logger } from "../app_logger.js";
6
- import { optional_import } from "hazo_core";
7
6
  import { randomUUID } from "crypto";
8
7
  // section: errors
9
8
  export class GoogleTokenStorageUnconfigured extends Error {
@@ -26,8 +25,32 @@ function makeKeyProvider(LookupKeyProvider) {
26
25
  return undefined;
27
26
  });
28
27
  }
28
+ /**
29
+ * Indirection around the hazo_secure/crypto dynamic import.
30
+ *
31
+ * The import specifier MUST be a string literal. hazo_secure is an optional
32
+ * peer dep, so this used to go through hazo_core's `optional_import(pkg)` — but
33
+ * that performs a dynamic `import(variable)`, which Turbopack/webpack cannot
34
+ * statically resolve when consumers bundle their server. The import then fails
35
+ * unconditionally and sign-in dies on GoogleTokenStorageUnconfigured even when
36
+ * hazo_secure is installed. A literal `import("hazo_secure/crypto")` is left as
37
+ * a native external import and resolves at runtime (consumers must list
38
+ * hazo_secure in serverExternalPackages so it isn't bundled).
39
+ *
40
+ * Wrapped in an object so tests can stub `_cryptoLoader.load` without touching
41
+ * the literal specifier. Not part of the public API (underscore-prefixed).
42
+ */
43
+ export const _cryptoLoader = {
44
+ load: () => import("hazo_secure/crypto"),
45
+ };
29
46
  async function load_crypto_module() {
30
- const cryptoModule = await optional_import("hazo_secure/crypto");
47
+ let cryptoModule;
48
+ try {
49
+ cryptoModule = await _cryptoLoader.load();
50
+ }
51
+ catch (_a) {
52
+ cryptoModule = null;
53
+ }
31
54
  if (!cryptoModule)
32
55
  throw new GoogleTokenStorageUnconfigured();
33
56
  return cryptoModule;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_auth",
3
- "version": "10.2.0",
3
+ "version": "10.2.1",
4
4
  "description": "Zero-config authentication UI components for Next.js with RBAC, OAuth, scope-based multi-tenancy, and invitations",
5
5
  "keywords": [
6
6
  "authentication",
@@ -253,9 +253,9 @@
253
253
  "@radix-ui/react-tabs": "^1.1.0",
254
254
  "@radix-ui/react-tooltip": "^1.2.0",
255
255
  "hazo_api": "^2.4.0",
256
- "hazo_config": "^2.1.10",
257
- "hazo_connect": "^3.6.0",
258
- "hazo_core": "^1.1.0",
256
+ "hazo_config": "^2.1.11",
257
+ "hazo_connect": "^3.7.0",
258
+ "hazo_core": "^1.2.0",
259
259
  "hazo_logs": "^2.0.3",
260
260
  "hazo_notify": "^6.1.3",
261
261
  "hazo_secure": "^1.1.0",
@@ -393,9 +393,9 @@
393
393
  "eslint-config-next": "^16.0.4",
394
394
  "eslint-plugin-storybook": "^10.0.6",
395
395
  "hazo_api": "^2.4.0",
396
- "hazo_config": "^2.1.10",
397
- "hazo_connect": "^3.6.0",
398
- "hazo_core": "^1.1.0",
396
+ "hazo_config": "^2.1.11",
397
+ "hazo_connect": "^3.7.0",
398
+ "hazo_core": "^1.2.0",
399
399
  "hazo_logs": "^2.0.3",
400
400
  "hazo_notify": "^6.1.3",
401
401
  "hazo_ui": "^4.0.0",