@enterprisestandard/react 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/enterprise-user.d.ts +124 -0
- package/dist/iam.d.ts +12 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +620 -0
- package/dist/oidc-schema.d.ts +43 -0
- package/dist/server.d.ts +5 -0
- package/dist/sso.d.ts +46 -0
- package/dist/standard-schema.d.ts +55 -0
- package/dist/ui/signed-in.d.ts +6 -0
- package/dist/useUser.d.ts +8 -0
- package/dist/utils.d.ts +8 -0
- package/dist/vault.d.ts +18 -0
- package/package.json +43 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export interface EnterpriseUser {
|
|
2
|
+
schemas?: string[];
|
|
3
|
+
id?: string;
|
|
4
|
+
externalId?: string;
|
|
5
|
+
meta?: {
|
|
6
|
+
resourceType?: string;
|
|
7
|
+
created?: string;
|
|
8
|
+
lastModified?: string;
|
|
9
|
+
version?: string;
|
|
10
|
+
location?: string;
|
|
11
|
+
};
|
|
12
|
+
userName: string;
|
|
13
|
+
name: string;
|
|
14
|
+
fullName?: {
|
|
15
|
+
formatted?: string;
|
|
16
|
+
familyName?: string;
|
|
17
|
+
givenName?: string;
|
|
18
|
+
middleName?: string;
|
|
19
|
+
honorificPrefix?: string;
|
|
20
|
+
honorificSuffix?: string;
|
|
21
|
+
};
|
|
22
|
+
nickName?: string;
|
|
23
|
+
profileUrl?: string;
|
|
24
|
+
avatarUrl?: string;
|
|
25
|
+
title?: string;
|
|
26
|
+
userType?: string;
|
|
27
|
+
preferredLanguage?: string;
|
|
28
|
+
locale?: string;
|
|
29
|
+
timezone?: string;
|
|
30
|
+
active?: boolean;
|
|
31
|
+
password?: string;
|
|
32
|
+
email: string;
|
|
33
|
+
emails?: Array<{
|
|
34
|
+
value: string;
|
|
35
|
+
type?: string;
|
|
36
|
+
primary?: boolean;
|
|
37
|
+
display?: string;
|
|
38
|
+
}>;
|
|
39
|
+
phoneNumbers?: Array<{
|
|
40
|
+
value: string;
|
|
41
|
+
type?: string;
|
|
42
|
+
primary?: boolean;
|
|
43
|
+
display?: string;
|
|
44
|
+
}>;
|
|
45
|
+
ims?: Array<{
|
|
46
|
+
value: string;
|
|
47
|
+
type?: string;
|
|
48
|
+
primary?: boolean;
|
|
49
|
+
display?: string;
|
|
50
|
+
}>;
|
|
51
|
+
photos?: Array<{
|
|
52
|
+
value: string;
|
|
53
|
+
type?: string;
|
|
54
|
+
primary?: boolean;
|
|
55
|
+
display?: string;
|
|
56
|
+
}>;
|
|
57
|
+
addresses?: Array<{
|
|
58
|
+
formatted?: string;
|
|
59
|
+
streetAddress?: string;
|
|
60
|
+
locality?: string;
|
|
61
|
+
region?: string;
|
|
62
|
+
postalCode?: string;
|
|
63
|
+
country?: string;
|
|
64
|
+
type?: string;
|
|
65
|
+
primary?: boolean;
|
|
66
|
+
}>;
|
|
67
|
+
groups?: Array<{
|
|
68
|
+
value: string;
|
|
69
|
+
$ref?: string;
|
|
70
|
+
display?: string;
|
|
71
|
+
type?: string;
|
|
72
|
+
}>;
|
|
73
|
+
entitlements?: Array<{
|
|
74
|
+
value: string;
|
|
75
|
+
display?: string;
|
|
76
|
+
type?: string;
|
|
77
|
+
primary?: boolean;
|
|
78
|
+
}>;
|
|
79
|
+
roles?: Array<{
|
|
80
|
+
value: string;
|
|
81
|
+
display?: string;
|
|
82
|
+
type?: string;
|
|
83
|
+
primary?: boolean;
|
|
84
|
+
}>;
|
|
85
|
+
x509Certificates?: Array<{
|
|
86
|
+
value: string;
|
|
87
|
+
display?: string;
|
|
88
|
+
type?: string;
|
|
89
|
+
primary?: boolean;
|
|
90
|
+
}>;
|
|
91
|
+
employeeNumber?: string;
|
|
92
|
+
costCenter?: string;
|
|
93
|
+
organization?: string;
|
|
94
|
+
division?: string;
|
|
95
|
+
department?: string;
|
|
96
|
+
manager?: {
|
|
97
|
+
value: string;
|
|
98
|
+
$ref?: string;
|
|
99
|
+
displayName?: string;
|
|
100
|
+
};
|
|
101
|
+
sso?: {
|
|
102
|
+
profile: IdTokenClaims;
|
|
103
|
+
tenant: {
|
|
104
|
+
id: string;
|
|
105
|
+
name: string;
|
|
106
|
+
};
|
|
107
|
+
scope?: string;
|
|
108
|
+
tokenType: string;
|
|
109
|
+
sessionState?: string;
|
|
110
|
+
expires: Date;
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export type IdTokenClaims = {
|
|
114
|
+
iss?: string;
|
|
115
|
+
aud?: string;
|
|
116
|
+
exp?: number;
|
|
117
|
+
iat?: number;
|
|
118
|
+
sub?: string;
|
|
119
|
+
name?: string;
|
|
120
|
+
email?: string;
|
|
121
|
+
preferred_username?: string;
|
|
122
|
+
picture?: string;
|
|
123
|
+
[key: string]: unknown;
|
|
124
|
+
};
|
package/dist/iam.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type IAMConfig = {
|
|
2
|
+
url: string;
|
|
3
|
+
userEndpoint: string;
|
|
4
|
+
groupEndpoint: string;
|
|
5
|
+
};
|
|
6
|
+
export type IAM = {
|
|
7
|
+
url: string;
|
|
8
|
+
userEndpoint: string;
|
|
9
|
+
groupEndpoint: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function iam(config: IAMConfig): Promise<IAM>;
|
|
12
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SSOManager } from './sso';
|
|
2
|
+
import { type Vault } from './vault';
|
|
3
|
+
import { type IAM } from './iam';
|
|
4
|
+
export type EnterpriseStandard = {
|
|
5
|
+
ioniteUrl: string;
|
|
6
|
+
appId: string;
|
|
7
|
+
defaultInstance: boolean;
|
|
8
|
+
vault: Vault;
|
|
9
|
+
sso?: SSOManager;
|
|
10
|
+
iam?: IAM;
|
|
11
|
+
};
|
|
12
|
+
type ESConfig = {
|
|
13
|
+
ioniteUrl?: string;
|
|
14
|
+
defaultInstance?: boolean;
|
|
15
|
+
ssoUserUrl?: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function enterpriseStandard(appId: string, appKey?: string, initConfig?: ESConfig): Promise<EnterpriseStandard>;
|
|
18
|
+
export type * from './enterprise-user';
|
|
19
|
+
export { oidcCallbackSchema } from './oidc-schema';
|
|
20
|
+
export * from './server';
|
|
21
|
+
export { useUser } from './useUser';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
// src/utils.ts
|
|
2
|
+
var defaultInstance;
|
|
3
|
+
function must(value, message = "Assertion failed. Required value is null or undefined.") {
|
|
4
|
+
if (value === undefined || value === null) {
|
|
5
|
+
throw new Error(message);
|
|
6
|
+
}
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
function setDefaultInstance(es) {
|
|
10
|
+
defaultInstance = es;
|
|
11
|
+
}
|
|
12
|
+
function getDefaultInstance() {
|
|
13
|
+
return defaultInstance;
|
|
14
|
+
}
|
|
15
|
+
function getES(es) {
|
|
16
|
+
if (es)
|
|
17
|
+
return es;
|
|
18
|
+
if (defaultInstance)
|
|
19
|
+
return defaultInstance;
|
|
20
|
+
throw new Error(`TODO standardize the error message when there isn't a default EntepriseStandard`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/sso.ts
|
|
24
|
+
var jwksCache = new Map;
|
|
25
|
+
|
|
26
|
+
class SSOManager {
|
|
27
|
+
config;
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.config = {
|
|
30
|
+
...config,
|
|
31
|
+
secure: config.secure === undefined || config.secure === true ? true : false,
|
|
32
|
+
sameSite: config.sameSite === undefined || config.sameSite === "Strict" ? "Strict" : "Lax",
|
|
33
|
+
cookiePrefix: config.cookiePrefix ?? `es.sso.${config.client_id}`,
|
|
34
|
+
cookiePath: config.cookiePath ?? "/"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async getUser(request) {
|
|
38
|
+
if (!this.config) {
|
|
39
|
+
console.error("SSO Manager not initialized");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const token = await this.getJwt(request);
|
|
44
|
+
if (!token)
|
|
45
|
+
return;
|
|
46
|
+
return await this.parseUser(token);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error("Error parsing user from cookies:", error);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async getRequiredUser(request) {
|
|
53
|
+
const user = await this.getUser(request);
|
|
54
|
+
if (user)
|
|
55
|
+
return user;
|
|
56
|
+
throw new Response("Unauthorized", {
|
|
57
|
+
status: 401,
|
|
58
|
+
statusText: "Unauthorized"
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async initiateLogin(landingPage, errorPage) {
|
|
62
|
+
if (!this.config) {
|
|
63
|
+
console.error("SSO Manager not initialized");
|
|
64
|
+
return Promise.resolve(new Response("SSO Manager not initialized", { status: 503 }));
|
|
65
|
+
}
|
|
66
|
+
const state = this.generateRandomString();
|
|
67
|
+
const codeVerifier = this.generateRandomString(64);
|
|
68
|
+
const url = new URL(this.config.authorization_url);
|
|
69
|
+
url.searchParams.append("client_id", this.config.client_id);
|
|
70
|
+
url.searchParams.append("redirect_uri", this.config.redirect_uri);
|
|
71
|
+
url.searchParams.append("response_type", "code");
|
|
72
|
+
url.searchParams.append("scope", this.config.scope);
|
|
73
|
+
url.searchParams.append("state", state);
|
|
74
|
+
const codeChallenge = await this.pkceChallengeFromVerifier(codeVerifier);
|
|
75
|
+
url.searchParams.append("code_challenge", codeChallenge);
|
|
76
|
+
url.searchParams.append("code_challenge_method", "S256");
|
|
77
|
+
const val = {
|
|
78
|
+
state,
|
|
79
|
+
codeVerifier,
|
|
80
|
+
landingPage,
|
|
81
|
+
errorPage
|
|
82
|
+
};
|
|
83
|
+
return new Response("Redirecting to SSO Provider", {
|
|
84
|
+
status: 302,
|
|
85
|
+
headers: {
|
|
86
|
+
Location: url.toString(),
|
|
87
|
+
"Set-Cookie": this.createCookie("state", val, 86400)
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async callbackHandler(request) {
|
|
92
|
+
if (!this.config) {
|
|
93
|
+
console.error("SSO Manager not initialized");
|
|
94
|
+
return Promise.resolve(new Response("SSO Manager not initialized", { status: 503 }));
|
|
95
|
+
}
|
|
96
|
+
const url = new URL(request.url);
|
|
97
|
+
const params = new URLSearchParams(url.search);
|
|
98
|
+
try {
|
|
99
|
+
const codeFromUrl = must(params.get("code"), 'OIDC "code" was not passed as a search param, ensure that the SSO login completed successfully');
|
|
100
|
+
const stateFromUrl = must(params.get("state"), 'OIDC "state" was not passed as a search param, ensure that the SSO login completed successfully');
|
|
101
|
+
const cookie = this.getCookie("state", request, true);
|
|
102
|
+
const { codeVerifier, state, landingPage } = cookie ?? {};
|
|
103
|
+
must(codeVerifier, 'OIDC "codeVerifier" was not present in cookies, ensure that the SSO login was initiated correctly');
|
|
104
|
+
must(state, 'OIDC "stateVerifier" was not present in cookies, ensure that the SSO login was initiated correctly');
|
|
105
|
+
must(landingPage, 'OIDC "landingPage" was not present in cookies');
|
|
106
|
+
if (stateFromUrl !== state) {
|
|
107
|
+
throw new Error('SSO State Verifier failed, the "state" request parameter does not equal the "state" in the SSO cookie');
|
|
108
|
+
}
|
|
109
|
+
const tokenResponse = await this.exchangeCodeForToken(codeFromUrl, codeVerifier);
|
|
110
|
+
const user = await this.parseUser(tokenResponse);
|
|
111
|
+
return new Response("Authentication successful, redirecting", {
|
|
112
|
+
status: 302,
|
|
113
|
+
headers: [
|
|
114
|
+
["Location", landingPage],
|
|
115
|
+
["Set-Cookie", this.clearCookie("state")],
|
|
116
|
+
...this.createJwtCookies(tokenResponse, user.sso.expires)
|
|
117
|
+
]
|
|
118
|
+
});
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error("Error during sign-in callback:", error);
|
|
121
|
+
try {
|
|
122
|
+
const cookie = this.getCookie("state", request, true);
|
|
123
|
+
const { errorPage } = cookie ?? {};
|
|
124
|
+
if (errorPage) {
|
|
125
|
+
return new Response("Redirecting to error page", {
|
|
126
|
+
status: 302,
|
|
127
|
+
headers: [
|
|
128
|
+
["Location", errorPage]
|
|
129
|
+
]
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.warn("Error parsing the errorPage from the OIDC cookie");
|
|
134
|
+
}
|
|
135
|
+
console.warn("No error page was found in the cookies. The user will be shown a default error page.");
|
|
136
|
+
return new Response("An error occurred during authentication, please return to the application homepage and try again.", {
|
|
137
|
+
status: 500
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async parseUser(token) {
|
|
142
|
+
if (!this.config)
|
|
143
|
+
throw new Error("SSO Manager not initialized");
|
|
144
|
+
const idToken = await this.parseJwt(token.id_token);
|
|
145
|
+
const expiresIn = Number(token.refresh_expires_in ?? token.expires_in ?? 3600);
|
|
146
|
+
const expires = token.expires ? new Date(token.expires) : new Date(Date.now() + expiresIn * 1000);
|
|
147
|
+
return {
|
|
148
|
+
userName: idToken.preferred_username || "",
|
|
149
|
+
name: idToken.name || "",
|
|
150
|
+
email: idToken.email || "",
|
|
151
|
+
emails: [{
|
|
152
|
+
value: idToken.email || "",
|
|
153
|
+
primary: true
|
|
154
|
+
}],
|
|
155
|
+
avatarUrl: idToken.picture,
|
|
156
|
+
sso: {
|
|
157
|
+
profile: {
|
|
158
|
+
...idToken,
|
|
159
|
+
iss: idToken.iss || this.config.authority,
|
|
160
|
+
aud: idToken.aud || this.config.client_id
|
|
161
|
+
},
|
|
162
|
+
tenant: {
|
|
163
|
+
id: idToken.idp || idToken.iss || this.config.authority,
|
|
164
|
+
name: idToken.iss || this.config.authority
|
|
165
|
+
},
|
|
166
|
+
scope: token.scope,
|
|
167
|
+
tokenType: token.token_type,
|
|
168
|
+
sessionState: token.session_state,
|
|
169
|
+
expires
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async exchangeCodeForToken(code, codeVerifier) {
|
|
174
|
+
if (!this.config)
|
|
175
|
+
throw new Error("SSO Manager not initialized");
|
|
176
|
+
const tokenUrl = this.config.token_url;
|
|
177
|
+
const body = new URLSearchParams;
|
|
178
|
+
body.append("grant_type", "authorization_code");
|
|
179
|
+
body.append("code", code);
|
|
180
|
+
body.append("redirect_uri", this.config.redirect_uri);
|
|
181
|
+
body.append("client_id", this.config.client_id);
|
|
182
|
+
body.append("code_verifier", codeVerifier);
|
|
183
|
+
try {
|
|
184
|
+
const response = await fetch(tokenUrl, {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: {
|
|
187
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
188
|
+
Accept: "application/json"
|
|
189
|
+
},
|
|
190
|
+
body: body.toString()
|
|
191
|
+
});
|
|
192
|
+
const data = await response.json();
|
|
193
|
+
if (!response.ok) {
|
|
194
|
+
console.error("Token exchange error:", data);
|
|
195
|
+
throw new Error(`Token exchange failed: ${data.error || response.statusText} - ${data.error_description || ""}`.trim());
|
|
196
|
+
}
|
|
197
|
+
return data;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error("Error during token exchange:", error);
|
|
200
|
+
throw new Error("Error during token exchange");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async refreshToken(refreshToken) {
|
|
204
|
+
return this.retryWithBackoff(async () => {
|
|
205
|
+
if (!this.config)
|
|
206
|
+
throw new Error("SSO Manager not initialized");
|
|
207
|
+
const tokenUrl = this.config.token_url;
|
|
208
|
+
const body = new URLSearchParams;
|
|
209
|
+
body.append("grant_type", "refresh_token");
|
|
210
|
+
body.append("refresh_token", refreshToken);
|
|
211
|
+
body.append("client_id", this.config.client_id);
|
|
212
|
+
const response = await fetch(tokenUrl, {
|
|
213
|
+
method: "POST",
|
|
214
|
+
headers: {
|
|
215
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
216
|
+
Accept: "application/json"
|
|
217
|
+
},
|
|
218
|
+
body: body.toString()
|
|
219
|
+
});
|
|
220
|
+
const data = await response.json();
|
|
221
|
+
if (!response.ok) {
|
|
222
|
+
console.error("Token refresh error:", data);
|
|
223
|
+
throw new Error(`Token refresh failed: ${data.error || response.statusText} - ${data.error_description || ""}`.trim());
|
|
224
|
+
}
|
|
225
|
+
return data;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
async fetchJwks() {
|
|
229
|
+
const url = this.config.jwks_uri || `${this.config.authority}/protocol/openid-connect/certs`;
|
|
230
|
+
if (jwksCache.has(url))
|
|
231
|
+
return jwksCache.get(url);
|
|
232
|
+
return this.retryWithBackoff(async () => {
|
|
233
|
+
if (!this.config)
|
|
234
|
+
throw new Error("SSO Manager not initialized");
|
|
235
|
+
const response = await fetch(url);
|
|
236
|
+
if (!response.ok)
|
|
237
|
+
throw new Error("Failed to fetch JWKS");
|
|
238
|
+
const jwks = await response.json();
|
|
239
|
+
jwksCache.set(url, jwks);
|
|
240
|
+
return jwks;
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
async retryWithBackoff(operation, maxRetries = 3, baseDelay = 1000, maxDelay = 30000) {
|
|
244
|
+
let lastError = new Error("Placeholder Error");
|
|
245
|
+
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
246
|
+
try {
|
|
247
|
+
return await operation();
|
|
248
|
+
} catch (error) {
|
|
249
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
250
|
+
if (error instanceof Error && error.message.includes("400")) {
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
if (attempt === maxRetries) {
|
|
254
|
+
throw lastError;
|
|
255
|
+
}
|
|
256
|
+
const delay = Math.min(baseDelay * 2 ** attempt, maxDelay);
|
|
257
|
+
const jitter = Math.random() * 0.1 * delay;
|
|
258
|
+
await new Promise((resolve) => setTimeout(resolve, delay + jitter));
|
|
259
|
+
console.warn(`Retry attempt ${attempt + 1} after ${delay + jitter}ms delay`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
throw lastError;
|
|
263
|
+
}
|
|
264
|
+
async parseJwt(token) {
|
|
265
|
+
try {
|
|
266
|
+
const parts = token.split(".");
|
|
267
|
+
if (parts.length !== 3)
|
|
268
|
+
throw new Error("Invalid JWT");
|
|
269
|
+
const header = JSON.parse(atob(parts[0].replace(/-/g, "+").replace(/_/g, "/")));
|
|
270
|
+
const payload = JSON.parse(atob(parts[1].replace(/-/g, "+").replace(/_/g, "/")));
|
|
271
|
+
const signature = parts[2].replace(/-/g, "+").replace(/_/g, "/");
|
|
272
|
+
const publicKey = await this.getPublicKey(header.kid);
|
|
273
|
+
const encoder = new TextEncoder;
|
|
274
|
+
const data = encoder.encode(`${parts[0]}.${parts[1]}`);
|
|
275
|
+
const isValid = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", publicKey, Uint8Array.from(atob(signature), (c) => c.charCodeAt(0)), data);
|
|
276
|
+
if (!isValid)
|
|
277
|
+
throw new Error("Invalid JWT signature");
|
|
278
|
+
return payload;
|
|
279
|
+
} catch (e) {
|
|
280
|
+
console.error("Error verifying JWT:", e);
|
|
281
|
+
throw e;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
generateRandomString(length = 32) {
|
|
285
|
+
const array = new Uint8Array(length);
|
|
286
|
+
crypto.getRandomValues(array);
|
|
287
|
+
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("").substring(0, length);
|
|
288
|
+
}
|
|
289
|
+
async pkceChallengeFromVerifier(verifier) {
|
|
290
|
+
const encoder = new TextEncoder;
|
|
291
|
+
const data = encoder.encode(verifier);
|
|
292
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
293
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
294
|
+
const hashBase64 = btoa(String.fromCharCode(...hashArray)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
295
|
+
return hashBase64;
|
|
296
|
+
}
|
|
297
|
+
async getPublicKey(kid) {
|
|
298
|
+
const jwks = await this.fetchJwks();
|
|
299
|
+
const key = jwks.keys.find((k) => k.kid === kid);
|
|
300
|
+
if (!key)
|
|
301
|
+
throw new Error("Public key not found");
|
|
302
|
+
const publicKey = await crypto.subtle.importKey("jwk", {
|
|
303
|
+
kty: key.kty,
|
|
304
|
+
n: key.n,
|
|
305
|
+
e: key.e
|
|
306
|
+
}, { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["verify"]);
|
|
307
|
+
return publicKey;
|
|
308
|
+
}
|
|
309
|
+
createJwtCookies(token, expires) {
|
|
310
|
+
const control = {
|
|
311
|
+
expires_in: token.expires_in,
|
|
312
|
+
refresh_expires_in: token.refresh_expires_in,
|
|
313
|
+
scope: token.scope,
|
|
314
|
+
session_state: token.session_state,
|
|
315
|
+
token_type: token.token_type,
|
|
316
|
+
expires: expires.toISOString()
|
|
317
|
+
};
|
|
318
|
+
return [
|
|
319
|
+
["Set-Cookie", this.createCookie("access", token.access_token, expires)],
|
|
320
|
+
["Set-Cookie", this.createCookie("id", token.id_token, expires)],
|
|
321
|
+
["Set-Cookie", this.createCookie("refresh", token.refresh_token ?? "", expires)],
|
|
322
|
+
["Set-Cookie", this.createCookie("control", control, expires)]
|
|
323
|
+
];
|
|
324
|
+
}
|
|
325
|
+
async getJwt(req) {
|
|
326
|
+
const access_token = this.getCookie("access", req);
|
|
327
|
+
const id_token = this.getCookie("id", req);
|
|
328
|
+
const refresh_token = this.getCookie("refresh", req);
|
|
329
|
+
const control = this.getCookie("control", req, true);
|
|
330
|
+
if (!access_token || !id_token || !refresh_token || !control) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
let tokenResponse = {
|
|
334
|
+
access_token,
|
|
335
|
+
id_token,
|
|
336
|
+
refresh_token,
|
|
337
|
+
...control
|
|
338
|
+
};
|
|
339
|
+
if (control.expires && refresh_token && Date.now() > new Date(control.expires).getTime()) {
|
|
340
|
+
tokenResponse = await this.refreshToken(refresh_token);
|
|
341
|
+
}
|
|
342
|
+
return tokenResponse;
|
|
343
|
+
}
|
|
344
|
+
createCookie(name, value, expires) {
|
|
345
|
+
name = `${this.config.cookiePrefix}.${name}`;
|
|
346
|
+
if (typeof value !== "string") {
|
|
347
|
+
value = btoa(JSON.stringify(value));
|
|
348
|
+
}
|
|
349
|
+
let exp;
|
|
350
|
+
if (expires instanceof Date) {
|
|
351
|
+
exp = `Expires=${expires.toUTCString()}`;
|
|
352
|
+
} else if (typeof expires === "number") {
|
|
353
|
+
exp = `Max-Age=${expires}`;
|
|
354
|
+
} else {
|
|
355
|
+
throw new Error("Invalid expires type", expires);
|
|
356
|
+
}
|
|
357
|
+
if (value.length > 4000) {
|
|
358
|
+
throw new Error(`Error setting cookie: ${name}. Cookie length is: ${value.length}`);
|
|
359
|
+
}
|
|
360
|
+
return `${name}=${value}; ${exp}; Path=${this.config.cookiePath}; HttpOnly;${this.config.secure ? " Secure;" : ""} SameSite=${this.config.sameSite};`;
|
|
361
|
+
}
|
|
362
|
+
clearCookie(name) {
|
|
363
|
+
return `${this.config.cookiePrefix}.${name}=; Max-Age=0; Path=${this.config.cookiePath}; HttpOnly;${this.config.secure ? " Secure;" : ""} SameSite=${this.config.sameSite};`;
|
|
364
|
+
}
|
|
365
|
+
getCookie(name, req, parse = false) {
|
|
366
|
+
const header = req.headers.get("cookie");
|
|
367
|
+
if (!header)
|
|
368
|
+
return null;
|
|
369
|
+
const cookie = header.split(";").find((row) => row.trim().startsWith(`${this.config.cookiePrefix}.${name}=`));
|
|
370
|
+
if (!cookie)
|
|
371
|
+
return null;
|
|
372
|
+
const val = cookie.split("=")[1].trim();
|
|
373
|
+
if (!parse)
|
|
374
|
+
return val;
|
|
375
|
+
const str = atob(val);
|
|
376
|
+
return JSON.parse(str);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/vault.ts
|
|
381
|
+
async function vault(url, token) {
|
|
382
|
+
async function getFullSecret(path) {
|
|
383
|
+
const resp = await fetch(`${url}/${path}`, { headers: { "X-Vault-Token": token } });
|
|
384
|
+
if (resp.status !== 200) {
|
|
385
|
+
throw new Error(`Vault returned invalid status, ${resp.status}: '${resp.statusText}' from URL: ${url}`);
|
|
386
|
+
}
|
|
387
|
+
const secret = await resp.json();
|
|
388
|
+
return secret.data;
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
url,
|
|
392
|
+
getFullSecret,
|
|
393
|
+
getSecret: async (path) => {
|
|
394
|
+
return (await getFullSecret(path)).data;
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// src/iam.ts
|
|
400
|
+
async function iam(config) {
|
|
401
|
+
return {
|
|
402
|
+
url: config.url,
|
|
403
|
+
userEndpoint: config.userEndpoint,
|
|
404
|
+
groupEndpoint: config.groupEndpoint
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/oidc-schema.ts
|
|
409
|
+
function oidcCallbackSchema(vendor) {
|
|
410
|
+
return {
|
|
411
|
+
"~standard": {
|
|
412
|
+
version: 1,
|
|
413
|
+
vendor,
|
|
414
|
+
validate: (value) => {
|
|
415
|
+
if (typeof value !== "object" || value === null) {
|
|
416
|
+
return {
|
|
417
|
+
issues: [
|
|
418
|
+
{
|
|
419
|
+
message: "Expected an object"
|
|
420
|
+
}
|
|
421
|
+
]
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
const params = value;
|
|
425
|
+
const issues = [];
|
|
426
|
+
const result = {};
|
|
427
|
+
if ("code" in params) {
|
|
428
|
+
if (typeof params.code === "string") {
|
|
429
|
+
result.code = params.code;
|
|
430
|
+
} else {
|
|
431
|
+
issues.push({
|
|
432
|
+
message: "code must be a string",
|
|
433
|
+
path: ["code"]
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
} else if (!("error" in params)) {
|
|
437
|
+
issues.push({
|
|
438
|
+
message: "code is required",
|
|
439
|
+
path: ["code"]
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
if ("state" in params) {
|
|
443
|
+
if (typeof params.state === "string" || params.state === undefined) {
|
|
444
|
+
result.state = params.state;
|
|
445
|
+
} else {
|
|
446
|
+
issues.push({
|
|
447
|
+
message: "state must be a string",
|
|
448
|
+
path: ["state"]
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if ("session_state" in params) {
|
|
453
|
+
if (typeof params.session_state === "string" || params.session_state === undefined) {
|
|
454
|
+
result.session_state = params.session_state;
|
|
455
|
+
} else {
|
|
456
|
+
issues.push({
|
|
457
|
+
message: "session_state must be a string",
|
|
458
|
+
path: ["session_state"]
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if ("error" in params) {
|
|
463
|
+
if (typeof params.error === "string") {
|
|
464
|
+
result.error = params.error;
|
|
465
|
+
} else {
|
|
466
|
+
issues.push({
|
|
467
|
+
message: "error must be a string",
|
|
468
|
+
path: ["error"]
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
if ("error_description" in params) {
|
|
472
|
+
if (typeof params.error_description === "string" || params.error_description === undefined) {
|
|
473
|
+
result.error_description = params.error_description;
|
|
474
|
+
} else {
|
|
475
|
+
issues.push({
|
|
476
|
+
message: "error_description must be a string",
|
|
477
|
+
path: ["error_description"]
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
if ("error_uri" in params) {
|
|
482
|
+
if (typeof params.error_uri === "string" || params.error_uri === undefined) {
|
|
483
|
+
result.error_uri = params.error_uri;
|
|
484
|
+
} else {
|
|
485
|
+
issues.push({
|
|
486
|
+
message: "error_uri must be a string",
|
|
487
|
+
path: ["error_uri"]
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if ("iss" in params) {
|
|
493
|
+
if (typeof params.iss === "string" || params.iss === undefined) {
|
|
494
|
+
result.iss = params.iss;
|
|
495
|
+
} else {
|
|
496
|
+
issues.push({
|
|
497
|
+
message: "iss must be a string",
|
|
498
|
+
path: ["iss"]
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
if (issues.length > 0) {
|
|
503
|
+
return { issues };
|
|
504
|
+
}
|
|
505
|
+
return { value: result };
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
// src/server.ts
|
|
511
|
+
function getSSO(es) {
|
|
512
|
+
es = getES(es);
|
|
513
|
+
if (!es.sso) {
|
|
514
|
+
console.error("TODO tell them how to connect SSO");
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
return es.sso;
|
|
518
|
+
}
|
|
519
|
+
function unavailable() {
|
|
520
|
+
new Response(JSON.stringify({ error: "SSO Unavailable" }), {
|
|
521
|
+
status: 503,
|
|
522
|
+
statusText: "SSO Unavailable",
|
|
523
|
+
headers: { "Content-Type": "application/json" }
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
async function getUser(request, es) {
|
|
527
|
+
return getSSO(es)?.getUser(request);
|
|
528
|
+
}
|
|
529
|
+
async function getRequiredUser(request, es) {
|
|
530
|
+
const sso = getSSO(es);
|
|
531
|
+
if (!sso)
|
|
532
|
+
throw unavailable();
|
|
533
|
+
return sso.getRequiredUser(request);
|
|
534
|
+
}
|
|
535
|
+
async function initiateLogin(landingPage, errorPage, es) {
|
|
536
|
+
const sso = getSSO(es);
|
|
537
|
+
if (!sso)
|
|
538
|
+
throw unavailable();
|
|
539
|
+
return sso.initiateLogin(landingPage, errorPage);
|
|
540
|
+
}
|
|
541
|
+
async function callback(request, es) {
|
|
542
|
+
const sso = getSSO(es);
|
|
543
|
+
if (!sso)
|
|
544
|
+
throw unavailable();
|
|
545
|
+
return sso.callbackHandler(request);
|
|
546
|
+
}
|
|
547
|
+
// src/useUser.ts
|
|
548
|
+
import { useState, useEffect } from "react";
|
|
549
|
+
function useUser(apiUrl) {
|
|
550
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
551
|
+
const [user, setUser] = useState(undefined);
|
|
552
|
+
const [status, setStatus] = useState(undefined);
|
|
553
|
+
const resolvedApiUrl = apiUrl || "/api/user";
|
|
554
|
+
useEffect(() => {
|
|
555
|
+
const fetchUser = async () => {
|
|
556
|
+
try {
|
|
557
|
+
const response = await fetch(resolvedApiUrl);
|
|
558
|
+
setStatus(response.status);
|
|
559
|
+
if (response.ok) {
|
|
560
|
+
const userData = await response.json();
|
|
561
|
+
setUser(userData);
|
|
562
|
+
} else {
|
|
563
|
+
setUser(undefined);
|
|
564
|
+
}
|
|
565
|
+
} catch (error) {
|
|
566
|
+
console.error("Error fetching user:", error);
|
|
567
|
+
setUser(undefined);
|
|
568
|
+
}
|
|
569
|
+
setIsLoading(false);
|
|
570
|
+
};
|
|
571
|
+
fetchUser();
|
|
572
|
+
}, [resolvedApiUrl]);
|
|
573
|
+
return { isLoading, user, status };
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// src/index.ts
|
|
577
|
+
async function enterpriseStandard(appId, appKey, initConfig) {
|
|
578
|
+
let vaultUrl;
|
|
579
|
+
let vaultToken;
|
|
580
|
+
let paths;
|
|
581
|
+
const ioniteUrl = initConfig?.ioniteUrl ?? "https://ionite.com";
|
|
582
|
+
if (appId === "IONITE_PUBLIC_DEMO") {
|
|
583
|
+
vaultUrl = "https://vault.ionite.dev/v1/secret/data";
|
|
584
|
+
vaultToken = "hvs.qIM9R0hmdnVCQmdvWdDGPSZ4";
|
|
585
|
+
paths = { sso: "ionite/DEV01K2JRP1DWXFCZN9FRDT37J1Y0" };
|
|
586
|
+
} else if (appKey) {
|
|
587
|
+
if (!vaultUrl || !vaultToken) {
|
|
588
|
+
throw new Error("TODO something is wrong with the ionite config, handle this error");
|
|
589
|
+
}
|
|
590
|
+
paths = {};
|
|
591
|
+
} else {
|
|
592
|
+
throw new Error("TODO tell them how to connect to ionite");
|
|
593
|
+
}
|
|
594
|
+
const defaultInstance2 = getDefaultInstance();
|
|
595
|
+
const vaultClient = await vault(vaultUrl, vaultToken);
|
|
596
|
+
const result = {
|
|
597
|
+
appId,
|
|
598
|
+
ioniteUrl,
|
|
599
|
+
defaultInstance: initConfig?.defaultInstance || initConfig?.defaultInstance !== false && !defaultInstance2,
|
|
600
|
+
vault: vaultClient,
|
|
601
|
+
sso: paths.sso ? new SSOManager(await vaultClient.getSecret(paths.sso)) : undefined,
|
|
602
|
+
iam: paths.iam ? await iam(await vaultClient.getSecret(paths.iam)) : undefined
|
|
603
|
+
};
|
|
604
|
+
if (result.defaultInstance) {
|
|
605
|
+
if (defaultInstance2) {
|
|
606
|
+
defaultInstance2.defaultInstance = false;
|
|
607
|
+
}
|
|
608
|
+
setDefaultInstance(result);
|
|
609
|
+
}
|
|
610
|
+
return result;
|
|
611
|
+
}
|
|
612
|
+
export {
|
|
613
|
+
useUser,
|
|
614
|
+
oidcCallbackSchema,
|
|
615
|
+
initiateLogin,
|
|
616
|
+
getUser,
|
|
617
|
+
getRequiredUser,
|
|
618
|
+
enterpriseStandard,
|
|
619
|
+
callback
|
|
620
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from './standard-schema';
|
|
2
|
+
/**
|
|
3
|
+
* OIDC Code Flow Callback URL Parameters
|
|
4
|
+
* @see https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
|
|
5
|
+
*/
|
|
6
|
+
export interface OidcCallbackParams {
|
|
7
|
+
/**
|
|
8
|
+
* REQUIRED. The authorization code returned from the authorization server.
|
|
9
|
+
*/
|
|
10
|
+
code: string;
|
|
11
|
+
/**
|
|
12
|
+
* REQUIRED if the "state" parameter was present in the client authorization request.
|
|
13
|
+
* The exact value received from the client.
|
|
14
|
+
*/
|
|
15
|
+
state?: string;
|
|
16
|
+
/**
|
|
17
|
+
* RECOMMENDED. The session state value. Clients should use this to verify the session state.
|
|
18
|
+
*/
|
|
19
|
+
session_state?: string;
|
|
20
|
+
/**
|
|
21
|
+
* OAuth 2.0 error code if the authorization request failed.
|
|
22
|
+
*/
|
|
23
|
+
error?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Human-readable ASCII text providing additional information for the error.
|
|
26
|
+
*/
|
|
27
|
+
error_description?: string;
|
|
28
|
+
/**
|
|
29
|
+
* A URI identifying a human-readable web page with information about the error.
|
|
30
|
+
*/
|
|
31
|
+
error_uri?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The "iss" (issuer) parameter identifies the principal that issued the response.
|
|
34
|
+
* This is typically used in the implicit flow.
|
|
35
|
+
*/
|
|
36
|
+
iss?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Creates a StandardSchemaV1 for validating OIDC callback URL parameters.
|
|
40
|
+
* @param vendor - The name of the vendor creating this schema
|
|
41
|
+
* @returns A StandardSchemaV1 instance for OIDC callback parameters
|
|
42
|
+
*/
|
|
43
|
+
export declare function oidcCallbackSchema(vendor: string): StandardSchemaV1<Record<string, unknown>, OidcCallbackParams>;
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { EnterpriseStandard, EnterpriseUser } from ".";
|
|
2
|
+
export declare function getUser(request: Request, es?: EnterpriseStandard): Promise<EnterpriseUser | undefined>;
|
|
3
|
+
export declare function getRequiredUser(request: Request, es?: EnterpriseStandard): Promise<EnterpriseUser>;
|
|
4
|
+
export declare function initiateLogin(landingPage: string, errorPage?: string, es?: EnterpriseStandard): Promise<Response>;
|
|
5
|
+
export declare function callback(request: Request, es?: EnterpriseStandard): Promise<Response>;
|
package/dist/sso.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { EnterpriseUser } from '.';
|
|
2
|
+
export type SSOConfig = {
|
|
3
|
+
authority: string;
|
|
4
|
+
token_url: string;
|
|
5
|
+
authorization_url: string;
|
|
6
|
+
client_id: string;
|
|
7
|
+
redirect_uri: string;
|
|
8
|
+
response_type: string;
|
|
9
|
+
scope: string;
|
|
10
|
+
post_logout_redirect_uri?: string;
|
|
11
|
+
silent_redirect_uri?: string;
|
|
12
|
+
jwks_uri?: string;
|
|
13
|
+
cookiePrefix?: string;
|
|
14
|
+
cookiePath?: string;
|
|
15
|
+
sameSite?: 'Strict' | 'Lax';
|
|
16
|
+
secure?: boolean;
|
|
17
|
+
};
|
|
18
|
+
type SSOConfigWithDefaults = SSOConfig & {
|
|
19
|
+
secure: boolean;
|
|
20
|
+
sameSite: string;
|
|
21
|
+
cookiePrefix: string;
|
|
22
|
+
cookiePath: string;
|
|
23
|
+
};
|
|
24
|
+
export declare class SSOManager {
|
|
25
|
+
config: SSOConfigWithDefaults;
|
|
26
|
+
constructor(config: SSOConfig);
|
|
27
|
+
getUser(request: Request): Promise<EnterpriseUser | undefined>;
|
|
28
|
+
getRequiredUser(request: Request): Promise<EnterpriseUser>;
|
|
29
|
+
initiateLogin(landingPage: string, errorPage?: string): Promise<Response>;
|
|
30
|
+
callbackHandler(request: Request): Promise<Response>;
|
|
31
|
+
private parseUser;
|
|
32
|
+
private exchangeCodeForToken;
|
|
33
|
+
private refreshToken;
|
|
34
|
+
private fetchJwks;
|
|
35
|
+
private retryWithBackoff;
|
|
36
|
+
private parseJwt;
|
|
37
|
+
private generateRandomString;
|
|
38
|
+
private pkceChallengeFromVerifier;
|
|
39
|
+
private getPublicKey;
|
|
40
|
+
private createJwtCookies;
|
|
41
|
+
private getJwt;
|
|
42
|
+
private createCookie;
|
|
43
|
+
private clearCookie;
|
|
44
|
+
private getCookie;
|
|
45
|
+
}
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** The Standard Schema interface. @see https://standardschema.dev/ */
|
|
2
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
3
|
+
/** The Standard Schema properties. */
|
|
4
|
+
readonly '~standard': StandardSchemaV1.Props<Input, Output>;
|
|
5
|
+
}
|
|
6
|
+
export declare namespace StandardSchemaV1 {
|
|
7
|
+
/** The Standard Schema properties interface. */
|
|
8
|
+
interface Props<Input = unknown, Output = Input> {
|
|
9
|
+
/** The version number of the standard. */
|
|
10
|
+
readonly version: 1;
|
|
11
|
+
/** The vendor name of the schema library. */
|
|
12
|
+
readonly vendor: string;
|
|
13
|
+
/** Validates unknown input values. */
|
|
14
|
+
readonly validate: (value: unknown) => Result<Output> | Promise<Result<Output>>;
|
|
15
|
+
/** Inferred types associated with the schema. */
|
|
16
|
+
readonly types?: Types<Input, Output> | undefined;
|
|
17
|
+
}
|
|
18
|
+
/** The result interface of the validate function. */
|
|
19
|
+
type Result<Output> = SuccessResult<Output> | FailureResult;
|
|
20
|
+
/** The result interface if validation succeeds. */
|
|
21
|
+
interface SuccessResult<Output> {
|
|
22
|
+
/** The typed output value. */
|
|
23
|
+
readonly value: Output;
|
|
24
|
+
/** The non-existent issues. */
|
|
25
|
+
readonly issues?: undefined;
|
|
26
|
+
}
|
|
27
|
+
/** The result interface if validation fails. */
|
|
28
|
+
interface FailureResult {
|
|
29
|
+
/** The issues of failed validation. */
|
|
30
|
+
readonly issues: ReadonlyArray<Issue>;
|
|
31
|
+
}
|
|
32
|
+
/** The issue interface of the failure output. */
|
|
33
|
+
interface Issue {
|
|
34
|
+
/** The error message of the issue. */
|
|
35
|
+
readonly message: string;
|
|
36
|
+
/** The path of the issue, if any. */
|
|
37
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
|
38
|
+
}
|
|
39
|
+
/** The path segment interface of the issue. */
|
|
40
|
+
interface PathSegment {
|
|
41
|
+
/** The key representing a path segment. */
|
|
42
|
+
readonly key: PropertyKey;
|
|
43
|
+
}
|
|
44
|
+
/** The Standard Schema types interface. */
|
|
45
|
+
interface Types<Input = unknown, Output = Input> {
|
|
46
|
+
/** The input type of the schema. */
|
|
47
|
+
readonly input: Input;
|
|
48
|
+
/** The output type of the schema. */
|
|
49
|
+
readonly output: Output;
|
|
50
|
+
}
|
|
51
|
+
/** Infers the input type of a Standard Schema. */
|
|
52
|
+
type InferInput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['input'];
|
|
53
|
+
/** Infers the output type of a Standard Schema. */
|
|
54
|
+
type InferOutput<Schema extends StandardSchemaV1> = NonNullable<Schema['~standard']['types']>['output'];
|
|
55
|
+
}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type EnterpriseStandard } from ".";
|
|
2
|
+
export declare function must<T>(value: T | undefined | null, message?: string): T;
|
|
3
|
+
export declare function setDefaultInstance(es: EnterpriseStandard): void;
|
|
4
|
+
export declare function getDefaultInstance(): EnterpriseStandard | undefined;
|
|
5
|
+
/**
|
|
6
|
+
* If an es is defined, then return it, otherwise return the defaultEnterpriseStandard
|
|
7
|
+
*/
|
|
8
|
+
export declare function getES(es?: EnterpriseStandard): EnterpriseStandard;
|
package/dist/vault.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type Secret<T> = {
|
|
2
|
+
data: T;
|
|
3
|
+
metadata: MetaData;
|
|
4
|
+
};
|
|
5
|
+
type MetaData = {
|
|
6
|
+
created_time: string;
|
|
7
|
+
custom_metadata: any;
|
|
8
|
+
deletion_time: string;
|
|
9
|
+
destroyed: boolean;
|
|
10
|
+
version: number;
|
|
11
|
+
};
|
|
12
|
+
export type Vault = {
|
|
13
|
+
url: string;
|
|
14
|
+
getFullSecret: <T>(path: string) => Promise<Secret<T>>;
|
|
15
|
+
getSecret: <T>(path: string) => Promise<T>;
|
|
16
|
+
};
|
|
17
|
+
export declare function vault(url: string, token: string): Promise<Vault>;
|
|
18
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@enterprisestandard/react",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Enterprise Standard React Components",
|
|
5
|
+
"private": false,
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "bun run build.ts",
|
|
9
|
+
"prepublishOnly": "bun run build"
|
|
10
|
+
},
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"typesVersions": {
|
|
19
|
+
"*": {
|
|
20
|
+
"*": [
|
|
21
|
+
"./dist/*",
|
|
22
|
+
"./dist/types/*"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist/*"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"author": "enterprisestandard",
|
|
36
|
+
"license": "proprietary",
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"minimatch": "^10.0.3"
|
|
42
|
+
}
|
|
43
|
+
}
|