@auditauth/next 0.2.0-beta.3 → 0.2.0-beta.5

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.
@@ -22,8 +22,8 @@ __export(createAuditAuth_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(createAuditAuth_exports);
24
24
  var import_headers = require("next/headers.js");
25
- var import_sdk = require("../sdk.js");
26
- var import_settings = require("../settings.js");
25
+ var import_sdk = require("../sdk.cjs");
26
+ var import_settings = require("../settings.cjs");
27
27
  function createAuditAuthNext(config) {
28
28
  const base = new URL(config.baseUrl);
29
29
  async function createInstance() {
@@ -15,8 +15,8 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
15
15
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
16
16
  var facade_exports = {};
17
17
  module.exports = __toCommonJS(facade_exports);
18
- __reExport(facade_exports, require("./createAuditAuth.js"), module.exports);
18
+ __reExport(facade_exports, require("./createAuditAuth.cjs"), module.exports);
19
19
  // Annotate the CommonJS export names for ESM import in node:
20
20
  0 && (module.exports = {
21
- ...require("./createAuditAuth.js")
21
+ ...require("./createAuditAuth.cjs")
22
22
  });
package/dist/guard.cjs CHANGED
@@ -25,7 +25,7 @@ __export(guard_exports, {
25
25
  module.exports = __toCommonJS(guard_exports);
26
26
  var import_jsx_runtime = require("react/jsx-runtime");
27
27
  var import_react = require("react");
28
- var import_settings = require("./settings.js");
28
+ var import_settings = require("./settings.cjs");
29
29
  const AuthContext = (0, import_react.createContext)(null);
30
30
  const useAuditAuth = () => {
31
31
  const ctx = (0, import_react.useContext)(AuthContext);
package/dist/helpers.cjs CHANGED
@@ -23,7 +23,7 @@ __export(helpers_exports, {
23
23
  logout: () => logout
24
24
  });
25
25
  module.exports = __toCommonJS(helpers_exports);
26
- var import_settings = require("./settings.js");
26
+ var import_settings = require("./settings.cjs");
27
27
  const login = () => {
28
28
  window.location.href = import_settings.SETTINGS.bff.paths.login;
29
29
  };
package/dist/index.cjs CHANGED
@@ -28,11 +28,11 @@ __export(index_exports, {
28
28
  useAuditAuth: () => import_guard.useAuditAuth
29
29
  });
30
30
  module.exports = __toCommonJS(index_exports);
31
- var import_sdk = require("./sdk.js");
32
- var import_guard = require("./guard.js");
33
- var import_helpers = require("./helpers.js");
34
- var import_request = require("./request.js");
35
- __reExport(index_exports, require("./facade/index.js"), module.exports);
31
+ var import_sdk = require("./sdk.cjs");
32
+ var import_guard = require("./guard.cjs");
33
+ var import_helpers = require("./helpers.cjs");
34
+ var import_request = require("./request.cjs");
35
+ __reExport(index_exports, require("./facade/index.cjs"), module.exports);
36
36
  // Annotate the CommonJS export names for ESM import in node:
37
37
  0 && (module.exports = {
38
38
  AuditAuthGuard,
@@ -42,5 +42,5 @@ __reExport(index_exports, require("./facade/index.js"), module.exports);
42
42
  login,
43
43
  logout,
44
44
  useAuditAuth,
45
- ...require("./facade/index.js")
45
+ ...require("./facade/index.cjs")
46
46
  });
package/dist/request.cjs CHANGED
@@ -23,7 +23,7 @@ __export(request_exports, {
23
23
  });
24
24
  module.exports = __toCommonJS(request_exports);
25
25
  var import_headers = require("next/headers.js");
26
- var import_settings = require("./settings.js");
26
+ var import_settings = require("./settings.cjs");
27
27
  var import_headers2 = require("next/headers.js");
28
28
  var import_core = require("@auditauth/core");
29
29
  const getRequestOrigin = async () => {
package/dist/sdk.cjs CHANGED
@@ -23,9 +23,12 @@ __export(sdk_exports, {
23
23
  });
24
24
  module.exports = __toCommonJS(sdk_exports);
25
25
  var import_server = require("next/server.js");
26
- var import_settings = require("./settings.js");
26
+ var import_settings = require("./settings.cjs");
27
27
  var import_core = require("@auditauth/core");
28
28
  var import_node = require("@auditauth/node");
29
+ const CALLBACK_CODE_COOKIE = "auditauth_last_code";
30
+ const CALLBACK_CODE_TTL_SECONDS = 120;
31
+ const callbackInFlight = /* @__PURE__ */ new Map();
29
32
  class AuditAuthNext {
30
33
  constructor(config, cookies) {
31
34
  if (!config.appId) throw new Error("Missing appId");
@@ -44,8 +47,32 @@ class AuditAuthNext {
44
47
  refresh: this.cookies.get(import_settings.SETTINGS.storage_keys.refresh)
45
48
  };
46
49
  }
50
+ isSecureCookie() {
51
+ return this.config.redirectUrl.startsWith("https://");
52
+ }
53
+ hasAuthCookies() {
54
+ const { access, refresh } = this.getCookieTokens();
55
+ return !!(access && refresh);
56
+ }
57
+ getLastAuthorizedCode() {
58
+ return this.cookies.get(CALLBACK_CODE_COOKIE);
59
+ }
60
+ setLastAuthorizedCode(code) {
61
+ this.cookies.set(CALLBACK_CODE_COOKIE, code, {
62
+ httpOnly: true,
63
+ sameSite: "lax",
64
+ secure: this.isSecureCookie(),
65
+ path: "/",
66
+ maxAge: CALLBACK_CODE_TTL_SECONDS
67
+ });
68
+ }
69
+ isDuplicateCodeError(error) {
70
+ if (!(error instanceof Error)) return false;
71
+ const message = error.message.toLowerCase();
72
+ return message.includes("code not found");
73
+ }
47
74
  setCookieTokens(params) {
48
- const isSecure = this.config.redirectUrl.includes("https");
75
+ const isSecure = this.isSecureCookie();
49
76
  this.cookies.set(
50
77
  import_settings.SETTINGS.storage_keys.access,
51
78
  params.access_token,
@@ -102,36 +129,61 @@ class AuditAuthNext {
102
129
  }
103
130
  async callback(request) {
104
131
  const code = new URL(request.url).searchParams.get("code");
105
- try {
106
- const { data } = await (0, import_core.authorizeCode)({ code, client_type: "server" });
107
- const session = {
108
- user: data.user
109
- };
110
- const isSecure = this.config.redirectUrl.includes("http");
111
- this.cookies.set(
112
- import_settings.SETTINGS.storage_keys.session,
113
- JSON.stringify(session),
114
- {
115
- httpOnly: true,
116
- sameSite: "lax",
117
- secure: isSecure,
118
- path: "/",
119
- maxAge: data.refresh_expires_seconds - 60
120
- }
121
- );
122
- this.setCookieTokens({
123
- access_token: data.access_token,
124
- access_expires_seconds: data.access_expires_seconds,
125
- refresh_token: data.refresh_token,
126
- refresh_expires_seconds: data.refresh_expires_seconds
127
- });
128
- return { ok: true, url: this.config.redirectUrl };
129
- } catch {
132
+ if (!code) {
130
133
  return {
131
134
  ok: false,
132
135
  url: `${import_settings.SETTINGS.domains.client}/auth/invalid?reason=wrong_config`
133
136
  };
134
137
  }
138
+ const lastAuthorizedCode = this.getLastAuthorizedCode();
139
+ if (lastAuthorizedCode === code && (this.hasSession() || this.hasAuthCookies())) {
140
+ return { ok: true, url: this.config.redirectUrl };
141
+ }
142
+ const inFlight = callbackInFlight.get(code);
143
+ if (inFlight) return inFlight;
144
+ const authorizeRequest = (async () => {
145
+ try {
146
+ const { data } = await (0, import_core.authorizeCode)({ code, client_type: "server" });
147
+ const session = {
148
+ user: data.user
149
+ };
150
+ const isSecure = this.isSecureCookie();
151
+ this.cookies.set(
152
+ import_settings.SETTINGS.storage_keys.session,
153
+ JSON.stringify(session),
154
+ {
155
+ httpOnly: true,
156
+ sameSite: "lax",
157
+ secure: isSecure,
158
+ path: "/",
159
+ maxAge: data.refresh_expires_seconds - 60
160
+ }
161
+ );
162
+ this.setCookieTokens({
163
+ access_token: data.access_token,
164
+ access_expires_seconds: data.access_expires_seconds,
165
+ refresh_token: data.refresh_token,
166
+ refresh_expires_seconds: data.refresh_expires_seconds
167
+ });
168
+ this.setLastAuthorizedCode(code);
169
+ return { ok: true, url: this.config.redirectUrl };
170
+ } catch (error) {
171
+ if (this.isDuplicateCodeError(error) && (this.hasSession() || this.hasAuthCookies() || this.getLastAuthorizedCode() === code)) {
172
+ this.setLastAuthorizedCode(code);
173
+ return { ok: true, url: this.config.redirectUrl };
174
+ }
175
+ return {
176
+ ok: false,
177
+ url: `${import_settings.SETTINGS.domains.client}/auth/invalid?reason=wrong_config`
178
+ };
179
+ }
180
+ })();
181
+ callbackInFlight.set(code, authorizeRequest);
182
+ try {
183
+ return await authorizeRequest;
184
+ } finally {
185
+ callbackInFlight.delete(code);
186
+ }
135
187
  }
136
188
  async logout() {
137
189
  const { access } = this.getCookieTokens();
@@ -252,8 +304,8 @@ class AuditAuthNext {
252
304
  ;
253
305
  case "refresh":
254
306
  {
255
- const { ok: ok2 } = await this.refresh();
256
- if (ok2) return import_server.NextResponse.redirect(redirectUrl || this.config.redirectUrl);
307
+ const { ok } = await this.refresh();
308
+ if (ok) return import_server.NextResponse.redirect(redirectUrl || this.config.redirectUrl);
257
309
  const url = await (0, import_core.buildAuthUrl)({ apiKey: this.config.apiKey, redirectUrl: `${this.config.baseUrl}/api/auditauth/callback` });
258
310
  return import_server.NextResponse.redirect(url);
259
311
  }
@@ -313,8 +365,8 @@ class AuditAuthNext {
313
365
  case "refresh":
314
366
  {
315
367
  const redirectUrl = req.nextUrl.searchParams.get("redirectUrl");
316
- const { ok: ok2 } = await this.refresh();
317
- if (ok2) return import_server.NextResponse.redirect(redirectUrl || this.config.redirectUrl);
368
+ const { ok } = await this.refresh();
369
+ if (ok) return import_server.NextResponse.redirect(redirectUrl || this.config.redirectUrl);
318
370
  return new Response("Session expired", { status: 401 });
319
371
  }
320
372
  ;
package/dist/sdk.d.cts CHANGED
@@ -8,6 +8,11 @@ declare class AuditAuthNext {
8
8
  private cookies;
9
9
  constructor(config: AuditAuthConfig, cookies: CookieAdapter);
10
10
  private getCookieTokens;
11
+ private isSecureCookie;
12
+ private hasAuthCookies;
13
+ private getLastAuthorizedCode;
14
+ private setLastAuthorizedCode;
15
+ private isDuplicateCodeError;
11
16
  private setCookieTokens;
12
17
  private pushMetric;
13
18
  getSession(): SessionUser | null;
package/dist/sdk.d.ts CHANGED
@@ -8,6 +8,11 @@ declare class AuditAuthNext {
8
8
  private cookies;
9
9
  constructor(config: AuditAuthConfig, cookies: CookieAdapter);
10
10
  private getCookieTokens;
11
+ private isSecureCookie;
12
+ private hasAuthCookies;
13
+ private getLastAuthorizedCode;
14
+ private setLastAuthorizedCode;
15
+ private isDuplicateCodeError;
11
16
  private setCookieTokens;
12
17
  private pushMetric;
13
18
  getSession(): SessionUser | null;
package/dist/sdk.js CHANGED
@@ -10,6 +10,9 @@ import {
10
10
  sendMetrics
11
11
  } from "@auditauth/core";
12
12
  import { verifyRequest } from "@auditauth/node";
13
+ const CALLBACK_CODE_COOKIE = "auditauth_last_code";
14
+ const CALLBACK_CODE_TTL_SECONDS = 120;
15
+ const callbackInFlight = /* @__PURE__ */ new Map();
13
16
  class AuditAuthNext {
14
17
  constructor(config, cookies) {
15
18
  if (!config.appId) throw new Error("Missing appId");
@@ -28,8 +31,32 @@ class AuditAuthNext {
28
31
  refresh: this.cookies.get(SETTINGS.storage_keys.refresh)
29
32
  };
30
33
  }
34
+ isSecureCookie() {
35
+ return this.config.redirectUrl.startsWith("https://");
36
+ }
37
+ hasAuthCookies() {
38
+ const { access, refresh } = this.getCookieTokens();
39
+ return !!(access && refresh);
40
+ }
41
+ getLastAuthorizedCode() {
42
+ return this.cookies.get(CALLBACK_CODE_COOKIE);
43
+ }
44
+ setLastAuthorizedCode(code) {
45
+ this.cookies.set(CALLBACK_CODE_COOKIE, code, {
46
+ httpOnly: true,
47
+ sameSite: "lax",
48
+ secure: this.isSecureCookie(),
49
+ path: "/",
50
+ maxAge: CALLBACK_CODE_TTL_SECONDS
51
+ });
52
+ }
53
+ isDuplicateCodeError(error) {
54
+ if (!(error instanceof Error)) return false;
55
+ const message = error.message.toLowerCase();
56
+ return message.includes("code not found");
57
+ }
31
58
  setCookieTokens(params) {
32
- const isSecure = this.config.redirectUrl.includes("https");
59
+ const isSecure = this.isSecureCookie();
33
60
  this.cookies.set(
34
61
  SETTINGS.storage_keys.access,
35
62
  params.access_token,
@@ -86,36 +113,61 @@ class AuditAuthNext {
86
113
  }
87
114
  async callback(request) {
88
115
  const code = new URL(request.url).searchParams.get("code");
89
- try {
90
- const { data } = await authorizeCode({ code, client_type: "server" });
91
- const session = {
92
- user: data.user
93
- };
94
- const isSecure = this.config.redirectUrl.includes("http");
95
- this.cookies.set(
96
- SETTINGS.storage_keys.session,
97
- JSON.stringify(session),
98
- {
99
- httpOnly: true,
100
- sameSite: "lax",
101
- secure: isSecure,
102
- path: "/",
103
- maxAge: data.refresh_expires_seconds - 60
104
- }
105
- );
106
- this.setCookieTokens({
107
- access_token: data.access_token,
108
- access_expires_seconds: data.access_expires_seconds,
109
- refresh_token: data.refresh_token,
110
- refresh_expires_seconds: data.refresh_expires_seconds
111
- });
112
- return { ok: true, url: this.config.redirectUrl };
113
- } catch {
116
+ if (!code) {
114
117
  return {
115
118
  ok: false,
116
119
  url: `${SETTINGS.domains.client}/auth/invalid?reason=wrong_config`
117
120
  };
118
121
  }
122
+ const lastAuthorizedCode = this.getLastAuthorizedCode();
123
+ if (lastAuthorizedCode === code && (this.hasSession() || this.hasAuthCookies())) {
124
+ return { ok: true, url: this.config.redirectUrl };
125
+ }
126
+ const inFlight = callbackInFlight.get(code);
127
+ if (inFlight) return inFlight;
128
+ const authorizeRequest = (async () => {
129
+ try {
130
+ const { data } = await authorizeCode({ code, client_type: "server" });
131
+ const session = {
132
+ user: data.user
133
+ };
134
+ const isSecure = this.isSecureCookie();
135
+ this.cookies.set(
136
+ SETTINGS.storage_keys.session,
137
+ JSON.stringify(session),
138
+ {
139
+ httpOnly: true,
140
+ sameSite: "lax",
141
+ secure: isSecure,
142
+ path: "/",
143
+ maxAge: data.refresh_expires_seconds - 60
144
+ }
145
+ );
146
+ this.setCookieTokens({
147
+ access_token: data.access_token,
148
+ access_expires_seconds: data.access_expires_seconds,
149
+ refresh_token: data.refresh_token,
150
+ refresh_expires_seconds: data.refresh_expires_seconds
151
+ });
152
+ this.setLastAuthorizedCode(code);
153
+ return { ok: true, url: this.config.redirectUrl };
154
+ } catch (error) {
155
+ if (this.isDuplicateCodeError(error) && (this.hasSession() || this.hasAuthCookies() || this.getLastAuthorizedCode() === code)) {
156
+ this.setLastAuthorizedCode(code);
157
+ return { ok: true, url: this.config.redirectUrl };
158
+ }
159
+ return {
160
+ ok: false,
161
+ url: `${SETTINGS.domains.client}/auth/invalid?reason=wrong_config`
162
+ };
163
+ }
164
+ })();
165
+ callbackInFlight.set(code, authorizeRequest);
166
+ try {
167
+ return await authorizeRequest;
168
+ } finally {
169
+ callbackInFlight.delete(code);
170
+ }
119
171
  }
120
172
  async logout() {
121
173
  const { access } = this.getCookieTokens();
@@ -236,8 +288,8 @@ class AuditAuthNext {
236
288
  ;
237
289
  case "refresh":
238
290
  {
239
- const { ok: ok2 } = await this.refresh();
240
- if (ok2) return NextResponse.redirect(redirectUrl || this.config.redirectUrl);
291
+ const { ok } = await this.refresh();
292
+ if (ok) return NextResponse.redirect(redirectUrl || this.config.redirectUrl);
241
293
  const url = await buildAuthUrl({ apiKey: this.config.apiKey, redirectUrl: `${this.config.baseUrl}/api/auditauth/callback` });
242
294
  return NextResponse.redirect(url);
243
295
  }
@@ -297,8 +349,8 @@ class AuditAuthNext {
297
349
  case "refresh":
298
350
  {
299
351
  const redirectUrl = req.nextUrl.searchParams.get("redirectUrl");
300
- const { ok: ok2 } = await this.refresh();
301
- if (ok2) return NextResponse.redirect(redirectUrl || this.config.redirectUrl);
352
+ const { ok } = await this.refresh();
353
+ if (ok) return NextResponse.redirect(redirectUrl || this.config.redirectUrl);
302
354
  return new Response("Session expired", { status: 401 });
303
355
  }
304
356
  ;
@@ -18,7 +18,7 @@ declare const SETTINGS: {
18
18
  readonly client: "https://auditauth.com";
19
19
  } | {
20
20
  readonly api: "http://localhost:4000/v1";
21
- readonly client: "http://localhost:3000";
21
+ readonly client: "https://localhost:3000";
22
22
  };
23
23
  readonly storage_keys: {
24
24
  readonly access: "auditauth_access";
@@ -18,7 +18,7 @@ declare const SETTINGS: {
18
18
  readonly client: "https://auditauth.com";
19
19
  } | {
20
20
  readonly api: "http://localhost:4000/v1";
21
- readonly client: "http://localhost:3000";
21
+ readonly client: "https://localhost:3000";
22
22
  };
23
23
  readonly storage_keys: {
24
24
  readonly access: "auditauth_access";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@auditauth/next",
3
- "version": "0.2.0-beta.3",
3
+ "version": "0.2.0-beta.5",
4
4
  "description": "AuditAuth NextJS SDK",
5
5
  "license": "MIT",
6
6
  "author": "Nimibyte",
@@ -42,7 +42,7 @@
42
42
  "./package.json": "./package.json"
43
43
  },
44
44
  "scripts": {
45
- "build": "tsup",
45
+ "build": "tsup && node ../../scripts/fix-cjs-relative-imports.mjs dist",
46
46
  "dev": "tsup --watch",
47
47
  "clean": "rm -rf dist",
48
48
  "prepack": "npm run build"
@@ -53,8 +53,8 @@
53
53
  "react-dom": ">=18"
54
54
  },
55
55
  "dependencies": {
56
- "@auditauth/core": "^0.2.0-beta.3",
57
- "@auditauth/node": "^0.2.0-beta.3"
56
+ "@auditauth/core": "^0.2.0-beta.5",
57
+ "@auditauth/node": "^0.2.0-beta.5"
58
58
  },
59
59
  "devDependencies": {
60
60
  "typescript": "^5.9.0",