@tidecloak/js 0.14.41 → 0.14.43-staging
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 +32 -47
- package/dist/cjs/lib/tidecloak-dpop.js +151 -42
- package/dist/cjs/lib/tidecloak-dpop.js.map +1 -1
- package/dist/cjs/lib/tidecloak.js +300 -378
- package/dist/cjs/lib/tidecloak.js.map +1 -1
- package/dist/cjs/src/AdminAPI.js +12 -19
- package/dist/cjs/src/AdminAPI.js.map +1 -1
- package/dist/cjs/src/IAMService.js +159 -2095
- package/dist/cjs/src/IAMService.js.map +1 -1
- package/dist/cjs/src/index.js +1 -7
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/esm/lib/tidecloak-dpop.js +151 -42
- package/dist/esm/lib/tidecloak-dpop.js.map +1 -1
- package/dist/esm/lib/tidecloak.js +300 -377
- package/dist/esm/lib/tidecloak.js.map +1 -1
- package/dist/esm/src/AdminAPI.js +3 -7
- package/dist/esm/src/AdminAPI.js.map +1 -1
- package/dist/esm/src/IAMService.js +156 -2062
- package/dist/esm/src/IAMService.js.map +1 -1
- package/dist/esm/src/index.js +1 -7
- package/dist/esm/src/index.js.map +1 -1
- package/dist/types/AdminAPI.d.ts +1 -1
- package/dist/types/IAMService.d.ts +57 -606
- package/dist/types/index.d.ts +1 -11
- package/dist/types/lib/tidecloak-dpop.d.ts +19 -3
- package/dist/types/lib/tidecloak.d.ts +46 -38
- package/dist/types/src/AdminAPI.d.ts +1 -1
- package/dist/types/src/IAMService.d.ts +57 -606
- package/dist/types/src/index.d.ts +1 -11
- package/docs/FRONT_CHANNEL.md +55 -55
- package/lib/README.md +10 -14
- package/package.json +4 -25
- package/dist/cjs/lib/secureFetchPolicy.js +0 -45
- package/dist/cjs/lib/secureFetchPolicy.js.map +0 -1
- package/dist/cjs/src/dpopRoute.js +0 -30
- package/dist/cjs/src/dpopRoute.js.map +0 -1
- package/dist/cjs/src/dpopServer.js +0 -77
- package/dist/cjs/src/dpopServer.js.map +0 -1
- package/dist/cjs/src/generated/dpopAuthAsset.js +0 -7
- package/dist/cjs/src/generated/dpopAuthAsset.js.map +0 -1
- package/dist/cjs/src/policy-react.js +0 -51
- package/dist/cjs/src/policy-react.js.map +0 -1
- package/dist/cjs/src/types.js +0 -6
- package/dist/cjs/src/types.js.map +0 -1
- package/dist/cjs/src/utils/dpopAuthPage.js +0 -69
- package/dist/cjs/src/utils/dpopAuthPage.js.map +0 -1
- package/dist/cjs/src/utils/fetch.js +0 -39
- package/dist/cjs/src/utils/fetch.js.map +0 -1
- package/dist/cjs/src/utils/index.js +0 -17
- package/dist/cjs/src/utils/index.js.map +0 -1
- package/dist/cjs/src/utils/initOptions.js +0 -78
- package/dist/cjs/src/utils/initOptions.js.map +0 -1
- package/dist/cjs/src/utils/pkce.js +0 -48
- package/dist/cjs/src/utils/pkce.js.map +0 -1
- package/dist/cjs/src/utils/silentCheckSso.js +0 -138
- package/dist/cjs/src/utils/silentCheckSso.js.map +0 -1
- package/dist/cjs/src/vite.js +0 -43
- package/dist/cjs/src/vite.js.map +0 -1
- package/dist/esm/lib/secureFetchPolicy.js +0 -42
- package/dist/esm/lib/secureFetchPolicy.js.map +0 -1
- package/dist/esm/src/dpopRoute.js +0 -27
- package/dist/esm/src/dpopRoute.js.map +0 -1
- package/dist/esm/src/dpopServer.js +0 -68
- package/dist/esm/src/dpopServer.js.map +0 -1
- package/dist/esm/src/generated/dpopAuthAsset.js +0 -4
- package/dist/esm/src/generated/dpopAuthAsset.js.map +0 -1
- package/dist/esm/src/policy-react.js +0 -45
- package/dist/esm/src/policy-react.js.map +0 -1
- package/dist/esm/src/types.js +0 -5
- package/dist/esm/src/types.js.map +0 -1
- package/dist/esm/src/utils/dpopAuthPage.js +0 -63
- package/dist/esm/src/utils/dpopAuthPage.js.map +0 -1
- package/dist/esm/src/utils/fetch.js +0 -36
- package/dist/esm/src/utils/fetch.js.map +0 -1
- package/dist/esm/src/utils/index.js +0 -6
- package/dist/esm/src/utils/index.js.map +0 -1
- package/dist/esm/src/utils/initOptions.js +0 -75
- package/dist/esm/src/utils/initOptions.js.map +0 -1
- package/dist/esm/src/utils/pkce.js +0 -43
- package/dist/esm/src/utils/pkce.js.map +0 -1
- package/dist/esm/src/utils/silentCheckSso.js +0 -134
- package/dist/esm/src/utils/silentCheckSso.js.map +0 -1
- package/dist/esm/src/vite.js +0 -40
- package/dist/esm/src/vite.js.map +0 -1
- package/dist/types/dpopRoute.d.ts +0 -11
- package/dist/types/dpopServer.d.ts +0 -35
- package/dist/types/generated/dpopAuthAsset.d.ts +0 -2
- package/dist/types/lib/secureFetchPolicy.d.ts +0 -37
- package/dist/types/policy-react.d.ts +0 -40
- package/dist/types/src/dpopRoute.d.ts +0 -11
- package/dist/types/src/dpopServer.d.ts +0 -35
- package/dist/types/src/generated/dpopAuthAsset.d.ts +0 -2
- package/dist/types/src/policy-react.d.ts +0 -40
- package/dist/types/src/types.d.ts +0 -114
- package/dist/types/src/utils/dpopAuthPage.d.ts +0 -22
- package/dist/types/src/utils/fetch.d.ts +0 -11
- package/dist/types/src/utils/index.d.ts +0 -5
- package/dist/types/src/utils/initOptions.d.ts +0 -69
- package/dist/types/src/utils/pkce.d.ts +0 -24
- package/dist/types/src/utils/silentCheckSso.d.ts +0 -82
- package/dist/types/src/vite.d.ts +0 -13
- package/dist/types/types.d.ts +0 -114
- package/dist/types/utils/dpopAuthPage.d.ts +0 -22
- package/dist/types/utils/fetch.d.ts +0 -11
- package/dist/types/utils/index.d.ts +0 -5
- package/dist/types/utils/initOptions.d.ts +0 -69
- package/dist/types/utils/pkce.d.ts +0 -24
- package/dist/types/utils/silentCheckSso.d.ts +0 -82
- package/dist/types/vite.d.ts +0 -13
- package/docs/HYBRID_MODE.md +0 -185
- package/docs/NATIVE_MODE.md +0 -284
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { IAMService } from './IAMService.js';
|
|
2
2
|
export { default as TideCloak } from "../lib/tidecloak.js";
|
|
3
3
|
export { RequestEnclave, ApprovalEnclaveNew } from "../lib/tidecloak.js";
|
|
4
4
|
export { Tools, Models } from "@tideorg/js";
|
|
5
|
-
export type NativeAdapter = import('./types.js').NativeAdapter;
|
|
6
|
-
export type NativeTokenData = import('./types.js').NativeTokenData;
|
|
7
|
-
export type NativeAuthCallbackResult = import('./types.js').NativeAuthCallbackResult;
|
|
8
|
-
export type NativeEncryptionCallbackResult = import('./types.js').NativeEncryptionCallbackResult;
|
|
9
|
-
export type NativeConfig = import('./types.js').NativeConfig;
|
|
10
|
-
/** @typedef {import('./types.js').NativeAdapter} NativeAdapter */
|
|
11
|
-
/** @typedef {import('./types.js').NativeTokenData} NativeTokenData */
|
|
12
|
-
/** @typedef {import('./types.js').NativeAuthCallbackResult} NativeAuthCallbackResult */
|
|
13
|
-
/** @typedef {import('./types.js').NativeEncryptionCallbackResult} NativeEncryptionCallbackResult */
|
|
14
|
-
/** @typedef {import('./types.js').NativeConfig} NativeConfig */
|
|
15
5
|
export { default as AdminAPI, AdminAPI as AdminAPIClass } from './AdminAPI.js';
|
package/docs/FRONT_CHANNEL.md
CHANGED
|
@@ -24,30 +24,15 @@ This file is required for silent session checks. It should be auto-copied when y
|
|
|
24
24
|
<html><body><script>parent.postMessage(location.href, location.origin)</script></body></html>
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
### 3.
|
|
28
|
-
|
|
29
|
-
Create `public/auth/redirect.html`:
|
|
30
|
-
|
|
31
|
-
```html
|
|
32
|
-
<!DOCTYPE html>
|
|
33
|
-
<html>
|
|
34
|
-
<head><title>Redirecting...</title></head>
|
|
35
|
-
<body>
|
|
36
|
-
<p>Redirecting...</p>
|
|
37
|
-
<script>window.location.href = "/";</script>
|
|
38
|
-
</body>
|
|
39
|
-
</html>
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 4. Initialize the SDK
|
|
27
|
+
### 3. Initialize the SDK
|
|
43
28
|
|
|
44
29
|
```js
|
|
45
30
|
import { IAMService } from "@tidecloak/js";
|
|
46
31
|
import config from "./tidecloak.json";
|
|
47
32
|
|
|
48
|
-
// Listen for events
|
|
33
|
+
// Listen for events
|
|
49
34
|
IAMService
|
|
50
|
-
.on("ready", (
|
|
35
|
+
.on("ready", (loggedIn) => {
|
|
51
36
|
console.log("Ready! Logged in:", loggedIn);
|
|
52
37
|
updateUI(loggedIn);
|
|
53
38
|
})
|
|
@@ -59,39 +44,40 @@ IAMService
|
|
|
59
44
|
});
|
|
60
45
|
|
|
61
46
|
// Start the SDK
|
|
62
|
-
await IAMService.
|
|
47
|
+
await IAMService.init(config);
|
|
63
48
|
```
|
|
64
49
|
|
|
65
50
|
### DPoP (opt-in)
|
|
66
51
|
|
|
67
|
-
DPoP (sender-constrained tokens, [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) binds an access token to a per-session key, so a stolen token can't be replayed. It is **opt-in**: set `
|
|
52
|
+
DPoP (sender-constrained tokens, [RFC 9449](https://datatracker.ietf.org/doc/html/rfc9449)) binds an access token to a per-session key, so a stolen token can't be replayed. It is **opt-in**: set `dpopConfig` and the SDK turns it on; omit it and the SDK requests an ordinary (unbound) token.
|
|
68
53
|
|
|
69
|
-
> **DPoP is not a flag you can flip in isolation.** Asking for it appends `dpop_jkt` to the authorization request, and the realm then issues a token carrying `typ: "DPoP"` and `cnf.jkt`. Such a token is **invalid** if it is later presented as a plain `Authorization: Bearer …`, and the resource server answers a bare `401`. So enable DPoP only if every call that carries the token attaches a `DPoP:` proof (use `IAMService.
|
|
54
|
+
> **DPoP is not a flag you can flip in isolation.** Asking for it appends `dpop_jkt` to the authorization request, and the realm then issues a token carrying `typ: "DPoP"` and `cnf.jkt`. Such a token is **invalid** if it is later presented as a plain `Authorization: Bearer …`, and the resource server answers a bare `401`. So enable DPoP only if every call that carries the token attaches a `DPoP:` proof (use `IAMService.fetch`, which does this for you). This is why the SDK will not turn DPoP on for you.
|
|
70
55
|
|
|
71
56
|
```js
|
|
72
57
|
// Opt in, enforced: init fails if the realm doesn't advertise DPoP support
|
|
73
|
-
await IAMService.
|
|
58
|
+
await IAMService.init({ ...config, dpopConfig: { mode: "strict" } });
|
|
74
59
|
|
|
75
60
|
// Use DPoP only when the realm supports it, otherwise fall back to bearer:
|
|
76
|
-
await IAMService.
|
|
61
|
+
await IAMService.init({ ...config, dpopConfig: { mode: "auto" } });
|
|
77
62
|
|
|
78
63
|
// Pick the proof signing algorithm (default "ES256"):
|
|
79
|
-
await IAMService.
|
|
64
|
+
await IAMService.init({ ...config, dpopConfig: { mode: "strict", alg: "EdDSA" } });
|
|
80
65
|
|
|
81
66
|
// No DPoP, a plain unbound access token (the default)
|
|
82
|
-
await IAMService.
|
|
67
|
+
await IAMService.init(config);
|
|
83
68
|
```
|
|
84
69
|
|
|
85
|
-
| `
|
|
86
|
-
|
|
|
87
|
-
| *(omitted)* / `false`
|
|
88
|
-
| `{ mode: "auto" }`
|
|
89
|
-
| `{ mode: "strict" }`
|
|
90
|
-
| `{ mode: …, alg }`
|
|
70
|
+
| `dpopConfig` value | Behavior |
|
|
71
|
+
| ------------------------------ | ------------------------------------------------------------------------- |
|
|
72
|
+
| *(omitted)* / `false` | **Default.** No DPoP. No `dpop_jkt`, plain unbound access token. |
|
|
73
|
+
| `{ mode: "auto" }` | Use DPoP when the realm advertises it; otherwise fall back to bearer. |
|
|
74
|
+
| `{ mode: "strict" }` | Enforce DPoP; init fails if the realm lacks DPoP support. |
|
|
75
|
+
| `{ mode: …, alg }` | As above, with a specific proof algorithm (`ES256` default). |
|
|
76
|
+
| `{ mode: …, strictStorage }` | `true` fails init if IndexedDB is unavailable instead of using memory. |
|
|
91
77
|
|
|
92
78
|
> Your **resource server** must validate DPoP proofs for the binding to be meaningful. See [`lib/README.md`](../lib/README.md#dpop-resource-server-setup) for serving `tide_dpop_auth.html`.
|
|
93
79
|
|
|
94
|
-
###
|
|
80
|
+
### 4. Add Login/Logout Buttons
|
|
95
81
|
|
|
96
82
|
```js
|
|
97
83
|
document.getElementById("login-btn").onclick = () => IAMService.doLogin();
|
|
@@ -107,50 +93,54 @@ document.getElementById("logout-btn").onclick = () => IAMService.doLogout();
|
|
|
107
93
|
IAMService.isLoggedIn(); // Is user logged in?
|
|
108
94
|
|
|
109
95
|
// Get tokens
|
|
110
|
-
|
|
111
|
-
IAMService.
|
|
96
|
+
IAMService.getToken(); // Access token (for API calls)
|
|
97
|
+
IAMService.getTokenExp(); // When the access token expires (Date)
|
|
112
98
|
|
|
113
99
|
// Get user info
|
|
114
100
|
IAMService.getName(); // Username
|
|
115
|
-
IAMService.
|
|
116
|
-
IAMService.getValueFromIDToken("name"); // getValueFromIdToken also works
|
|
101
|
+
IAMService.getClaim("email"); // Any access-token claim
|
|
117
102
|
|
|
118
103
|
// Check roles
|
|
119
104
|
IAMService.hasRealmRole("admin");
|
|
120
105
|
IAMService.hasClientRole("editor");
|
|
121
106
|
|
|
122
107
|
// Auth actions
|
|
123
|
-
IAMService.doLogin();
|
|
124
|
-
IAMService.doLogout();
|
|
125
|
-
await IAMService.
|
|
108
|
+
await IAMService.doLogin();
|
|
109
|
+
await IAMService.doLogout();
|
|
110
|
+
await IAMService.updateToken(); // Refresh if the token expires within 5s
|
|
111
|
+
await IAMService.forceUpdateToken(); // Refresh now
|
|
112
|
+
|
|
113
|
+
// Call an API with the access token (adds a DPoP proof when DPoP is on)
|
|
114
|
+
await IAMService.fetch("/api/data", {
|
|
115
|
+
headers: { Authorization: `Bearer ${IAMService.getToken()}` },
|
|
116
|
+
});
|
|
126
117
|
|
|
127
118
|
// Encryption (if configured)
|
|
128
|
-
await IAMService.
|
|
129
|
-
await IAMService.
|
|
119
|
+
await IAMService.tide.encrypt([{ data: "secret", tags: ["personal"] }]);
|
|
120
|
+
await IAMService.tide.decrypt([{ encrypted: "...", tags: ["personal"] }]);
|
|
130
121
|
```
|
|
131
122
|
|
|
132
123
|
---
|
|
133
124
|
|
|
134
125
|
## Events
|
|
135
126
|
|
|
136
|
-
Every handler is called with the event name first, then the event's arguments.
|
|
137
|
-
|
|
138
127
|
```js
|
|
139
128
|
IAMService
|
|
140
|
-
.on("ready", (
|
|
129
|
+
.on("ready", (loggedIn) => {
|
|
141
130
|
// SDK is ready - loggedIn is true/false
|
|
142
131
|
})
|
|
143
132
|
.on("authSuccess", () => {
|
|
144
133
|
// User logged in
|
|
145
134
|
})
|
|
146
|
-
.on("authError", (
|
|
135
|
+
.on("authError", (err) => {
|
|
147
136
|
// Login failed
|
|
148
137
|
})
|
|
149
138
|
.on("logout", () => {
|
|
150
139
|
// User logged out
|
|
151
140
|
})
|
|
152
141
|
.on("tokenExpired", () => {
|
|
153
|
-
// Token expired -
|
|
142
|
+
// Token expired - refresh it
|
|
143
|
+
IAMService.updateToken();
|
|
154
144
|
});
|
|
155
145
|
```
|
|
156
146
|
|
|
@@ -162,13 +152,13 @@ Protect sensitive data with tag-based encryption:
|
|
|
162
152
|
|
|
163
153
|
```js
|
|
164
154
|
// Encrypt one or more items
|
|
165
|
-
const encrypted = await IAMService.
|
|
155
|
+
const encrypted = await IAMService.tide.encrypt([
|
|
166
156
|
{ data: "10 Smith Street", tags: ["address"] },
|
|
167
157
|
{ data: "john@example.com", tags: ["email"] },
|
|
168
158
|
]);
|
|
169
159
|
|
|
170
160
|
// Decrypt
|
|
171
|
-
const decrypted = await IAMService.
|
|
161
|
+
const decrypted = await IAMService.tide.decrypt([
|
|
172
162
|
{ encrypted: encrypted[0], tags: ["address"] },
|
|
173
163
|
{ encrypted: encrypted[1], tags: ["email"] },
|
|
174
164
|
]);
|
|
@@ -184,22 +174,32 @@ const decrypted = await IAMService.doDecrypt([
|
|
|
184
174
|
|
|
185
175
|
```js
|
|
186
176
|
// Wrong - objects not allowed
|
|
187
|
-
await IAMService.
|
|
177
|
+
await IAMService.tide.encrypt([{ data: { name: "John" }, tags: ["user"] }]);
|
|
188
178
|
|
|
189
179
|
// Right - stringify first
|
|
190
|
-
await IAMService.
|
|
180
|
+
await IAMService.tide.encrypt([{ data: JSON.stringify({ name: "John" }), tags: ["user"] }]);
|
|
191
181
|
```
|
|
192
182
|
|
|
193
183
|
---
|
|
194
184
|
|
|
195
185
|
## Custom Redirect Path
|
|
196
186
|
|
|
197
|
-
By default, users
|
|
187
|
+
By default, users come back to the page they logged in from. To send them somewhere else:
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
await IAMService.doLogin({ redirectUri: "https://myapp.com/callback" });
|
|
191
|
+
await IAMService.doLogout({ redirectUri: "https://myapp.com/" });
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Silent SSO Page Location
|
|
197
|
+
|
|
198
|
+
The SDK looks for `silent-check-sso.html` at your origin root. If you serve it somewhere else, pass its URL (and register it as a valid redirect URI on the client):
|
|
198
199
|
|
|
199
200
|
```js
|
|
200
|
-
await IAMService.
|
|
201
|
-
|
|
202
|
-
redirectUri: "https://myapp.com/callback"
|
|
201
|
+
await IAMService.init(config, {
|
|
202
|
+
silentCheckSsoRedirectUri: "https://myapp.com/app/silent-check-sso.html",
|
|
203
203
|
});
|
|
204
204
|
```
|
|
205
205
|
|
|
@@ -209,7 +209,7 @@ await IAMService.initIAM({
|
|
|
209
209
|
|
|
210
210
|
**Blank page after login**
|
|
211
211
|
|
|
212
|
-
Make sure you
|
|
212
|
+
Make sure the page you return to (or the `redirectUri` you pass to `doLogin`) is registered as a valid redirect URI in TideCloak.
|
|
213
213
|
|
|
214
214
|
**"silent-check-sso.html not found" or silent SSO fails**
|
|
215
215
|
|
package/lib/README.md
CHANGED
|
@@ -55,7 +55,13 @@ const tidecloak = new TideCloak({
|
|
|
55
55
|
const authenticated = await tidecloak.init({
|
|
56
56
|
onLoad: "login-required",
|
|
57
57
|
checkLoginIframe: false,
|
|
58
|
-
|
|
58
|
+
dpopConfig: { mode: "strict", alg: "EdDSA" },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Call DPoP-protected APIs with tidecloak.fetch. It swaps the SDK's own Bearer
|
|
62
|
+
// token for `Authorization: DPoP` plus a proof; any other request goes out as-is.
|
|
63
|
+
const response = await tidecloak.fetch("https://api.example.com/user", {
|
|
64
|
+
headers: { Authorization: `Bearer ${tidecloak.token}` },
|
|
59
65
|
});
|
|
60
66
|
```
|
|
61
67
|
**Also ensure your resource server has DPoP JWT validation - this could be through using a package like Asgard or any other open sourced SDK**
|
|
@@ -64,19 +70,9 @@ const authenticated = await tidecloak.init({
|
|
|
64
70
|
With DPoP on, TideCloak loads `tide_dpop_auth.html` from your app's origin during login to confirm the browser holds the DPoP key. The file ships in this package and is the same for every app. Your issuer and client id go in the URL, not the file.
|
|
65
71
|
|
|
66
72
|
#### 1. Serve the page
|
|
67
|
-
In Next.js,
|
|
68
|
-
|
|
69
|
-
```javascript
|
|
70
|
-
// app/tide_dpop/[...path]/route.js
|
|
71
|
-
import { createDpopRoute } from "@tidecloak/js/dpop-route";
|
|
72
|
-
import config from "../../../tidecloak.json";
|
|
73
|
-
|
|
74
|
-
export const { GET, HEAD } = createDpopRoute({ config });
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
Use a route handler, not a proxy or middleware: common matchers skip paths containing a dot.
|
|
73
|
+
This package is browser-only, so serving the page is up to your app's server or host. In Next.js, use `createDpopRoute` from [`@tidecloak/nextjs/server`](https://github.com/tide-foundation/tidecloak-js/blob/main/packages/tidecloak-nextjs/docs/FRONT_CHANNEL.md#serving-the-dpop-page).
|
|
78
74
|
|
|
79
|
-
|
|
75
|
+
Anywhere else, serve `node_modules/@tidecloak/js/tide_dpop_auth.html` for any request to
|
|
80
76
|
`/tide_dpop/iss/{issuer as hex}/aud/{client id as hex}/tide_dpop_auth.html`
|
|
81
77
|
with these response headers:
|
|
82
78
|
1. `Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-utc6UrebuHOyLd/2aiMXS/p1EDy9UZBDe/XEMKDw9Mc='; style-src 'self' 'sha256-F7OJTdJYct4J+cQfuJUoDauitndqt8pAc8EbA8gwDPU='`
|
|
@@ -164,7 +160,7 @@ There are two ways to use a policy:
|
|
|
164
160
|
|
|
165
161
|
Pass the signed policy as the second argument. When a policy is provided, the default realm-role tag checks are skipped and the policy itself controls access.
|
|
166
162
|
|
|
167
|
-
`IAMService.
|
|
163
|
+
`IAMService.tide.encrypt` and `IAMService.tide.decrypt` take the policy the same way.
|
|
168
164
|
|
|
169
165
|
```javascript
|
|
170
166
|
const policy = yourSignedPolicy; // Uint8Array
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tidecloak/js",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.43-staging",
|
|
4
4
|
"description": "Vanilla JavaScript/browser SDK for TideCloak with an IAMService singleton and low-level client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cjs/src/index.js",
|
|
@@ -16,26 +16,6 @@
|
|
|
16
16
|
"require": "./dist/cjs/src/index.js",
|
|
17
17
|
"default": "./dist/esm/src/index.js"
|
|
18
18
|
},
|
|
19
|
-
"./dpop-server": {
|
|
20
|
-
"types": "./dist/types/src/dpopServer.d.ts",
|
|
21
|
-
"import": "./dist/esm/src/dpopServer.js",
|
|
22
|
-
"require": "./dist/cjs/src/dpopServer.js"
|
|
23
|
-
},
|
|
24
|
-
"./vite": {
|
|
25
|
-
"types": "./dist/types/src/vite.d.ts",
|
|
26
|
-
"import": "./dist/esm/src/vite.js",
|
|
27
|
-
"require": "./dist/cjs/src/vite.js"
|
|
28
|
-
},
|
|
29
|
-
"./dpop-route": {
|
|
30
|
-
"types": "./dist/types/src/dpopRoute.d.ts",
|
|
31
|
-
"import": "./dist/esm/src/dpopRoute.js",
|
|
32
|
-
"require": "./dist/cjs/src/dpopRoute.js"
|
|
33
|
-
},
|
|
34
|
-
"./policy-react": {
|
|
35
|
-
"types": "./dist/types/src/policy-react.d.ts",
|
|
36
|
-
"import": "./dist/esm/src/policy-react.js",
|
|
37
|
-
"require": "./dist/cjs/src/policy-react.js"
|
|
38
|
-
},
|
|
39
19
|
"./policy.css": "./dist/esm/src/policy.css"
|
|
40
20
|
},
|
|
41
21
|
"scripts": {
|
|
@@ -45,8 +25,7 @@
|
|
|
45
25
|
"build:esm": "tsc -p tsconfig.esm.json",
|
|
46
26
|
"build:types": "tsc -p tsconfig.types.json",
|
|
47
27
|
"build:copy-css": "node scripts/copy-css.cjs",
|
|
48
|
-
"build:
|
|
49
|
-
"build": "npm run build:dpop-asset && npm run build:cjs && npm run build:esm && npm run build:types && npm run build:copy-css && cp ./silent-check-sso.html dist/ && cp ./tide_dpop_auth.html dist/ && node scripts/postbuild.cjs",
|
|
28
|
+
"build": "npm run build:cjs && npm run build:esm && npm run build:types && npm run build:copy-css && cp ./silent-check-sso.html dist/ && cp ./tide_dpop_auth.html dist/ && node scripts/postbuild.cjs",
|
|
50
29
|
"prepare": "npm run build"
|
|
51
30
|
},
|
|
52
31
|
"files": [
|
|
@@ -76,7 +55,7 @@
|
|
|
76
55
|
"authorization"
|
|
77
56
|
],
|
|
78
57
|
"dependencies": {
|
|
79
|
-
"heimdall-tide": "
|
|
80
|
-
"@tideorg/js": "
|
|
58
|
+
"heimdall-tide": "0.14.43-staging",
|
|
59
|
+
"@tideorg/js": "0.14.43-staging"
|
|
81
60
|
}
|
|
82
61
|
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* The decision seam of `TideCloak.secureFetch`: given the caller's `Authorization`
|
|
4
|
-
* header (if any) and the access token this client currently holds, should we sign
|
|
5
|
-
* the request as our own - `Authorization: DPoP <token>` plus a `DPoP:` proof?
|
|
6
|
-
*
|
|
7
|
-
* Kept in its own dependency-free module so it can be unit-tested directly.
|
|
8
|
-
* `lib/tidecloak.js` pulls in `heimdall-tide`, which cannot be loaded under plain
|
|
9
|
-
* node ESM, so the predicate is unreachable from a test if it lives there - and
|
|
10
|
-
* this is precisely the seam where a mistake becomes an unexplainable `401`.
|
|
11
|
-
*/
|
|
12
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.shouldAttachDpopProof = shouldAttachDpopProof;
|
|
14
|
-
/**
|
|
15
|
-
* @param {string|null|undefined} existingAuth - the caller's `Authorization` header.
|
|
16
|
-
* @param {string} ourToken - the access token this client currently holds.
|
|
17
|
-
* @returns {boolean} true -> attach `Authorization: DPoP <ourToken>` + a DPoP proof.
|
|
18
|
-
*
|
|
19
|
-
* Three cases:
|
|
20
|
-
*
|
|
21
|
-
* - NO `Authorization` header: YES, attach. "I set no Authorization, use your
|
|
22
|
-
* token" is the entire point of calling `secureFetch` rather than `fetch`, and
|
|
23
|
-
* it is exactly what `secureFetch`'s own stale-token warning tells callers to do
|
|
24
|
-
* ("omit the Authorization header entirely and let secureFetch attach it").
|
|
25
|
-
*
|
|
26
|
-
* This case used to be false, which sent the request as a plain, UNAUTHENTICATED
|
|
27
|
-
* fetch: no token, no proof, no credentials of any kind. A consumer that reads
|
|
28
|
-
* its token from an async state container and calls before that state has
|
|
29
|
-
* settled (React state that has not yet committed, say) would silently ship a
|
|
30
|
-
* credential-less admin request and get back a bare `401` naming none of this.
|
|
31
|
-
* That is the admin-API 401 this closes.
|
|
32
|
-
*
|
|
33
|
-
* - OUR bearer token: YES, attach - upgrade `Bearer` to `DPoP` + proof. Unchanged.
|
|
34
|
-
*
|
|
35
|
-
* - SOMEONE ELSE'S bearer token: NO. A third-party API call is a legitimate
|
|
36
|
-
* pass-through: it goes out verbatim and must never be handed one of our proofs.
|
|
37
|
-
* Unchanged. (A stale copy of one of ours lands here too, and is warned about
|
|
38
|
-
* once by the caller.)
|
|
39
|
-
*/
|
|
40
|
-
function shouldAttachDpopProof(existingAuth, ourToken) {
|
|
41
|
-
if (existingAuth === null || existingAuth === undefined)
|
|
42
|
-
return true;
|
|
43
|
-
return existingAuth === `Bearer ${ourToken}`;
|
|
44
|
-
}
|
|
45
|
-
//# sourceMappingURL=secureFetchPolicy.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"secureFetchPolicy.js","sourceRoot":"","sources":["../../../lib/secureFetchPolicy.js"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,+BAAuC,YAAY,EAAE,QAAQ;IAC3D,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACpE,OAAO,YAAY,KAAK,UAAU,QAAQ,EAAE,CAAA;AAC9C,CAAC"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Serves tide_dpop_auth.html at /tide_dpop/iss/<hex>/aud/<hex>/tide_dpop_auth.html.
|
|
3
|
-
// Uses web Request/Response, so it drops into a Next.js route handler:
|
|
4
|
-
//
|
|
5
|
-
// // app/tide_dpop/[...path]/route.js
|
|
6
|
-
// export const { GET, HEAD } = createDpopRoute({ config });
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.createDpopRoute = createDpopRoute;
|
|
9
|
-
const dpopServer_js_1 = require("./dpopServer.js");
|
|
10
|
-
/**
|
|
11
|
-
* @param {Object} [options]
|
|
12
|
-
* @param {Object} [options.config] TideCloak adapter config (tidecloak.json). Used to
|
|
13
|
-
* reject requests for another realm or client. Omitted or empty, it serves any.
|
|
14
|
-
*/
|
|
15
|
-
function createDpopRoute({ config = {} } = {}) {
|
|
16
|
-
const { issuer, client } = (0, dpopServer_js_1.expectationsFromConfig)(config);
|
|
17
|
-
const handle = (request) => {
|
|
18
|
-
const result = (0, dpopServer_js_1.resolveDpopAuthRequest)({
|
|
19
|
-
pathname: new URL(request.url).pathname,
|
|
20
|
-
expectedIssuer: issuer,
|
|
21
|
-
expectedClient: client,
|
|
22
|
-
method: request.method,
|
|
23
|
-
});
|
|
24
|
-
if (!result)
|
|
25
|
-
return new Response('Not Found', { status: 404, headers: { 'Content-Type': 'text/plain' } });
|
|
26
|
-
return new Response(result.body, { status: result.status, headers: result.headers });
|
|
27
|
-
};
|
|
28
|
-
return { GET: handle, HEAD: handle };
|
|
29
|
-
}
|
|
30
|
-
//# sourceMappingURL=dpopRoute.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dpopRoute.js","sourceRoot":"","sources":["../../../src/dpopRoute.js"],"names":[],"mappings":";AAAA,oFAAoF;AACpF,uEAAuE;AACvE,EAAE;AACF,wCAAwC;AACxC,8DAA8D;;;AAE9D,mDAAiF;AAEjF;;;;GAIG;AACH,yBAAgC,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,EAAE;IAClD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAA,sCAAsB,EAAC,MAAM,CAAC,CAAC;IAE1D,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,IAAA,sCAAsB,EAAC;YACpC,QAAQ,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ;YACvC,cAAc,EAAE,MAAM;YACtB,cAAc,EAAE,MAAM;YACtB,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;QAC1G,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IACvF,CAAC,CAAC;IAEF,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACvC,CAAC"}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Framework-neutral core for serving tide_dpop_auth.html. No fs, crypto or
|
|
3
|
-
// Buffer, so it also runs on Edge. Adapters: ./dpopRoute.js and ./vite.js.
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.DPOP_AUTH_CSP = exports.DPOP_AUTH_HTML = void 0;
|
|
6
|
-
exports.dpopAuthHeaders = dpopAuthHeaders;
|
|
7
|
-
exports.hexToUtf8 = hexToUtf8;
|
|
8
|
-
exports.dpopAuthPathFor = dpopAuthPathFor;
|
|
9
|
-
exports.expectationsFromConfig = expectationsFromConfig;
|
|
10
|
-
exports.resolveDpopAuthRequest = resolveDpopAuthRequest;
|
|
11
|
-
const dpopAuthAsset_js_1 = require("./generated/dpopAuthAsset.js");
|
|
12
|
-
Object.defineProperty(exports, "DPOP_AUTH_HTML", { enumerable: true, get: function () { return dpopAuthAsset_js_1.DPOP_AUTH_HTML; } });
|
|
13
|
-
Object.defineProperty(exports, "DPOP_AUTH_CSP", { enumerable: true, get: function () { return dpopAuthAsset_js_1.DPOP_AUTH_CSP; } });
|
|
14
|
-
const PATH_RE = /^\/tide_dpop\/iss\/([0-9a-fA-F]+)\/aud\/([0-9a-fA-F]+)\/tide_dpop_auth\.html$/;
|
|
15
|
-
// X-Frame-Options or frame-ancestors would block the enclave's embed.
|
|
16
|
-
function dpopAuthHeaders() {
|
|
17
|
-
return {
|
|
18
|
-
'Content-Type': 'text/html; charset=utf-8',
|
|
19
|
-
'Content-Security-Policy': dpopAuthAsset_js_1.DPOP_AUTH_CSP,
|
|
20
|
-
'Allow-CSP-From': '*',
|
|
21
|
-
'Cache-Control': 'no-store',
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
function hexToUtf8(hex) {
|
|
25
|
-
if (!hex || hex.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(hex))
|
|
26
|
-
return null;
|
|
27
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
28
|
-
for (let i = 0; i < bytes.length; i++)
|
|
29
|
-
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
30
|
-
try {
|
|
31
|
-
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function dpopAuthPathFor(issuer, clientId) {
|
|
38
|
-
const hex = (s) => [...new TextEncoder().encode(s)].map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
39
|
-
return `/tide_dpop/iss/${hex(issuer)}/aud/${hex(clientId)}/tide_dpop_auth.html`;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Issuer and client id to accept, from a TideCloak adapter config.
|
|
43
|
-
* Either is undefined when the config does not carry it (e.g. the `{}` placeholder).
|
|
44
|
-
*/
|
|
45
|
-
function expectationsFromConfig(config = {}) {
|
|
46
|
-
const url = config['auth-server-url'];
|
|
47
|
-
return {
|
|
48
|
-
issuer: url && config.realm ? `${String(url).replace(/\/+$/, '')}/realms/${config.realm}` : undefined,
|
|
49
|
-
client: config.resource || undefined,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* The response for a request path, as `{ status, headers, body }`, or null when
|
|
54
|
-
* the path is not the DPoP page. Omitted expectations are not checked.
|
|
55
|
-
*/
|
|
56
|
-
function resolveDpopAuthRequest({ pathname, expectedIssuer, expectedClient, method = 'GET' }) {
|
|
57
|
-
const match = String(pathname !== null && pathname !== void 0 ? pathname : '').split('?')[0].match(PATH_RE);
|
|
58
|
-
if (!match)
|
|
59
|
-
return null;
|
|
60
|
-
const text = (status, body, extra = {}) => ({
|
|
61
|
-
status,
|
|
62
|
-
headers: { 'Content-Type': 'text/plain; charset=utf-8', 'Cache-Control': 'no-store', ...extra },
|
|
63
|
-
body,
|
|
64
|
-
});
|
|
65
|
-
const verb = String(method).toUpperCase();
|
|
66
|
-
if (verb !== 'GET' && verb !== 'HEAD')
|
|
67
|
-
return text(405, 'Method Not Allowed', { Allow: 'GET, HEAD' });
|
|
68
|
-
const issuer = hexToUtf8(match[1]);
|
|
69
|
-
const client = hexToUtf8(match[2]);
|
|
70
|
-
if (issuer === null || client === null)
|
|
71
|
-
return text(400, 'Invalid hex encoding in URL');
|
|
72
|
-
if ((expectedIssuer && issuer !== expectedIssuer) || (expectedClient && client !== expectedClient)) {
|
|
73
|
-
return text(403, 'Issuer or client mismatch');
|
|
74
|
-
}
|
|
75
|
-
return { status: 200, headers: dpopAuthHeaders(), body: verb === 'HEAD' ? null : dpopAuthAsset_js_1.DPOP_AUTH_HTML };
|
|
76
|
-
}
|
|
77
|
-
//# sourceMappingURL=dpopServer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dpopServer.js","sourceRoot":"","sources":["../../../src/dpopServer.js"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DPOP_AUTH_CSP = exports.DPOP_AUTH_HTML = void 0;
|
|
4
|
-
// Generated by scripts/gen-dpop-asset.cjs. Do not edit.
|
|
5
|
-
exports.DPOP_AUTH_HTML = "<html>\n\n<head>\n <style>\n body {\n margin: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100vh;\n background: #f6f6f7;\n font-family: -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n }\n\n .status {\n text-align: center;\n opacity: 0;\n }\n\n .status.visible {\n opacity: 1;\n }\n\n .checkmark-circle {\n width: 72px;\n height: 72px;\n margin: 0 auto 16px;\n }\n\n .checkmark-circle circle {\n fill: none;\n stroke: #039855;\n stroke-width: 3;\n stroke-dasharray: 201;\n stroke-dashoffset: 201;\n }\n\n .checkmark-circle path {\n fill: none;\n stroke: #039855;\n stroke-width: 3;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-dasharray: 50;\n stroke-dashoffset: 50;\n }\n\n .status.visible .checkmark-circle circle {\n animation: circle-draw 0.5s ease forwards;\n }\n\n .status.visible .checkmark-circle path {\n animation: check-draw 0.3s 0.4s ease forwards;\n }\n\n .status.visible .status-text {\n animation: fade-in 0.3s 0.6s ease forwards;\n }\n\n .status-text {\n font-size: 15px;\n color: #464647;\n margin: 0;\n opacity: 0;\n }\n\n @keyframes circle-draw {\n to {\n stroke-dashoffset: 0;\n }\n }\n\n @keyframes check-draw {\n to {\n stroke-dashoffset: 0;\n }\n }\n\n @keyframes fade-in {\n to {\n opacity: 1;\n }\n }\n </style>\n</head>\n\n<body>\n <div class=\"status\" id=\"status\">\n <svg class=\"checkmark-circle\" viewBox=\"0 0 72 72\">\n <circle cx=\"36\" cy=\"36\" r=\"32\" />\n <path d=\"M22 36 l10 10 l18 -20\" />\n </svg>\n <p class=\"status-text\">Session verified</p>\n </div>\n <script>\n const thisVersion = 1;\n\n const hexToStr = (hex) => {\n if (!hex || hex.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(hex)) throw Error(\"Invalid hex string\");\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(hex.substr(i * 2, 2), 16);\n return new TextDecoder().decode(bytes);\n };\n\n const createDbName = async (issuer, clientId) => {\n const encoder = new TextEncoder();\n const issuerHash = Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', encoder.encode(issuer))).slice(0, 8))\n .map(b => b.toString(16).padStart(2, '0')).join('');\n const clientHash = Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', encoder.encode(clientId))).slice(0, 8))\n .map(b => b.toString(16).padStart(2, '0')).join('');\n return `dpop:${issuerHash}:${clientHash}`;\n };\n\n // Determine target window: popup has window.opener, iframe has window.parent\n const isPopup = !!window.opener;\n const targetWindow = window.opener || window.parent;\n\n if (!targetWindow || targetWindow === window) {\n document.body.textContent = \"Error: No parent or opener window found.\";\n throw Error(\"No target window available for postMessage\");\n }\n\n const urlParams = new URL(window.location.href).searchParams;\n const enclaveVersion = urlParams.get(\"version\");\n if (!enclaveVersion) throw Error(\"No enclave version provided in url\");\n let openerOrigin = urlParams.get(\"openerOrigin\");\n if (!openerOrigin) throw Error(\"No opener origin provided in url\");\n openerOrigin = new URL(decodeURIComponent(openerOrigin)).origin;\n\n const showSuccess = () => {\n document.getElementById('status').classList.add('visible');\n };\n\n const version1Flow = async () => {\n const paths = new URL(window.location.href).pathname.split(\"/\");\n const iss = hexToStr(paths[paths.indexOf(\"iss\") + 1]);\n const aud = hexToStr(paths[paths.indexOf(\"aud\") + 1]);\n const dbname = await createDbName(iss, aud);\n let dpopKey;\n try {\n // Chrome 125+: requestStorageAccess({ indexedDB: true }) returns a handle\n // whose .indexedDB factory points directly at the unpartitioned bucket.\n // If permission was already granted (within 30 days) this resolves\n // automatically with no prompt.\n //\n // In popup mode we have first-party context so we can access IndexedDB directly.\n let idbFactory;\n if (isPopup) {\n idbFactory = window.indexedDB;\n } else {\n const handle = await document.requestStorageAccess({ indexedDB: true });\n idbFactory = handle.indexedDB;\n }\n\n const db = await new Promise((resolve, reject) => {\n const req = idbFactory.open(dbname);\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n dpopKey = await new Promise((resolve, reject) => {\n const tx = db.transaction(\"main\", \"readonly\");\n const store = tx.objectStore(\"main\");\n const req = store.get(\"dpopState\");\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n db.close();\n if (!dpopKey || !dpopKey.keys) throw Error(\"No DPoP key found in IndexedDB\");\n } catch (ex) {\n const isStorageBlocked =\n ex?.name === \"NotAllowedError\" ||\n (ex instanceof DOMException && (\n ex.name === \"SecurityError\" ||\n ex.name === \"UnknownError\" ||\n ex.message?.includes(\"not a known object store name\")\n ));\n const detail = isStorageBlocked\n ? \"Browser is blocking third-party storage. Please allow storage access or disable strict tracking protection.\"\n : \"Failed to access DPoP key from IndexedDB.\";\n targetWindow.postMessage({ type: \"error\", message: \"db failed\", detail }, openerOrigin);\n document.body.textContent = detail;\n throw ex;\n }\n\n const challenge = () => new Promise((resolve) => {\n const handler = (event) => {\n if (event.origin !== openerOrigin) return;\n if (event.data.type === \"challenge\") {\n resolve(event.data.challenge);\n window.removeEventListener(\"message\", handler);\n }\n };\n window.addEventListener(\"message\", handler, false);\n });\n\n const pre_challenge = challenge();\n\n targetWindow.postMessage({ type: \"pageLoaded\", message: \"yay\" }, openerOrigin);\n\n const challengeResp = await pre_challenge;\n if (typeof challengeResp !== \"string\" || challengeResp?.length != 64) throw Error(\"Unexpect challenge format\");\n\n const te = new TextEncoder();\n const signatureInput = te.encode(\"dpop-auth-challenge:\" + challengeResp);\n const algName = dpopKey.keys.privateKey.algorithm.name;\n const signParams = algName === \"ECDSA\"\n ? { name: \"ECDSA\", hash: { \"P-256\": \"SHA-256\", \"P-384\": \"SHA-384\", \"P-521\": \"SHA-512\" }[dpopKey.keys.privateKey.algorithm.namedCurve] }\n : { name: algName };\n const signature = await crypto.subtle.sign(signParams, dpopKey.keys.privateKey, signatureInput);\n\n const publicJwk = await crypto.subtle.exportKey(\"jwk\", dpopKey.keys.publicKey);\n\n targetWindow.postMessage({\n type: \"challenge response\",\n message: {\n dpop_public: JSON.stringify({ crv: publicJwk.crv, kty: publicJwk.kty, x: publicJwk.x, y: publicJwk.y }),\n signature: btoa(String.fromCharCode(...new Uint8Array(signature)))\n }\n }, openerOrigin);\n\n showSuccess();\n };\n\n const versionFlows = new Map([[1, version1Flow]]);\n const latestFlow = version1Flow;\n\n (async () => {\n if (Number(enclaveVersion) >= thisVersion) await latestFlow();\n else {\n const flow = versionFlows.get(Number(enclaveVersion));\n if (!flow) throw Error(\"Cannot find supported enclave version: \" + enclaveVersion);\n await flow();\n }\n })();\n </script>\n</body>\n\n</html>";
|
|
6
|
-
exports.DPOP_AUTH_CSP = "default-src 'self'; script-src 'self' 'sha256-utc6UrebuHOyLd/2aiMXS/p1EDy9UZBDe/XEMKDw9Mc='; style-src 'self' 'sha256-F7OJTdJYct4J+cQfuJUoDauitndqt8pAc8EbA8gwDPU='";
|
|
7
|
-
//# sourceMappingURL=dpopAuthAsset.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dpopAuthAsset.js","sourceRoot":"","sources":["../../../../src/generated/dpopAuthAsset.js"],"names":[],"mappings":";;;AAAA,wDAAwD;AAC3C,QAAA,cAAc,GAAG,uvSAAuvS,CAAC;AACzwS,QAAA,aAAa,GAAG,qKAAqK,CAAC"}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* @deprecated The policy editor is no longer part of @tidecloak/js. This entry
|
|
4
|
-
* point is kept so existing imports still resolve; every component throws when
|
|
5
|
-
* rendered. The @tidecloak/policy package it used to re-export was never
|
|
6
|
-
* published to npm.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.PolicyBuilder = PolicyBuilder;
|
|
10
|
-
exports.PolicyCanvas = PolicyCanvas;
|
|
11
|
-
exports.PropertiesPanel = PropertiesPanel;
|
|
12
|
-
exports.BlockPalette = BlockPalette;
|
|
13
|
-
const moved = (name) => new Error(`${name} from "@tidecloak/js/policy-react" is no longer available: the policy editor has moved out of @tidecloak/js.`);
|
|
14
|
-
/** @typedef {any} Policy */
|
|
15
|
-
/** @typedef {any} PolicyBlock */
|
|
16
|
-
/** @typedef {any} Model */
|
|
17
|
-
/** @typedef {any} ModelField */
|
|
18
|
-
/** @typedef {any} Claim */
|
|
19
|
-
/**
|
|
20
|
-
* @deprecated The policy editor has moved out of @tidecloak/js.
|
|
21
|
-
* @param {any} _props
|
|
22
|
-
* @returns {never}
|
|
23
|
-
*/
|
|
24
|
-
function PolicyBuilder(_props) {
|
|
25
|
-
throw moved("PolicyBuilder");
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* @deprecated The policy editor has moved out of @tidecloak/js.
|
|
29
|
-
* @param {any} _props
|
|
30
|
-
* @returns {never}
|
|
31
|
-
*/
|
|
32
|
-
function PolicyCanvas(_props) {
|
|
33
|
-
throw moved("PolicyCanvas");
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* @deprecated The policy editor has moved out of @tidecloak/js.
|
|
37
|
-
* @param {any} _props
|
|
38
|
-
* @returns {never}
|
|
39
|
-
*/
|
|
40
|
-
function PropertiesPanel(_props) {
|
|
41
|
-
throw moved("PropertiesPanel");
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* @deprecated The policy editor has moved out of @tidecloak/js.
|
|
45
|
-
* @param {any} _props
|
|
46
|
-
* @returns {never}
|
|
47
|
-
*/
|
|
48
|
-
function BlockPalette(_props) {
|
|
49
|
-
throw moved("BlockPalette");
|
|
50
|
-
}
|
|
51
|
-
//# sourceMappingURL=policy-react.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"policy-react.js","sourceRoot":"","sources":["../../../src/policy-react.js"],"names":[],"mappings":";AAAA;;;;;GAKG;;;;;;AAEH,MAAM,KAAK,GAAG,CAAC,IAAI,EAAE,EAAE,CACrB,IAAI,KAAK,CACP,GAAG,IAAI,8GAA8G,CACtH,CAAC;AAEJ,4BAA4B;AAC5B,iCAAiC;AACjC,2BAA2B;AAC3B,gCAAgC;AAChC,2BAA2B;AAE3B;;;;GAIG;AACH,uBAA8B,MAAM;IAClC,MAAM,KAAK,CAAC,eAAe,CAAC,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,sBAA6B,MAAM;IACjC,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,yBAAgC,MAAM;IACpC,MAAM,KAAK,CAAC,iBAAiB,CAAC,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,sBAA6B,MAAM;IACjC,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC;AAC9B,CAAC"}
|
package/dist/cjs/src/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":";AAAA;;GAEG"}
|