@hexclave/js 1.0.20 → 1.0.22
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 +187 -7
- package/dist/esm/lib/auth.d.ts.map +1 -1
- package/dist/esm/lib/auth.js +32 -11
- package/dist/esm/lib/auth.js.map +1 -1
- package/dist/esm/lib/auth.test.js +25 -10
- package/dist/esm/lib/auth.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.js +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +54 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +4 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +28 -4
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.js +17 -13
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.test.js +4 -8
- package/dist/esm/lib/hexclave-app/apps/implementations/event-tracker.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.d.ts +3 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.js +19 -13
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.test.js +4 -9
- package/dist/esm/lib/hexclave-app/apps/implementations/session-replay.test.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/admin-app.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/interfaces/admin-app.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/admin-app.js.map +1 -1
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +31 -10
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/auth.test.js +23 -8
- package/dist/lib/auth.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js +1 -0
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js +54 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +4 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +27 -3
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/common.js +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.js +16 -12
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.test.js +4 -8
- package/dist/lib/hexclave-app/apps/implementations/event-tracker.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.d.ts +3 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.js +19 -12
- package/dist/lib/hexclave-app/apps/implementations/session-replay.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/session-replay.test.js +4 -9
- package/dist/lib/hexclave-app/apps/implementations/session-replay.test.js.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.js.map +1 -1
- package/package.json +2 -2
- package/src/lib/auth.test.ts +32 -10
- package/src/lib/auth.ts +41 -7
- package/src/lib/hexclave-app/apps/implementations/admin-app-impl.ts +2 -1
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.cross-domain.test.ts +66 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +39 -3
- package/src/lib/hexclave-app/apps/implementations/event-tracker.test.ts +5 -13
- package/src/lib/hexclave-app/apps/implementations/event-tracker.ts +19 -14
- package/src/lib/hexclave-app/apps/implementations/session-replay.test.ts +4 -20
- package/src/lib/hexclave-app/apps/implementations/session-replay.ts +19 -12
- package/src/lib/hexclave-app/apps/interfaces/admin-app.ts +1 -0
package/README.md
CHANGED
|
@@ -1,10 +1,190 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Hexclave
|
|
2
|
+
<div align="center">
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
<img src=".github/assets/hexclave-header.svg" alt="Hexclave" width="320"/>
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
- @hexclave/react
|
|
7
|
-
- @hexclave/js
|
|
8
|
-
- @hexclave/cli
|
|
6
|
+
<br/>
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
**The user infrastructure platform.**
|
|
9
|
+
|
|
10
|
+
Hexclave handles everything around your users: authentication, teams,
|
|
11
|
+
payments, emails, analytics, and much more. Start in minutes on the hosted
|
|
12
|
+
cloud. Your data is always yours to export and self-host.
|
|
13
|
+
|
|
14
|
+
[Website](https://hexclave.com) · [Docs](https://docs.hexclave.com) · [Dashboard](https://app.hexclave.com) · [Discord](https://discord.hexclave.com)
|
|
15
|
+
|
|
16
|
+

|
|
17
|
+

|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
<div align="center">
|
|
25
|
+
<img src=".github/assets/comparison.png" alt="Where Hexclave fits in the infrastructure stack" width="900"/>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
## Get started
|
|
29
|
+
|
|
30
|
+
Setting up Hexclave is one prompt. Paste this into your coding agent of choice:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
Read skill.hexclave.com and help me setup hexclave in this project
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## For AI agents
|
|
37
|
+
|
|
38
|
+
If you're an AI coding agent, use your web fetch tool or curl the skill documentation for up-to-date integration instructions:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
curl -sSL "https://skill.hexclave.com?question=<your-question>&context=<your-context>"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## What's included
|
|
45
|
+
|
|
46
|
+
Hexclave ships as a catalog of apps you switch on as your product needs them.
|
|
47
|
+
Each one is built on the same user model, and new apps land regularly.
|
|
48
|
+
|
|
49
|
+
<table><tr>
|
|
50
|
+
<td width="50%" valign="middle">
|
|
51
|
+
|
|
52
|
+
### <img src=".github/assets/logos/authentication.png" alt="" width="40" align="top"/> Authentication
|
|
53
|
+
|
|
54
|
+
Authentication that just works with passkeys, OAuth, and CLI auth. Drop in one component and ship the whole flow; auth methods toggle from the dashboard with no code changes needed.
|
|
55
|
+
|
|
56
|
+
</td>
|
|
57
|
+
<td width="50%" valign="middle" align="center">
|
|
58
|
+
<img src=".github/assets/app-shots/authentication.png" width="520" alt="Authentication"/>
|
|
59
|
+
</td>
|
|
60
|
+
</tr></table>
|
|
61
|
+
|
|
62
|
+
<table><tr>
|
|
63
|
+
<td width="50%" valign="middle">
|
|
64
|
+
|
|
65
|
+
### <img src=".github/assets/logos/teams.png" alt="" width="40" align="top"/> Teams
|
|
66
|
+
|
|
67
|
+
Build for teams, not just users, with workspaces, email invites, and roles that actually gate the work. The workspace switcher remembers selection, invites auto sign up new users, and permissions hold up under audit.
|
|
68
|
+
|
|
69
|
+
</td>
|
|
70
|
+
<td width="50%" valign="middle" align="center">
|
|
71
|
+
<img src=".github/assets/app-shots/teams.png" width="520" alt="Teams"/>
|
|
72
|
+
</td>
|
|
73
|
+
</tr></table>
|
|
74
|
+
|
|
75
|
+
<table><tr>
|
|
76
|
+
<td width="50%" valign="middle">
|
|
77
|
+
|
|
78
|
+
### <img src=".github/assets/logos/rbac.png" alt="" width="40" align="top"/> RBAC
|
|
79
|
+
|
|
80
|
+
Permissions, sorted: roles that nest and one permission check that works the same on server or client. Define them in the dashboard, check them anywhere in your code.
|
|
81
|
+
|
|
82
|
+
</td>
|
|
83
|
+
<td width="50%" valign="middle" align="center">
|
|
84
|
+
<img src=".github/assets/app-shots/rbac.png" width="520" alt="RBAC"/>
|
|
85
|
+
</td>
|
|
86
|
+
</tr></table>
|
|
87
|
+
|
|
88
|
+
<table><tr>
|
|
89
|
+
<td width="50%" valign="middle">
|
|
90
|
+
|
|
91
|
+
### <img src=".github/assets/logos/api-keys.png" alt="" width="40" align="top"/> API Keys
|
|
92
|
+
|
|
93
|
+
API keys without the footguns: leaked keys get auto-revoked, work for users and teams, and show the full secret only once. We never keep the plaintext after creation.
|
|
94
|
+
|
|
95
|
+
</td>
|
|
96
|
+
<td width="50%" valign="middle" align="center">
|
|
97
|
+
<img src=".github/assets/app-shots/api-keys.png" width="520" alt="API Keys"/>
|
|
98
|
+
</td>
|
|
99
|
+
</tr></table>
|
|
100
|
+
|
|
101
|
+
<table><tr>
|
|
102
|
+
<td width="50%" valign="middle">
|
|
103
|
+
|
|
104
|
+
### <img src=".github/assets/logos/payments.png" alt="" width="40" align="top"/> Payments
|
|
105
|
+
|
|
106
|
+
Payments without the plumbing for subscriptions, one-time charges, and usage metering with credits. Bill a person or a whole team with one model, no separate codepath.
|
|
107
|
+
|
|
108
|
+
</td>
|
|
109
|
+
<td width="50%" valign="middle" align="center">
|
|
110
|
+
<img src=".github/assets/app-shots/payments.png" width="520" alt="Payments"/>
|
|
111
|
+
</td>
|
|
112
|
+
</tr></table>
|
|
113
|
+
|
|
114
|
+
<table><tr>
|
|
115
|
+
<td width="50%" valign="middle">
|
|
116
|
+
|
|
117
|
+
### <img src=".github/assets/logos/emails.png" alt="" width="40" align="top"/> Emails
|
|
118
|
+
|
|
119
|
+
Email that delivers and tells you so, handling transactional and marketing sends from one API. Edit templates with an AI editor, theme once, and track every open and click.
|
|
120
|
+
|
|
121
|
+
</td>
|
|
122
|
+
<td width="50%" valign="middle" align="center">
|
|
123
|
+
<img src=".github/assets/app-shots/emails.png" width="520" alt="Emails"/>
|
|
124
|
+
</td>
|
|
125
|
+
</tr></table>
|
|
126
|
+
|
|
127
|
+
<table><tr>
|
|
128
|
+
<td width="50%" valign="middle">
|
|
129
|
+
|
|
130
|
+
### <img src=".github/assets/logos/analytics.png" alt="" width="40" align="top"/> Analytics
|
|
131
|
+
|
|
132
|
+
Know your users with no data stack required, with live active user counts and session replays out of the box. Ask in plain English to build dashboards or write SQL to save queries, all with one flag enabled.
|
|
133
|
+
|
|
134
|
+
</td>
|
|
135
|
+
<td width="50%" valign="middle" align="center">
|
|
136
|
+
<img src=".github/assets/app-shots/analytics.png" width="520" alt="Analytics"/>
|
|
137
|
+
</td>
|
|
138
|
+
</tr></table>
|
|
139
|
+
|
|
140
|
+
<table><tr>
|
|
141
|
+
<td width="50%" valign="middle">
|
|
142
|
+
|
|
143
|
+
### <img src=".github/assets/logos/webhooks.png" alt="" width="40" align="top"/> Webhooks
|
|
144
|
+
|
|
145
|
+
React to every user event in real time with signed, tamper-proof webhooks. Retries and backoff are handled for you; verify in five lines and manage endpoints from the dashboard.
|
|
146
|
+
|
|
147
|
+
</td>
|
|
148
|
+
<td width="50%" valign="middle" align="center">
|
|
149
|
+
<img src=".github/assets/app-shots/webhooks.png" width="520" alt="Webhooks"/>
|
|
150
|
+
</td>
|
|
151
|
+
</tr></table>
|
|
152
|
+
|
|
153
|
+
<table><tr>
|
|
154
|
+
<td width="50%" valign="middle">
|
|
155
|
+
|
|
156
|
+
### <img src=".github/assets/logos/data-vault.png" alt="" width="40" align="top"/> Data Vault
|
|
157
|
+
|
|
158
|
+
A safe for the secrets your users hand you, locked with your secret so we never see the plaintext. Store and retrieve tokens in two lines each, server-only by design.
|
|
159
|
+
|
|
160
|
+
</td>
|
|
161
|
+
<td width="50%" valign="middle" align="center">
|
|
162
|
+
<img src=".github/assets/app-shots/data-vault.png" width="520" alt="Data Vault"/>
|
|
163
|
+
</td>
|
|
164
|
+
</tr></table>
|
|
165
|
+
|
|
166
|
+
<table><tr>
|
|
167
|
+
<td width="50%" valign="middle">
|
|
168
|
+
|
|
169
|
+
### <img src=".github/assets/logos/launch-checklist.png" alt="" width="40" align="top"/> Launch Checklist
|
|
170
|
+
|
|
171
|
+
Run through the must-do checks before flipping to production: domain setup, callbacks locked, secrets rotated. The progress tracker keeps your team aligned so nothing critical slips through on launch day.
|
|
172
|
+
|
|
173
|
+
</td>
|
|
174
|
+
<td width="50%" valign="middle" align="center">
|
|
175
|
+
<img src=".github/assets/app-shots/launch-checklist.png" width="520" alt="Launch Checklist"/>
|
|
176
|
+
</td>
|
|
177
|
+
</tr></table>
|
|
178
|
+
|
|
179
|
+
## Contributing
|
|
180
|
+
|
|
181
|
+
Hexclave is open source, and contributions are welcome. Read
|
|
182
|
+
[`CONTRIBUTING.md`](./CONTRIBUTING.md) to get started, and say hello in
|
|
183
|
+
[Discord](https://discord.hexclave.com) before picking up anything large.
|
|
184
|
+
Found a security issue? Email security@hexclave.com.
|
|
185
|
+
|
|
186
|
+
## ❤ Contributors
|
|
187
|
+
|
|
188
|
+
<a href="https://github.com/hexclave/hexclave/graphs/contributors">
|
|
189
|
+
<img src="https://contrib.rocks/image?repo=hexclave/hexclave&columns=9" alt="Contributors" width="100%" />
|
|
190
|
+
</a>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/lib/auth.ts"],"mappings":";;;;iBAWsB,6BAAA,CACpB,KAAA,EAAO,uBAAA,EACP,OAAA;EACE,QAAA;EACA,WAAA;EACA,gBAAA;EACA,aAAA;AAAA,GAEF,OAAA,EAAS,eAAA,GACR,OAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../../src/lib/auth.ts"],"mappings":";;;;iBAWsB,6BAAA,CACpB,KAAA,EAAO,uBAAA,EACP,OAAA;EACE,QAAA;EACA,WAAA;EACA,gBAAA;EACA,aAAA;AAAA,GAEF,OAAA,EAAS,eAAA,GACR,OAAA;AAAA,iBA4JmB,iBAAA,CACpB,KAAA,EAAO,uBAAA,EACP,WAAA,UACA,OAAA;EACE,+BAAA;AAAA,IACD,OAAA"}
|
package/dist/esm/lib/auth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { deindent } from "@hexclave/shared/dist/utils/strings";
|
|
2
2
|
import { HexclaveAssertionError, throwErr } from "@hexclave/shared/dist/utils/errors";
|
|
3
|
-
import { KnownError } from "@hexclave/shared";
|
|
3
|
+
import { KnownError, KnownErrors } from "@hexclave/shared";
|
|
4
4
|
import { Result } from "@hexclave/shared/dist/utils/results";
|
|
5
5
|
import { constructRedirectUrl } from "../utils/url.js";
|
|
6
6
|
import { consumeVerifierAndStateCookie, saveVerifierAndState } from "./cookie.js";
|
|
@@ -20,14 +20,29 @@ async function getNewOAuthProviderOrScopeUrl(iface, options, session) {
|
|
|
20
20
|
providerScope: options.providerScope
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
|
+
const oauthErrorParams = [
|
|
24
|
+
"error",
|
|
25
|
+
"error_description",
|
|
26
|
+
"errorCode",
|
|
27
|
+
"message",
|
|
28
|
+
"details"
|
|
29
|
+
];
|
|
30
|
+
function removeOAuthErrorParamsFromHistory(originalUrl) {
|
|
31
|
+
const newUrl = new URL(originalUrl);
|
|
32
|
+
for (const param of oauthErrorParams) newUrl.searchParams.delete(param);
|
|
33
|
+
window.history.replaceState({}, "", newUrl.toString());
|
|
34
|
+
}
|
|
35
|
+
function getProviderOAuthErrorFromUrl(originalUrl) {
|
|
36
|
+
const providerError = originalUrl.searchParams.get("error");
|
|
37
|
+
const providerErrorDescription = originalUrl.searchParams.get("error_description");
|
|
38
|
+
if (providerError == null && providerErrorDescription == null) return null;
|
|
39
|
+
switch (providerError) {
|
|
40
|
+
case "access_denied":
|
|
41
|
+
case "consent_required": return new KnownErrors.OAuthProviderAccessDenied();
|
|
42
|
+
default: return new KnownErrors.OAuthProviderTemporarilyUnavailable();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
23
45
|
function consumeOAuthCallbackQueryParams(options) {
|
|
24
|
-
const oauthErrorParams = [
|
|
25
|
-
"error",
|
|
26
|
-
"error_description",
|
|
27
|
-
"errorCode",
|
|
28
|
-
"message",
|
|
29
|
-
"details"
|
|
30
|
-
];
|
|
31
46
|
const requiredParams = ["code", "state"];
|
|
32
47
|
const originalUrl = new URL(window.location.href);
|
|
33
48
|
const knownErrorCode = originalUrl.searchParams.get("errorCode");
|
|
@@ -43,9 +58,7 @@ function consumeOAuthCallbackQueryParams(options) {
|
|
|
43
58
|
cause: error
|
|
44
59
|
});
|
|
45
60
|
}
|
|
46
|
-
|
|
47
|
-
for (const param of oauthErrorParams) newUrl.searchParams.delete(param);
|
|
48
|
-
window.history.replaceState({}, "", newUrl.toString());
|
|
61
|
+
removeOAuthErrorParamsFromHistory(originalUrl);
|
|
49
62
|
return {
|
|
50
63
|
type: "known-error",
|
|
51
64
|
error: KnownError.fromJson({
|
|
@@ -55,6 +68,14 @@ function consumeOAuthCallbackQueryParams(options) {
|
|
|
55
68
|
})
|
|
56
69
|
};
|
|
57
70
|
}
|
|
71
|
+
const providerOAuthError = getProviderOAuthErrorFromUrl(originalUrl);
|
|
72
|
+
if (providerOAuthError != null && !requiredParams.every((param) => originalUrl.searchParams.has(param))) {
|
|
73
|
+
removeOAuthErrorParamsFromHistory(originalUrl);
|
|
74
|
+
return {
|
|
75
|
+
type: "known-error",
|
|
76
|
+
error: providerOAuthError
|
|
77
|
+
};
|
|
78
|
+
}
|
|
58
79
|
for (const param of requiredParams) if (!originalUrl.searchParams.has(param)) {
|
|
59
80
|
if (!options?.dontWarnAboutMissingQueryParams) console.warn(/* @__PURE__ */ new Error(`Missing required query parameter on OAuth callback: ${param}. Maybe you opened or reloaded the oauth-callback page from your history?`));
|
|
60
81
|
return null;
|
package/dist/esm/lib/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","names":[],"sources":["../../../src/lib/auth.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownError, HexclaveClientInterface } from \"@hexclave/shared\";\nimport { InternalSession } from \"@hexclave/shared/dist/sessions\";\nimport { HexclaveAssertionError, throwErr } from \"@hexclave/shared/dist/utils/errors\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { deindent } from \"@hexclave/shared/dist/utils/strings\";\nimport { constructRedirectUrl } from \"../utils/url\";\nimport { consumeVerifierAndStateCookie, saveVerifierAndState } from \"./cookie\";\nexport async function getNewOAuthProviderOrScopeUrl(\n iface: HexclaveClientInterface,\n options: {\n provider: string,\n redirectUrl: string,\n errorRedirectUrl: string,\n providerScope?: string,\n },\n session: InternalSession,\n): Promise<string> {\n const { codeChallenge, state } = await saveVerifierAndState();\n return await iface.getOAuthUrl({\n provider: options.provider,\n redirectUrl: constructRedirectUrl(options.redirectUrl, \"redirectUrl\"),\n errorRedirectUrl: constructRedirectUrl(options.errorRedirectUrl, \"errorRedirectUrl\"),\n afterCallbackRedirectUrl: constructRedirectUrl(window.location.href, \"afterCallbackRedirectUrl\"),\n codeChallenge,\n state,\n type: \"link\",\n session,\n providerScope: options.providerScope,\n });\n}\n\n/**\n * Checks if the current URL has the query parameters for an OAuth callback, and if so, removes them.\n *\n * Must be synchronous for the logic in callOAuthCallback to work without race conditions.\n */\ntype OAuthCallbackConsumptionResult =\n | {\n type: \"oauth-response\",\n originalUrl: URL,\n codeVerifier: string,\n state: string,\n }\n | {\n type: \"known-error\",\n error: KnownError,\n };\n\nfunction consumeOAuthCallbackQueryParams(options?: {\n dontWarnAboutMissingQueryParams?: boolean,\n}): OAuthCallbackConsumptionResult | null {\n const oauthErrorParams = [\"error\", \"error_description\", \"errorCode\", \"message\", \"details\"] as const;\n const requiredParams = [\"code\", \"state\"];\n const originalUrl = new URL(window.location.href);\n const knownErrorCode = originalUrl.searchParams.get(\"errorCode\");\n const knownErrorMessage = originalUrl.searchParams.get(\"message\");\n if (knownErrorCode && knownErrorMessage) {\n const details = originalUrl.searchParams.get(\"details\");\n let detailsJson = {};\n if (details) {\n try {\n detailsJson = JSON.parse(details);\n } catch (error) {\n throw new HexclaveAssertionError(\"OAuth callback returned malformed known-error details\", {\n details,\n cause: error,\n });\n }\n }\n\n const newUrl = new URL(originalUrl);\n for (const param of oauthErrorParams) {\n newUrl.searchParams.delete(param);\n }\n window.history.replaceState({}, \"\", newUrl.toString());\n\n return {\n type: \"known-error\",\n error: KnownError.fromJson({\n code: knownErrorCode,\n message: knownErrorMessage,\n details: detailsJson,\n }),\n };\n }\n\n for (const param of requiredParams) {\n if (!originalUrl.searchParams.has(param)) {\n if (!options?.dontWarnAboutMissingQueryParams) {\n console.warn(new Error(`Missing required query parameter on OAuth callback: ${param}. Maybe you opened or reloaded the oauth-callback page from your history?`));\n }\n return null;\n }\n }\n\n const expectedState = originalUrl.searchParams.get(\"state\") ?? throwErr(\"This should never happen; isn't state required above?\");\n const cookieResult = consumeVerifierAndStateCookie(expectedState);\n\n if (!cookieResult) {\n // If the state can't be found in the cookies, then the callback wasn't meant for us.\n // Maybe the website uses another OAuth library?\n console.warn(deindent`\n Stack found an outer OAuth callback state in the query parameters, but not in cookies.\n\n This could have multiple reasons:\n - The cookie expired, because the OAuth flow took too long.\n - The user's browser deleted the cookie, either manually or because of a very strict cookie policy.\n - The cookie was already consumed by this page, and the user already logged in.\n - You are using another OAuth client library with the same callback URL as Stack.\n - The user opened the OAuth callback page from their history.\n\n Either way, it is probably safe to ignore this warning unless you are debugging an OAuth issue.\n `);\n return null;\n }\n\n\n const newUrl = new URL(originalUrl);\n for (const param of requiredParams) {\n newUrl.searchParams.delete(param);\n }\n\n // let's get rid of the authorization code in the history as we\n // don't redirect to `redirectUrl` if there's a validation error\n // (as the redirectUrl might be malicious!).\n //\n // We use history.replaceState instead of location.assign(...) to\n // prevent an unnecessary reload\n window.history.replaceState({}, \"\", newUrl.toString());\n\n return {\n type: \"oauth-response\",\n originalUrl,\n codeVerifier: cookieResult.codeVerifier,\n state: expectedState,\n };\n}\n\nexport async function callOAuthCallback(\n iface: HexclaveClientInterface,\n redirectUrl: string,\n options?: {\n dontWarnAboutMissingQueryParams?: boolean,\n },\n) {\n // note: this part of the function (until the return) needs\n // to be synchronous, to prevent race conditions when\n // callOAuthCallback is called multiple times in parallel\n const consumed = consumeOAuthCallbackQueryParams(options);\n if (!consumed) return Result.ok(undefined);\n if (consumed.type === \"known-error\") {\n throw consumed.error;\n }\n\n // the rest can be asynchronous (we now know that we are the\n // intended recipient of the callback, and the only instance\n // of callOAuthCallback that's running)\n try {\n return Result.ok(await iface.callOAuthCallback({\n oauthParams: consumed.originalUrl.searchParams,\n redirectUri: constructRedirectUrl(redirectUrl, \"redirectUri\"),\n codeVerifier: consumed.codeVerifier,\n state: consumed.state,\n }));\n } catch (e) {\n if (KnownError.isKnownError(e)) {\n throw e;\n }\n throw new HexclaveAssertionError(\"Error signing in during OAuth callback. Please try again.\", { cause: e });\n }\n}\n"],"mappings":";;;;;;;;AAWA,eAAsB,8BACpB,OACA,SAMA,SACiB;CACjB,MAAM,EAAE,eAAe,UAAU,MAAM,sBAAsB;AAC7D,QAAO,MAAM,MAAM,YAAY;EAC7B,UAAU,QAAQ;EAClB,aAAa,qBAAqB,QAAQ,aAAa,cAAc;EACrE,kBAAkB,qBAAqB,QAAQ,kBAAkB,mBAAmB;EACpF,0BAA0B,qBAAqB,OAAO,SAAS,MAAM,2BAA2B;EAChG;EACA;EACA,MAAM;EACN;EACA,eAAe,QAAQ;EACxB,CAAC;;AAoBJ,SAAS,gCAAgC,SAEC;CACxC,MAAM,mBAAmB;EAAC;EAAS;EAAqB;EAAa;EAAW;EAAU;CAC1F,MAAM,iBAAiB,CAAC,QAAQ,QAAQ;CACxC,MAAM,cAAc,IAAI,IAAI,OAAO,SAAS,KAAK;CACjD,MAAM,iBAAiB,YAAY,aAAa,IAAI,YAAY;CAChE,MAAM,oBAAoB,YAAY,aAAa,IAAI,UAAU;AACjE,KAAI,kBAAkB,mBAAmB;EACvC,MAAM,UAAU,YAAY,aAAa,IAAI,UAAU;EACvD,IAAI,cAAc,EAAE;AACpB,MAAI,QACF,KAAI;AACF,iBAAc,KAAK,MAAM,QAAQ;WAC1B,OAAO;AACd,SAAM,IAAI,uBAAuB,yDAAyD;IACxF;IACA,OAAO;IACR,CAAC;;EAIN,MAAM,SAAS,IAAI,IAAI,YAAY;AACnC,OAAK,MAAM,SAAS,iBAClB,QAAO,aAAa,OAAO,MAAM;AAEnC,SAAO,QAAQ,aAAa,EAAE,EAAE,IAAI,OAAO,UAAU,CAAC;AAEtD,SAAO;GACL,MAAM;GACN,OAAO,WAAW,SAAS;IACzB,MAAM;IACN,SAAS;IACT,SAAS;IACV,CAAC;GACH;;AAGH,MAAK,MAAM,SAAS,eAClB,KAAI,CAAC,YAAY,aAAa,IAAI,MAAM,EAAE;AACxC,MAAI,CAAC,SAAS,gCACZ,SAAQ,qBAAK,IAAI,MAAM,uDAAuD,MAAM,2EAA2E,CAAC;AAElK,SAAO;;CAIX,MAAM,gBAAgB,YAAY,aAAa,IAAI,QAAQ,IAAI,SAAS,wDAAwD;CAChI,MAAM,eAAe,8BAA8B,cAAc;AAEjE,KAAI,CAAC,cAAc;AAGjB,UAAQ,KAAK,QAAQ;;;;;;;;;;;MAWnB;AACF,SAAO;;CAIT,MAAM,SAAS,IAAI,IAAI,YAAY;AACnC,MAAK,MAAM,SAAS,eAClB,QAAO,aAAa,OAAO,MAAM;AASnC,QAAO,QAAQ,aAAa,EAAE,EAAE,IAAI,OAAO,UAAU,CAAC;AAEtD,QAAO;EACL,MAAM;EACN;EACA,cAAc,aAAa;EAC3B,OAAO;EACR;;AAGH,eAAsB,kBACpB,OACA,aACA,SAGA;CAIA,MAAM,WAAW,gCAAgC,QAAQ;AACzD,KAAI,CAAC,SAAU,QAAO,OAAO,GAAG,OAAU;AAC1C,KAAI,SAAS,SAAS,cACpB,OAAM,SAAS;AAMjB,KAAI;AACF,SAAO,OAAO,GAAG,MAAM,MAAM,kBAAkB;GAC7C,aAAa,SAAS,YAAY;GAClC,aAAa,qBAAqB,aAAa,cAAc;GAC7D,cAAc,SAAS;GACvB,OAAO,SAAS;GACjB,CAAC,CAAC;UACI,GAAG;AACV,MAAI,WAAW,aAAa,EAAE,CAC5B,OAAM;AAER,QAAM,IAAI,uBAAuB,6DAA6D,EAAE,OAAO,GAAG,CAAC"}
|
|
1
|
+
{"version":3,"file":"auth.js","names":[],"sources":["../../../src/lib/auth.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { KnownError, KnownErrors, HexclaveClientInterface } from \"@hexclave/shared\";\nimport { InternalSession } from \"@hexclave/shared/dist/sessions\";\nimport { HexclaveAssertionError, throwErr } from \"@hexclave/shared/dist/utils/errors\";\nimport { Result } from \"@hexclave/shared/dist/utils/results\";\nimport { deindent } from \"@hexclave/shared/dist/utils/strings\";\nimport { constructRedirectUrl } from \"../utils/url\";\nimport { consumeVerifierAndStateCookie, saveVerifierAndState } from \"./cookie\";\nexport async function getNewOAuthProviderOrScopeUrl(\n iface: HexclaveClientInterface,\n options: {\n provider: string,\n redirectUrl: string,\n errorRedirectUrl: string,\n providerScope?: string,\n },\n session: InternalSession,\n): Promise<string> {\n const { codeChallenge, state } = await saveVerifierAndState();\n return await iface.getOAuthUrl({\n provider: options.provider,\n redirectUrl: constructRedirectUrl(options.redirectUrl, \"redirectUrl\"),\n errorRedirectUrl: constructRedirectUrl(options.errorRedirectUrl, \"errorRedirectUrl\"),\n afterCallbackRedirectUrl: constructRedirectUrl(window.location.href, \"afterCallbackRedirectUrl\"),\n codeChallenge,\n state,\n type: \"link\",\n session,\n providerScope: options.providerScope,\n });\n}\n\n/**\n * Checks if the current URL has the query parameters for an OAuth callback, and if so, removes them.\n *\n * Must be synchronous for the logic in callOAuthCallback to work without race conditions.\n */\ntype OAuthCallbackConsumptionResult =\n | {\n type: \"oauth-response\",\n originalUrl: URL,\n codeVerifier: string,\n state: string,\n }\n | {\n type: \"known-error\",\n error: KnownError,\n };\n\nconst oauthErrorParams = [\"error\", \"error_description\", \"errorCode\", \"message\", \"details\"] as const;\n\nfunction removeOAuthErrorParamsFromHistory(originalUrl: URL): void {\n const newUrl = new URL(originalUrl);\n for (const param of oauthErrorParams) {\n newUrl.searchParams.delete(param);\n }\n window.history.replaceState({}, \"\", newUrl.toString());\n}\n\nfunction getProviderOAuthErrorFromUrl(originalUrl: URL): KnownError | null {\n const providerError = originalUrl.searchParams.get(\"error\");\n const providerErrorDescription = originalUrl.searchParams.get(\"error_description\");\n if (providerError == null && providerErrorDescription == null) {\n return null;\n }\n\n switch (providerError) {\n case \"access_denied\":\n case \"consent_required\": {\n return new KnownErrors.OAuthProviderAccessDenied();\n }\n case \"server_error\":\n case \"temporarily_unavailable\":\n default: {\n return new KnownErrors.OAuthProviderTemporarilyUnavailable();\n }\n }\n}\n\nfunction consumeOAuthCallbackQueryParams(options?: {\n dontWarnAboutMissingQueryParams?: boolean,\n}): OAuthCallbackConsumptionResult | null {\n const requiredParams = [\"code\", \"state\"];\n const originalUrl = new URL(window.location.href);\n const knownErrorCode = originalUrl.searchParams.get(\"errorCode\");\n const knownErrorMessage = originalUrl.searchParams.get(\"message\");\n if (knownErrorCode && knownErrorMessage) {\n const details = originalUrl.searchParams.get(\"details\");\n let detailsJson = {};\n if (details) {\n try {\n detailsJson = JSON.parse(details);\n } catch (error) {\n throw new HexclaveAssertionError(\"OAuth callback returned malformed known-error details\", {\n details,\n cause: error,\n });\n }\n }\n\n removeOAuthErrorParamsFromHistory(originalUrl);\n\n return {\n type: \"known-error\",\n error: KnownError.fromJson({\n code: knownErrorCode,\n message: knownErrorMessage,\n details: detailsJson,\n }),\n };\n }\n\n const providerOAuthError = getProviderOAuthErrorFromUrl(originalUrl);\n if (providerOAuthError != null && !requiredParams.every(param => originalUrl.searchParams.has(param))) {\n removeOAuthErrorParamsFromHistory(originalUrl);\n return {\n type: \"known-error\",\n error: providerOAuthError,\n };\n }\n\n for (const param of requiredParams) {\n if (!originalUrl.searchParams.has(param)) {\n if (!options?.dontWarnAboutMissingQueryParams) {\n console.warn(new Error(`Missing required query parameter on OAuth callback: ${param}. Maybe you opened or reloaded the oauth-callback page from your history?`));\n }\n return null;\n }\n }\n\n const expectedState = originalUrl.searchParams.get(\"state\") ?? throwErr(\"This should never happen; isn't state required above?\");\n const cookieResult = consumeVerifierAndStateCookie(expectedState);\n\n if (!cookieResult) {\n // If the state can't be found in the cookies, then the callback wasn't meant for us.\n // Maybe the website uses another OAuth library?\n console.warn(deindent`\n Stack found an outer OAuth callback state in the query parameters, but not in cookies.\n\n This could have multiple reasons:\n - The cookie expired, because the OAuth flow took too long.\n - The user's browser deleted the cookie, either manually or because of a very strict cookie policy.\n - The cookie was already consumed by this page, and the user already logged in.\n - You are using another OAuth client library with the same callback URL as Stack.\n - The user opened the OAuth callback page from their history.\n\n Either way, it is probably safe to ignore this warning unless you are debugging an OAuth issue.\n `);\n return null;\n }\n\n\n const newUrl = new URL(originalUrl);\n for (const param of requiredParams) {\n newUrl.searchParams.delete(param);\n }\n\n // let's get rid of the authorization code in the history as we\n // don't redirect to `redirectUrl` if there's a validation error\n // (as the redirectUrl might be malicious!).\n //\n // We use history.replaceState instead of location.assign(...) to\n // prevent an unnecessary reload\n window.history.replaceState({}, \"\", newUrl.toString());\n\n return {\n type: \"oauth-response\",\n originalUrl,\n codeVerifier: cookieResult.codeVerifier,\n state: expectedState,\n };\n}\n\nexport async function callOAuthCallback(\n iface: HexclaveClientInterface,\n redirectUrl: string,\n options?: {\n dontWarnAboutMissingQueryParams?: boolean,\n },\n) {\n // note: this part of the function (until the return) needs\n // to be synchronous, to prevent race conditions when\n // callOAuthCallback is called multiple times in parallel\n const consumed = consumeOAuthCallbackQueryParams(options);\n if (!consumed) return Result.ok(undefined);\n if (consumed.type === \"known-error\") {\n throw consumed.error;\n }\n\n // the rest can be asynchronous (we now know that we are the\n // intended recipient of the callback, and the only instance\n // of callOAuthCallback that's running)\n try {\n return Result.ok(await iface.callOAuthCallback({\n oauthParams: consumed.originalUrl.searchParams,\n redirectUri: constructRedirectUrl(redirectUrl, \"redirectUri\"),\n codeVerifier: consumed.codeVerifier,\n state: consumed.state,\n }));\n } catch (e) {\n if (KnownError.isKnownError(e)) {\n throw e;\n }\n throw new HexclaveAssertionError(\"Error signing in during OAuth callback. Please try again.\", { cause: e });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { HexclaveClientInterface } from "@hexclave/shared";
|
|
3
|
-
import { getNewOAuthProviderOrScopeUrl } from "./auth.js";
|
|
2
|
+
import { HexclaveClientInterface, KnownErrors } from "@hexclave/shared";
|
|
3
|
+
import { callOAuthCallback, getNewOAuthProviderOrScopeUrl } from "./auth.js";
|
|
4
4
|
|
|
5
5
|
//#region src/lib/auth.test.ts
|
|
6
6
|
// @vitest-environment jsdom
|
|
@@ -13,17 +13,20 @@ vi.mock("./cookie", async (importOriginal) => {
|
|
|
13
13
|
})
|
|
14
14
|
};
|
|
15
15
|
});
|
|
16
|
+
function createTestInterface() {
|
|
17
|
+
return new HexclaveClientInterface({
|
|
18
|
+
clientVersion: "test",
|
|
19
|
+
getBaseUrl: () => "https://api.example.com",
|
|
20
|
+
getApiUrls: () => ["https://api.example.com"],
|
|
21
|
+
extraRequestHeaders: {},
|
|
22
|
+
projectId: "00000000-0000-4000-8000-000000000000",
|
|
23
|
+
publishableClientKey: "pck_test"
|
|
24
|
+
});
|
|
25
|
+
}
|
|
16
26
|
describe("getNewOAuthProviderOrScopeUrl", () => {
|
|
17
27
|
it("returns the OAuth URL without performing navigation", async () => {
|
|
18
28
|
window.history.replaceState({}, "", "/account?after_auth_return_to=%2Fsettings");
|
|
19
|
-
const iface =
|
|
20
|
-
clientVersion: "test",
|
|
21
|
-
getBaseUrl: () => "https://api.example.com",
|
|
22
|
-
getApiUrls: () => ["https://api.example.com"],
|
|
23
|
-
extraRequestHeaders: {},
|
|
24
|
-
projectId: "00000000-0000-4000-8000-000000000000",
|
|
25
|
-
publishableClientKey: "pck_test"
|
|
26
|
-
});
|
|
29
|
+
const iface = createTestInterface();
|
|
27
30
|
const location = await getNewOAuthProviderOrScopeUrl(iface, {
|
|
28
31
|
provider: "github",
|
|
29
32
|
redirectUrl: "/handler/oauth-callback",
|
|
@@ -54,6 +57,18 @@ describe("getNewOAuthProviderOrScopeUrl", () => {
|
|
|
54
57
|
`);
|
|
55
58
|
});
|
|
56
59
|
});
|
|
60
|
+
describe("callOAuthCallback", () => {
|
|
61
|
+
it("turns provider access denial callback params into a known error", async () => {
|
|
62
|
+
window.history.replaceState({}, "", "/handler/oauth-callback?error=access_denied&error_description=User+cancelled");
|
|
63
|
+
await expect(callOAuthCallback(createTestInterface(), "/handler/oauth-callback")).rejects.toSatisfy((error) => KnownErrors.OAuthProviderAccessDenied.isInstance(error));
|
|
64
|
+
expect(window.location.href).toBe("http://localhost:3000/handler/oauth-callback");
|
|
65
|
+
});
|
|
66
|
+
it("turns generic provider error callback params into a known error", async () => {
|
|
67
|
+
window.history.replaceState({}, "", "/handler/oauth-callback?error=server_error&error_description=Provider+failed");
|
|
68
|
+
await expect(callOAuthCallback(createTestInterface(), "/handler/oauth-callback")).rejects.toSatisfy((error) => KnownErrors.OAuthProviderTemporarilyUnavailable.isInstance(error));
|
|
69
|
+
expect(window.location.href).toBe("http://localhost:3000/handler/oauth-callback");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
57
72
|
|
|
58
73
|
//#endregion
|
|
59
74
|
export { };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.test.js","names":[],"sources":["../../../src/lib/auth.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n// @vitest-environment jsdom\n\nimport { HexclaveClientInterface } from \"@hexclave/shared\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport { getNewOAuthProviderOrScopeUrl } from \"./auth\";\n\nvi.mock(\"./cookie\", async (importOriginal) => {\n const actual = await importOriginal<typeof import(\"./cookie\")>();\n return {\n ...actual,\n saveVerifierAndState: async () => ({\n codeChallenge: \"<stripped code challenge>\",\n state: \"<stripped state>\",\n }),\n };\n});\n\
|
|
1
|
+
{"version":3,"file":"auth.test.js","names":[],"sources":["../../../src/lib/auth.test.ts"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n// @vitest-environment jsdom\n\nimport { HexclaveClientInterface, KnownErrors } from \"@hexclave/shared\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport { callOAuthCallback, getNewOAuthProviderOrScopeUrl } from \"./auth\";\n\nvi.mock(\"./cookie\", async (importOriginal) => {\n const actual = await importOriginal<typeof import(\"./cookie\")>();\n return {\n ...actual,\n saveVerifierAndState: async () => ({\n codeChallenge: \"<stripped code challenge>\",\n state: \"<stripped state>\",\n }),\n };\n});\n\nfunction createTestInterface() {\n return new HexclaveClientInterface({\n clientVersion: \"test\",\n getBaseUrl: () => \"https://api.example.com\",\n getApiUrls: () => [\"https://api.example.com\"],\n extraRequestHeaders: {},\n projectId: \"00000000-0000-4000-8000-000000000000\",\n publishableClientKey: \"pck_test\",\n });\n}\n\ndescribe(\"getNewOAuthProviderOrScopeUrl\", () => {\n it(\"returns the OAuth URL without performing navigation\", async () => {\n window.history.replaceState({}, \"\", \"/account?after_auth_return_to=%2Fsettings\");\n\n const iface = createTestInterface();\n const session = iface.createSession({ refreshToken: null, accessToken: null });\n\n const location = await getNewOAuthProviderOrScopeUrl(\n iface,\n {\n provider: \"github\",\n redirectUrl: \"/handler/oauth-callback\",\n errorRedirectUrl: \"/handler/error\",\n providerScope: \"repo user\",\n },\n session,\n );\n\n const url = new URL(location);\n expect(`${url.origin}${url.pathname}`).toBe(\"https://api.example.com/api/v1/auth/oauth/authorize/github\");\n expect(Object.fromEntries(url.searchParams.entries())).toMatchInlineSnapshot(`\n {\n \"after_callback_redirect_url\": \"http://localhost:3000/account?after_auth_return_to=%2Fsettings\",\n \"client_id\": \"00000000-0000-4000-8000-000000000000\",\n \"client_secret\": \"pck_test\",\n \"code_challenge\": \"<stripped code challenge>\",\n \"code_challenge_method\": \"S256\",\n \"error_redirect_url\": \"http://localhost:3000/handler/error?after_auth_return_to=%2Fsettings\",\n \"grant_type\": \"authorization_code\",\n \"provider_scope\": \"repo user\",\n \"redirect_uri\": \"http://localhost:3000/handler/oauth-callback?after_auth_return_to=%2Fsettings\",\n \"response_type\": \"code\",\n \"scope\": \"legacy\",\n \"state\": \"<stripped state>\",\n \"type\": \"link\",\n }\n `);\n });\n});\n\ndescribe(\"callOAuthCallback\", () => {\n it(\"turns provider access denial callback params into a known error\", async () => {\n window.history.replaceState({}, \"\", \"/handler/oauth-callback?error=access_denied&error_description=User+cancelled\");\n\n await expect(callOAuthCallback(createTestInterface(), \"/handler/oauth-callback\"))\n .rejects.toSatisfy((error: unknown) => KnownErrors.OAuthProviderAccessDenied.isInstance(error));\n expect(window.location.href).toBe(\"http://localhost:3000/handler/oauth-callback\");\n });\n\n it(\"turns generic provider error callback params into a known error\", async () => {\n window.history.replaceState({}, \"\", \"/handler/oauth-callback?error=server_error&error_description=Provider+failed\");\n\n await expect(callOAuthCallback(createTestInterface(), \"/handler/oauth-callback\"))\n .rejects.toSatisfy((error: unknown) => KnownErrors.OAuthProviderTemporarilyUnavailable.isInstance(error));\n expect(window.location.href).toBe(\"http://localhost:3000/handler/oauth-callback\");\n });\n});\n"],"mappings":";;;;;;AAUA,GAAG,KAAK,YAAY,OAAO,mBAAmB;AAE5C,QAAO;EACL,GAFa,MAAM,gBAA2C;EAG9D,sBAAsB,aAAa;GACjC,eAAe;GACf,OAAO;GACR;EACF;EACD;AAEF,SAAS,sBAAsB;AAC7B,QAAO,IAAI,wBAAwB;EACjC,eAAe;EACf,kBAAkB;EAClB,kBAAkB,CAAC,0BAA0B;EAC7C,qBAAqB,EAAE;EACvB,WAAW;EACX,sBAAsB;EACvB,CAAC;;AAGJ,SAAS,uCAAuC;AAC9C,IAAG,uDAAuD,YAAY;AACpE,SAAO,QAAQ,aAAa,EAAE,EAAE,IAAI,4CAA4C;EAEhF,MAAM,QAAQ,qBAAqB;EAGnC,MAAM,WAAW,MAAM,8BACrB,OACA;GACE,UAAU;GACV,aAAa;GACb,kBAAkB;GAClB,eAAe;GAChB,EATa,MAAM,cAAc;GAAE,cAAc;GAAM,aAAa;GAAM,CAAC,CAW7E;EAED,MAAM,MAAM,IAAI,IAAI,SAAS;AAC7B,SAAO,GAAG,IAAI,SAAS,IAAI,WAAW,CAAC,KAAK,6DAA6D;AACzG,SAAO,OAAO,YAAY,IAAI,aAAa,SAAS,CAAC,CAAC,CAAC,sBAAsB;;;;;;;;;;;;;;;;MAgB3E;GACF;EACF;AAEF,SAAS,2BAA2B;AAClC,IAAG,mEAAmE,YAAY;AAChF,SAAO,QAAQ,aAAa,EAAE,EAAE,IAAI,+EAA+E;AAEnH,QAAM,OAAO,kBAAkB,qBAAqB,EAAE,0BAA0B,CAAC,CAC9E,QAAQ,WAAW,UAAmB,YAAY,0BAA0B,WAAW,MAAM,CAAC;AACjG,SAAO,OAAO,SAAS,KAAK,CAAC,KAAK,+CAA+C;GACjF;AAEF,IAAG,mEAAmE,YAAY;AAChF,SAAO,QAAQ,aAAa,EAAE,EAAE,IAAI,+EAA+E;AAEnH,QAAM,OAAO,kBAAkB,qBAAqB,EAAE,0BAA0B,CAAC,CAC9E,QAAQ,WAAW,UAAmB,YAAY,oCAAoC,WAAW,MAAM,CAAC;AAC3G,SAAO,OAAO,SAAS,KAAK,CAAC,KAAK,+CAA+C;GACjF;EACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-app-impl.d.ts","names":[],"sources":["../../../../../../src/lib/hexclave-app/apps/implementations/admin-app-impl.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyEa,+BAAA,kEAAiG,gCAAA,CAAiC,aAAA,EAAe,SAAA,aAAsB,aAAA,CAAc,aAAA,EAAe,SAAA;EAAA,UAC7L,UAAA,EAAY,sBAAA;EAAA,iBAEb,kBAAA;EAAA,iBAGA,qBAAA;EAAA,iBAIA,qBAAA;EAAA,iBAGA,sBAAA;EAAA,iBAGA,yBAAA;EAAA,iBAGA,sBAAA;EAAA,iBAGA,oCAAA;EAAA,iBAGA,uCAAA;EAAA,iBAGA,eAAA;EAAA,iBAMA,aAAA;EAAA,iBAIA,kBAAA;EAAA,iBAGA,uBAAA;EAAA,iBAGA,kBAAA;EAAA,iBAGA,qCAAA;EAAA,iBAGA,qBAAA;EAAA,iBAGA,uBAAA;EAAA,iBAUA,kBAAA;cAIL,OAAA,EAAS,+BAAA,CAAgC,aAAA,EAAe,SAAA,GAAY,YAAA;IAAiB,gBAAA;IAA2B,WAAA;IAAsB,SAAA,GAAY,sBAAA;EAAA;EA2B9J,oBAAA,CAAqB,IAAA;IAAQ,aAAA;EAAA,IAA0B,cAAA;EAIvD,0BAAA,CAA2B,IAAA,EAAM,YAAA,mBAA+B,SAAA,QAAiB,OAAA,SAAgB,iBAAA;EAUjG,qBAAA,CAAsB,IAAA,EAAM,YAAA,mBAA+B,SAAA,QAAiB,OAAA,SAAgB,YAAA;EAsI5F,2BAAA,CAA4B,IAAA,EAAM,iBAAA,oBAAqC,kBAAA;EASxD,UAAA,CAAA,GAAc,OAAA,CAAQ,YAAA;EAAA,UAQ3B,iCAAA,CAAkC,IAAA,EAAM,0BAAA,GAA6B,kBAAA;EAAA,UAwBrE,6BAAA,CAA8B,IAAA,EAAM,mBAAA,oBAAuC,cAAA;EAAA,UAS3E,sCAAA,CAAuC,IAAA,EAAM,gCAAA,GAAmC,uBAAA;EASpF,mBAAA,CAAA,GAAuB,OAAA,CAAQ,cAAA;EAM/B,oBAAA,CAAqB,OAAA,EAAS,2BAAA,GAA8B,OAAA,CAAQ,uBAAA;EAOpE,eAAA,CAAA,GAAmB,OAAA;IAAU,EAAA;IAAY,WAAA;EAAA;EAQzC,kBAAA,CAAA,GAAsB,OAAA;IAAU,EAAA;IAAY,WAAA;IAAqB,OAAA;IAAkB,SAAA;EAAA;EAUnF,eAAA,CAAA,GAAmB,OAAA;IAAU,EAAA;IAAY,WAAA;IAAqB,OAAA;IAAqC,SAAA;IAAmB,MAAA,EAAQ,IAAA;EAAA;EAY9H,8BAAA,CAA+B,IAAA,EAAM,0CAAA,GAA6C,OAAA,CAAQ,mBAAA;EAM1F,8BAAA,CAA+B,YAAA,UAAsB,IAAA,EAAM,0CAAA,GAA0C,OAAA;EAKrG,8BAAA,CAA+B,YAAA,WAAuB,OAAA;EAKtD,6BAAA,CAAA,GAAiC,OAAA,CAAQ,6BAAA;EAKzC,sCAAA,CACJ,OAAA;IAAW,KAAA;IAAe,MAAA;IAAiB,KAAA;EAAA,IAC1C,OAAA;IAAU,KAAA,EAAO,6BAAA;IAAiC,UAAA;EAAA;EAS/C,iCAAA,CAAkC,IAAA,EAAM,6CAAA,GAAgD,OAAA,CAAQ,sBAAA;EAMhG,iCAAA,CAAkC,YAAA,UAAsB,IAAA,EAAM,6CAAA,GAA6C,OAAA;EAK3G,iCAAA,CAAkC,YAAA,WAAuB,OAAA;EAKzD,gCAAA,CAAA,GAAoC,OAAA,CAAQ,gCAAA;EAAA,UAMzB,eAAA,CAAA,GAAe,OAAA;EAAA,UAOxB,qBAAA,CAAA,GAAqB,OAAA;EAAA,UAOrB,uBAAA,CAAA,GAAuB,OAAA;EAAA,UAId,aAAA,CAAA,GAAa,OAAA;EAAA,KAQjC,0BAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"admin-app-impl.d.ts","names":[],"sources":["../../../../../../src/lib/hexclave-app/apps/implementations/admin-app-impl.ts"],"mappings":"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"}
|
|
@@ -785,6 +785,7 @@ var _HexclaveAdminAppImplIncomplete = class extends _HexclaveServerAppImplIncomp
|
|
|
785
785
|
const response = await this._interface.listOutboxEmails({
|
|
786
786
|
status: options?.status,
|
|
787
787
|
simple_status: options?.simpleStatus,
|
|
788
|
+
user_id: options?.userId,
|
|
788
789
|
limit: options?.limit,
|
|
789
790
|
cursor: options?.cursor
|
|
790
791
|
});
|