@auditauth/next 0.2.0-beta.4 → 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.
- package/dist/sdk.cjs +82 -30
- package/dist/sdk.d.cts +5 -0
- package/dist/sdk.d.ts +5 -0
- package/dist/sdk.js +82 -30
- package/dist/settings.d.cts +3 -3
- package/dist/settings.d.ts +3 -3
- package/package.json +3 -3
package/dist/sdk.cjs
CHANGED
|
@@ -26,6 +26,9 @@ var import_server = require("next/server.js");
|
|
|
26
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.
|
|
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
|
-
|
|
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
|
|
256
|
-
if (
|
|
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
|
|
317
|
-
if (
|
|
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.
|
|
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
|
-
|
|
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
|
|
240
|
-
if (
|
|
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
|
|
301
|
-
if (
|
|
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
|
;
|
package/dist/settings.d.cts
CHANGED
|
@@ -14,11 +14,11 @@ declare const SETTINGS: {
|
|
|
14
14
|
readonly jwt_public_key: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2EYs4Q9OyjNuAEPqb4j\nIzc52JdfVcNvEbG43Xp8B2kI9QxwRyX7rtFSwKowj3W1BlCLaTIMK3TafWOf9QwH\nfemuL9Ni37PFcGptzpyuoCYYA650EuD82PENcO49lsObvty2cuXxQszbPPvAecm4\nJ/XG70td/W1UwbjAJcdmp8ktZGYR0JXM37hYA9Xq/aKwu7d0FTL6WdKTvt3L5VxL\nF6WNyLs65ZSbu+j8UEkwmoJ9h9Y0mLQmFtmkoh/HWOFyFDnBNiJX0vRb++RhJw6w\ncrSbqpbTu7z4vIep5lgSOut39P273SVTQZ3cGQIS+605Ur5wjkkSzzaJV1QLBBR9\nAQIDAQAB\n-----END PUBLIC KEY-----\n";
|
|
15
15
|
readonly jwt_issuer: "https://api.auditauth.com";
|
|
16
16
|
readonly domains: {
|
|
17
|
-
readonly api: "http://localhost:4000/v1";
|
|
18
|
-
readonly client: "http://localhost:3000";
|
|
19
|
-
} | {
|
|
20
17
|
readonly api: "https://api.auditauth.com/v1";
|
|
21
18
|
readonly client: "https://auditauth.com";
|
|
19
|
+
} | {
|
|
20
|
+
readonly api: "http://localhost:4000/v1";
|
|
21
|
+
readonly client: "https://localhost:3000";
|
|
22
22
|
};
|
|
23
23
|
readonly storage_keys: {
|
|
24
24
|
readonly access: "auditauth_access";
|
package/dist/settings.d.ts
CHANGED
|
@@ -14,11 +14,11 @@ declare const SETTINGS: {
|
|
|
14
14
|
readonly jwt_public_key: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs2EYs4Q9OyjNuAEPqb4j\nIzc52JdfVcNvEbG43Xp8B2kI9QxwRyX7rtFSwKowj3W1BlCLaTIMK3TafWOf9QwH\nfemuL9Ni37PFcGptzpyuoCYYA650EuD82PENcO49lsObvty2cuXxQszbPPvAecm4\nJ/XG70td/W1UwbjAJcdmp8ktZGYR0JXM37hYA9Xq/aKwu7d0FTL6WdKTvt3L5VxL\nF6WNyLs65ZSbu+j8UEkwmoJ9h9Y0mLQmFtmkoh/HWOFyFDnBNiJX0vRb++RhJw6w\ncrSbqpbTu7z4vIep5lgSOut39P273SVTQZ3cGQIS+605Ur5wjkkSzzaJV1QLBBR9\nAQIDAQAB\n-----END PUBLIC KEY-----\n";
|
|
15
15
|
readonly jwt_issuer: "https://api.auditauth.com";
|
|
16
16
|
readonly domains: {
|
|
17
|
-
readonly api: "http://localhost:4000/v1";
|
|
18
|
-
readonly client: "http://localhost:3000";
|
|
19
|
-
} | {
|
|
20
17
|
readonly api: "https://api.auditauth.com/v1";
|
|
21
18
|
readonly client: "https://auditauth.com";
|
|
19
|
+
} | {
|
|
20
|
+
readonly api: "http://localhost:4000/v1";
|
|
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
|
+
"version": "0.2.0-beta.5",
|
|
4
4
|
"description": "AuditAuth NextJS SDK",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Nimibyte",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"react-dom": ">=18"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@auditauth/core": "^0.2.0-beta.
|
|
57
|
-
"@auditauth/node": "^0.2.0-beta.
|
|
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",
|