@tula-tech/contract 0.1.1 → 0.3.0
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 +106 -1
- package/dist/custom-claims.d.ts +118 -3
- package/dist/custom-claims.js +21 -2
- package/dist/error-codes.d.ts +204 -0
- package/dist/error-codes.js +1 -1
- package/dist/event-types.d.ts +34 -1
- package/dist/event-types.js +1 -1
- package/dist/headers.d.ts +25 -5
- package/dist/headers.js +7 -3
- package/dist/index.d.ts +3934 -791
- package/dist/index.js +1896 -597
- package/dist/organization-domain-rules.d.ts +132 -0
- package/dist/organization-domain-rules.js +34 -0
- package/dist/organization-permissions.d.ts +185 -0
- package/dist/organization-permissions.js +42 -0
- package/dist/password-rules.d.ts +204 -0
- package/dist/redirect-url.d.ts +3 -3
- package/dist/redirect-url.js +1 -1
- package/dist/shared/{chunk-q309e9bk.js → chunk-4hytq4je.js} +3 -1
- package/dist/shared/{chunk-cce3bhet.js → chunk-5r2ka03q.js} +192 -0
- package/dist/shared/{chunk-7yqzghgm.js → chunk-bsea97tx.js} +66 -0
- package/dist/shared/chunk-cdg41bxh.js +151 -0
- package/dist/shared/{chunk-4rb5jsfz.js → chunk-fkx6fc8e.js} +61 -1
- package/dist/shared/{chunk-pcn6ax54.js → chunk-g0ddvjqg.js} +29 -29
- package/dist/shared/{chunk-r202nxfw.js → chunk-n1jdq72m.js} +18 -3
- package/dist/shared/chunk-yzgakhtb.js +50 -0
- package/dist/theme.d.ts +6 -3
- package/dist/theme.js +1 -1
- package/openapi.json +29170 -10212
- package/package.json +10 -1
package/README.md
CHANGED
|
@@ -35,4 +35,109 @@ const step = FlowStepSchema.parse(await response.json())
|
|
|
35
35
|
|
|
36
36
|
The reference of every export is [docs/reference/contract.md](../../docs/reference/contract.md).
|
|
37
37
|
Under a strict Content-Security-Policy (no `'unsafe-eval'`), see
|
|
38
|
-
[docs/releasing.md](../../docs/releasing.md#tula-techcontract-under-a-strict-content-security-policy).
|
|
38
|
+
[docs/internal/releasing.md](../../docs/internal/releasing.md#tula-techcontract-under-a-strict-content-security-policy).
|
|
39
|
+
|
|
40
|
+
## What it holds
|
|
41
|
+
|
|
42
|
+
| Area | What is in it |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| Flow protocol | `FlowStepSchema` and the schemas of the steps a server answers a sign-up, a sign-in or a reset with (`needs_password`, `needs_first_factor`, `needs_email_verification`, `needs_new_password`, `needs_second_factor`, `needs_factor_enrolment`, `complete`). The API never says which screen to draw; a client reads the step. |
|
|
45
|
+
| Errors | `ERROR_CODES`, `ERROR_DEFINITIONS` and `errorDefinition(code)`: every stable, dot-namespaced code (`password.too_short`) with its status. The envelope an error is answered with is a schema too. |
|
|
46
|
+
| Passwords | `evaluatePassword(policy, password, userInfo)` and the policy presets: the same rules the server applies, so a form can show them before a request is made. |
|
|
47
|
+
| Sessions and tokens | The session profile, the token claims, `environmentIssuer(apiUrl, environmentId)` and `jwksUrl(issuer)`, which tell a verifier where an environment's keys are published. |
|
|
48
|
+
| Settings | The schema of an environment's settings, OAuth providers, hooks, native apps, JWT templates, email and SMS templates and organization roles. `@tula-tech/config` validates a config file with it. |
|
|
49
|
+
| Events | `ACTIVITY_TYPES` and the webhook signature constants (the `webhook-id`, `webhook-timestamp` and `webhook-signature` headers, `WEBHOOK_TOLERANCE_SECONDS`), for a receiver that checks a delivery. |
|
|
50
|
+
| Headers | `PUBLISHABLE_KEY_HEADER` (`x-tula-publishable-key`), `CLIENT_HEADER` (`x-tula-client`) and the attempt header a flow call carries. |
|
|
51
|
+
| Redirect URLs | `isRedirectUrl`, `redirectUrlKind` and the rules for a custom-scheme redirect, shared by the server and the SDKs. |
|
|
52
|
+
| Theme | The default theme tokens the React SDK's stylesheet and the native SDKs are generated from (`@tula-tech/contract/theme`). |
|
|
53
|
+
|
|
54
|
+
## Examples
|
|
55
|
+
|
|
56
|
+
Check a password against a policy before a form is submitted:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { PASSWORD_POLICY_PRESETS } from '@tula-tech/contract'
|
|
60
|
+
import { evaluatePassword } from '@tula-tech/contract/password-rules'
|
|
61
|
+
|
|
62
|
+
const { ok, checks } = evaluatePassword(PASSWORD_POLICY_PRESETS.recommended, 'correct horse')
|
|
63
|
+
if (!ok) {
|
|
64
|
+
for (const check of checks.filter((c) => !c.passed)) {
|
|
65
|
+
console.log(check.rule)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Find where an environment publishes its signing keys, to verify an access token offline:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { environmentIssuer, jwksUrl } from '@tula-tech/contract/issuer'
|
|
74
|
+
|
|
75
|
+
const issuer = environmentIssuer('https://auth.example.com', environmentId)
|
|
76
|
+
const keys = jwksUrl(issuer) // <issuer>/.well-known/jwks.json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Look up what an error code means without importing Zod:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { ERROR_CODES, errorDefinition } from '@tula-tech/contract/error-codes'
|
|
83
|
+
|
|
84
|
+
if ((ERROR_CODES as readonly string[]).includes(body.code)) {
|
|
85
|
+
const { status } = errorDefinition(body.code)
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Using the OpenAPI document
|
|
90
|
+
|
|
91
|
+
`@tula-tech/contract/openapi.json` is the document the API publishes at `/v1/openapi.json`,
|
|
92
|
+
committed with every release. It is what `@tula-tech/core` and `@tula-tech/admin` generate
|
|
93
|
+
their types from, so a generator of your own (another language, a test double) can start from
|
|
94
|
+
the same file:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import openapi from '@tula-tech/contract/openapi.json' with { type: 'json' }
|
|
98
|
+
|
|
99
|
+
console.log(Object.keys(openapi.paths).length)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Compatibility
|
|
103
|
+
|
|
104
|
+
- **ESM only**, with type declarations. The package has no side effects, so a bundler can drop
|
|
105
|
+
what you do not import.
|
|
106
|
+
- It runs in browsers, Node, Bun and edge runtimes: it uses no Node-only API.
|
|
107
|
+
- The root entry point depends on **Zod 4**. The subpath entry points listed above import no
|
|
108
|
+
Zod, so a browser bundle that needs only a constant or a pure function does not carry it.
|
|
109
|
+
- Zod's JIT code generation uses `new Function`. Where a strict Content-Security-Policy
|
|
110
|
+
forbids `'unsafe-eval'`, follow
|
|
111
|
+
[the note in docs/internal/releasing.md](../../docs/internal/releasing.md#tula-techcontract-under-a-strict-content-security-policy).
|
|
112
|
+
|
|
113
|
+
## Stability and versions
|
|
114
|
+
|
|
115
|
+
- Every `@tula-tech/*` package of one release shares a version. Install this package at the
|
|
116
|
+
same version as the SDK that uses it (`@tula-tech/core`, `@tula-tech/react`,
|
|
117
|
+
`@tula-tech/nextjs`), or let that SDK bring it.
|
|
118
|
+
- A change to a route, a response or an error code is a change to this package, and the
|
|
119
|
+
OpenAPI document in it is regenerated with the API. While the version is `0.x`, read the
|
|
120
|
+
[changelog](https://github.com/Tula-Solutions/tula-auth-and-user-management/releases)
|
|
121
|
+
before you upgrade.
|
|
122
|
+
- Match on an error's `code`, not on its `detail` sentence, which is for people.
|
|
123
|
+
|
|
124
|
+
## Security notes
|
|
125
|
+
|
|
126
|
+
- The schemas describe what a server and a client may send; they do not authenticate anything.
|
|
127
|
+
Validating a payload with them is not a substitute for verifying a token or a webhook
|
|
128
|
+
signature.
|
|
129
|
+
- Nothing in this package holds a secret. Secret keys (`tula_sk_…`) belong to the server
|
|
130
|
+
side only and are the concern of `@tula-tech/admin`.
|
|
131
|
+
|
|
132
|
+
## Related packages
|
|
133
|
+
|
|
134
|
+
| Package | What |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| [`@tula-tech/core`](../core/README.md) | The headless client: flows, the session, single-flight refresh. |
|
|
137
|
+
| [`@tula-tech/react`](../react/README.md) | Provider, hooks and prebuilt components. |
|
|
138
|
+
| [`@tula-tech/nextjs`](../nextjs/README.md) | The Next.js App Router SDK. |
|
|
139
|
+
| [`@tula-tech/admin`](../admin/README.md) | The typed client for `/v1/admin/*`. Server-side only. |
|
|
140
|
+
| [`@tula-tech/config`](../config/README.md) | `defineConfig()` for `tula.config.ts`. |
|
|
141
|
+
|
|
142
|
+
The flow protocol is explained in [ADR 0019](../../docs/adr/0019-flow-engine-v2.md); the decisions
|
|
143
|
+
behind the rest are indexed in the [ADR README](../../docs/adr/README.md). Licensed under Apache-2.0.
|
package/dist/custom-claims.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The one top-level claim every custom claim is nested under
|
|
2
|
+
* The one top-level claim every custom claim is nested under.
|
|
3
3
|
*
|
|
4
4
|
* An operator's claims never sit beside Tula's own, so a claim Tula adds later cannot collide
|
|
5
5
|
* with a customer's, and a template cannot set a claim Tula's servers or SDKs act on. `ext`
|
|
@@ -12,6 +12,17 @@
|
|
|
12
12
|
* ```
|
|
13
13
|
*/
|
|
14
14
|
declare const CUSTOM_CLAIMS_CLAIM = "ext";
|
|
15
|
+
/** The access token's claim naming the active organization. */
|
|
16
|
+
declare const ORGANIZATION_ID_CLAIM = "org_id";
|
|
17
|
+
/** The access token's claim naming the SSO connection a session was signed in through. */
|
|
18
|
+
declare const SSO_CONNECTION_CLAIM = "sso_conn";
|
|
19
|
+
/** The access token's claim holding the key of the member's role in the active organization. */
|
|
20
|
+
declare const ORGANIZATION_ROLE_CLAIM = "org_role";
|
|
21
|
+
/**
|
|
22
|
+
* The access token's claim holding the permission keys of that role. A list of strings,
|
|
23
|
+
* present only beside the other two.
|
|
24
|
+
*/
|
|
25
|
+
declare const ORGANIZATION_PERMISSIONS_CLAIM = "org_perms";
|
|
15
26
|
/**
|
|
16
27
|
* Claim names a custom claim's key may never be: every claim Tula's tokens carry, the
|
|
17
28
|
* registered claims a verifier acts on, `cnf` (reserved for device binding) and the namespace
|
|
@@ -25,7 +36,7 @@ declare const CUSTOM_CLAIMS_CLAIM = "ext";
|
|
|
25
36
|
* RESERVED_CLAIM_NAMES.includes('sub') // true
|
|
26
37
|
* ```
|
|
27
38
|
*/
|
|
28
|
-
declare const RESERVED_CLAIM_NAMES: readonly ["iss", "sub", "aud", "exp", "nbf", "iat", "jti", "sid", "pid", "eid", "v", "auth_time", "amr", "sp", "cnf", "ext"];
|
|
39
|
+
declare const RESERVED_CLAIM_NAMES: readonly ["iss", "sub", "aud", "exp", "nbf", "iat", "jti", "sid", "pid", "eid", "v", "auth_time", "amr", "sp", "cnf", "org_id", "org_role", "org_perms", "sso_conn", "ext"];
|
|
29
40
|
/**
|
|
30
41
|
* The most bytes the namespace claim's value may take as JSON (UTF-8), whatever puts claims
|
|
31
42
|
* in it.
|
|
@@ -36,6 +47,110 @@ declare const RESERVED_CLAIM_NAMES: readonly ["iss", "sub", "aud", "exp", "nbf",
|
|
|
36
47
|
* room for a long issuer URL.
|
|
37
48
|
*/
|
|
38
49
|
declare const MAX_CUSTOM_CLAIMS_BYTES = 1024;
|
|
50
|
+
/**
|
|
51
|
+
* The most bytes the three organization claims may take together, as JSON (UTF-8): their own
|
|
52
|
+
* cap, beside {@link MAX_CUSTOM_CLAIMS_BYTES} and never taken from it.
|
|
53
|
+
*
|
|
54
|
+
* It is the true upper bound of what the grammars allow: an organization id (a UUID, 36
|
|
55
|
+
* characters), a role key of 32 characters and 32 permissions of the longest key
|
|
56
|
+
* (`org:sys:` + 32 + `:` + 32 = 73 characters) take 2,541 bytes; the cap leaves a little
|
|
57
|
+
* room. A role is bounded by the contract and the database, so a token never reaches it
|
|
58
|
+
* unless a row was written by hand: the server then issues the token **without** the
|
|
59
|
+
* organization claims (no claim is "no permission"), never with a cut list.
|
|
60
|
+
*/
|
|
61
|
+
declare const MAX_ORGANIZATION_CLAIMS_BYTES = 2560;
|
|
62
|
+
/** Most permissions an organization role (and so the `org_perms` claim) holds. */
|
|
63
|
+
declare const MAX_ORGANIZATION_PERMISSIONS_CLAIM = 32;
|
|
64
|
+
/** The organization claims of a verified session: who it acts as, in which organization. */
|
|
65
|
+
interface OrganizationClaims {
|
|
66
|
+
/** The active organization's id. */
|
|
67
|
+
readonly orgId: string;
|
|
68
|
+
/** The key of the member's role in it. */
|
|
69
|
+
readonly orgRole: string;
|
|
70
|
+
/** The permission keys of that role. */
|
|
71
|
+
readonly orgPermissions: readonly string[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Whether permission keys and an organization id and role can ride in a token: the UTF-8 length
|
|
75
|
+
* of the JSON of the three claims, which {@link MAX_ORGANIZATION_CLAIMS_BYTES} caps.
|
|
76
|
+
*
|
|
77
|
+
* @param claims - The three claims.
|
|
78
|
+
* @returns The byte count.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* organizationClaimsBytes({ orgId: 'o1', orgRole: 'member', orgPermissions: [] }) // 52
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
declare function organizationClaimsBytes(claims: OrganizationClaims): number;
|
|
86
|
+
/**
|
|
87
|
+
* Read the organization claims out of a session's claims, for an SDK or an application.
|
|
88
|
+
*
|
|
89
|
+
* Call it only with claims that were **verified**: this checks a shape, not where the claims
|
|
90
|
+
* came from. The three are returned together or not at all. A token that carries one without
|
|
91
|
+
* the others, a value of another type, an id or a role key outside their grammar, more than
|
|
92
|
+
* {@link MAX_ORGANIZATION_PERMISSIONS_CLAIM} permissions, a permission outside the permission
|
|
93
|
+
* grammar (`PERMISSION_KEY_PATTERN`) or claims over {@link MAX_ORGANIZATION_CLAIMS_BYTES} has
|
|
94
|
+
* **no** active organization, whole: never repaired and never partly believed. Treat `null`
|
|
95
|
+
* as "no organization, no permission".
|
|
96
|
+
*
|
|
97
|
+
* @param claims - A session's verified claims.
|
|
98
|
+
* @returns A frozen copy, or `null` when no organization is active (or the claims are not
|
|
99
|
+
* what a Tula server issues).
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* readOrganizationClaims({ org_id: 'o1', org_role: 'admin', org_perms: ['org:sys:memberships:read']
|
|
104
|
+
* })
|
|
105
|
+
* // { orgId: 'o1', orgRole: 'admin', orgPermissions: ['org:sys:memberships:read'] }
|
|
106
|
+
* readOrganizationClaims({ org_id: 'o1' }) // null
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
declare function readOrganizationClaims(claims: unknown): OrganizationClaims | null;
|
|
110
|
+
/**
|
|
111
|
+
* What {@link hasOrganizationAccess} asks: one permission key, or one role key. Exactly one
|
|
112
|
+
* of the two.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* const byPermission: OrganizationCheck = { permission: 'org:billing:read' }
|
|
117
|
+
* const byRole: OrganizationCheck = { role: 'admin' }
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
type OrganizationCheck = {
|
|
121
|
+
readonly permission: string;
|
|
122
|
+
readonly role?: never;
|
|
123
|
+
} | {
|
|
124
|
+
readonly role: string;
|
|
125
|
+
readonly permission?: never;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Whether the active organization grants a permission or a role: the one rule behind
|
|
129
|
+
* `has()` in `@tula-tech/nextjs` and `@tula-tech/react`.
|
|
130
|
+
*
|
|
131
|
+
* It is exact. A permission is held when the claims list that very key; there is no wildcard
|
|
132
|
+
* (`org:billing:*` matches nothing) and no implication between keys (`manage` does not
|
|
133
|
+
* include `read`). A role is held when it is the member's role key. It is **false** with no
|
|
134
|
+
* active organization, for a check that names neither or both, and for anything that is not a
|
|
135
|
+
* non-empty string: a check that cannot be understood never grants.
|
|
136
|
+
*
|
|
137
|
+
* This reads claims, so it is as old as the access token (the server decides afresh at every
|
|
138
|
+
* refresh, within the token's life). Use it to decide what to show and what to let through
|
|
139
|
+
* quickly; a request that changes data is authorized by the server that changes it.
|
|
140
|
+
*
|
|
141
|
+
* @param claims - What {@link readOrganizationClaims} returned, or `null`.
|
|
142
|
+
* @param check - The permission or the role asked for.
|
|
143
|
+
* @returns Whether the organization grants it.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* const claims = readOrganizationClaims(payload)
|
|
148
|
+
* hasOrganizationAccess(claims, { permission: 'org:billing:read' })
|
|
149
|
+
* hasOrganizationAccess(claims, { role: 'admin' })
|
|
150
|
+
* hasOrganizationAccess(null, { role: 'admin' }) // false: no organization is active
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
declare function hasOrganizationAccess(claims: OrganizationClaims | null | undefined, check: OrganizationCheck): boolean;
|
|
39
154
|
/** Most JWT templates an environment may define. */
|
|
40
155
|
declare const MAX_JWT_TEMPLATES = 10;
|
|
41
156
|
/** Most claims one JWT template may define. */
|
|
@@ -158,4 +273,4 @@ declare function checkCustomClaims(value: unknown): CustomClaimsCheck;
|
|
|
158
273
|
* ```
|
|
159
274
|
*/
|
|
160
275
|
declare function readCustomClaims(claims: unknown): CustomClaims | null;
|
|
161
|
-
export { CUSTOM_CLAIMS_CLAIM, CustomClaimValue, CustomClaims, CustomClaimsCheck, MAX_CUSTOM_CLAIMS_BYTES, MAX_CUSTOM_CLAIM_CONSTANT_LENGTH, MAX_CUSTOM_CLAIM_KEY_LENGTH, MAX_JWT_TEMPLATES, MAX_JWT_TEMPLATE_CLAIMS, RESERVED_CLAIM_NAMES, checkCustomClaims, customClaimsBytes, isCustomClaimKey, isCustomClaimValue, readCustomClaims };
|
|
276
|
+
export { CUSTOM_CLAIMS_CLAIM, CustomClaimValue, CustomClaims, CustomClaimsCheck, MAX_CUSTOM_CLAIMS_BYTES, MAX_CUSTOM_CLAIM_CONSTANT_LENGTH, MAX_CUSTOM_CLAIM_KEY_LENGTH, MAX_JWT_TEMPLATES, MAX_JWT_TEMPLATE_CLAIMS, MAX_ORGANIZATION_CLAIMS_BYTES, MAX_ORGANIZATION_PERMISSIONS_CLAIM, ORGANIZATION_ID_CLAIM, ORGANIZATION_PERMISSIONS_CLAIM, ORGANIZATION_ROLE_CLAIM, OrganizationCheck, OrganizationClaims, RESERVED_CLAIM_NAMES, SSO_CONNECTION_CLAIM, checkCustomClaims, customClaimsBytes, hasOrganizationAccess, isCustomClaimKey, isCustomClaimValue, organizationClaimsBytes, readCustomClaims, readOrganizationClaims };
|
package/dist/custom-claims.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CUSTOM_CLAIMS_CLAIM2,
|
|
3
|
+
ORGANIZATION_ID_CLAIM2,
|
|
4
|
+
SSO_CONNECTION_CLAIM2,
|
|
5
|
+
ORGANIZATION_ROLE_CLAIM2,
|
|
6
|
+
ORGANIZATION_PERMISSIONS_CLAIM2,
|
|
3
7
|
RESERVED_CLAIM_NAMES2,
|
|
4
8
|
MAX_CUSTOM_CLAIMS_BYTES2,
|
|
9
|
+
MAX_ORGANIZATION_CLAIMS_BYTES2,
|
|
10
|
+
MAX_ORGANIZATION_PERMISSIONS_CLAIM2,
|
|
11
|
+
organizationClaimsBytes2,
|
|
12
|
+
readOrganizationClaims2,
|
|
13
|
+
hasOrganizationAccess2,
|
|
5
14
|
MAX_JWT_TEMPLATES2,
|
|
6
15
|
MAX_JWT_TEMPLATE_CLAIMS2,
|
|
7
16
|
MAX_CUSTOM_CLAIM_KEY_LENGTH2,
|
|
@@ -11,7 +20,8 @@ import {
|
|
|
11
20
|
customClaimsBytes2,
|
|
12
21
|
checkCustomClaims2,
|
|
13
22
|
readCustomClaims2
|
|
14
|
-
} from "./shared/chunk-
|
|
23
|
+
} from "./shared/chunk-fkx6fc8e.js";
|
|
24
|
+
import"./shared/chunk-yzgakhtb.js";
|
|
15
25
|
export {
|
|
16
26
|
CUSTOM_CLAIMS_CLAIM2 as CUSTOM_CLAIMS_CLAIM,
|
|
17
27
|
MAX_CUSTOM_CLAIMS_BYTES2 as MAX_CUSTOM_CLAIMS_BYTES,
|
|
@@ -19,10 +29,19 @@ export {
|
|
|
19
29
|
MAX_CUSTOM_CLAIM_KEY_LENGTH2 as MAX_CUSTOM_CLAIM_KEY_LENGTH,
|
|
20
30
|
MAX_JWT_TEMPLATES2 as MAX_JWT_TEMPLATES,
|
|
21
31
|
MAX_JWT_TEMPLATE_CLAIMS2 as MAX_JWT_TEMPLATE_CLAIMS,
|
|
32
|
+
MAX_ORGANIZATION_CLAIMS_BYTES2 as MAX_ORGANIZATION_CLAIMS_BYTES,
|
|
33
|
+
MAX_ORGANIZATION_PERMISSIONS_CLAIM2 as MAX_ORGANIZATION_PERMISSIONS_CLAIM,
|
|
34
|
+
ORGANIZATION_ID_CLAIM2 as ORGANIZATION_ID_CLAIM,
|
|
35
|
+
ORGANIZATION_PERMISSIONS_CLAIM2 as ORGANIZATION_PERMISSIONS_CLAIM,
|
|
36
|
+
ORGANIZATION_ROLE_CLAIM2 as ORGANIZATION_ROLE_CLAIM,
|
|
22
37
|
RESERVED_CLAIM_NAMES2 as RESERVED_CLAIM_NAMES,
|
|
38
|
+
SSO_CONNECTION_CLAIM2 as SSO_CONNECTION_CLAIM,
|
|
23
39
|
checkCustomClaims2 as checkCustomClaims,
|
|
24
40
|
customClaimsBytes2 as customClaimsBytes,
|
|
41
|
+
hasOrganizationAccess2 as hasOrganizationAccess,
|
|
25
42
|
isCustomClaimKey2 as isCustomClaimKey,
|
|
26
43
|
isCustomClaimValue2 as isCustomClaimValue,
|
|
27
|
-
|
|
44
|
+
organizationClaimsBytes2 as organizationClaimsBytes,
|
|
45
|
+
readCustomClaims2 as readCustomClaims,
|
|
46
|
+
readOrganizationClaims2 as readOrganizationClaims
|
|
28
47
|
};
|
package/dist/error-codes.d.ts
CHANGED
|
@@ -122,6 +122,22 @@ declare const ERROR_DEFINITIONS: {
|
|
|
122
122
|
readonly status: 409;
|
|
123
123
|
readonly message: "This account has no password yet. Use \"forgot password\" to set one.";
|
|
124
124
|
};
|
|
125
|
+
readonly "import.unsupported_algorithm": {
|
|
126
|
+
readonly status: 422;
|
|
127
|
+
readonly message: "That password hash algorithm is not supported.";
|
|
128
|
+
};
|
|
129
|
+
readonly "import.hash_malformed": {
|
|
130
|
+
readonly status: 422;
|
|
131
|
+
readonly message: "That password hash is malformed.";
|
|
132
|
+
};
|
|
133
|
+
readonly "import.hash_parameters_too_weak": {
|
|
134
|
+
readonly status: 422;
|
|
135
|
+
readonly message: "That password hash is too weak to import.";
|
|
136
|
+
};
|
|
137
|
+
readonly "import.hash_parameters_too_costly": {
|
|
138
|
+
readonly status: 422;
|
|
139
|
+
readonly message: "That password hash is too costly to verify.";
|
|
140
|
+
};
|
|
125
141
|
readonly "verification.invalid_code": {
|
|
126
142
|
readonly status: 422;
|
|
127
143
|
readonly message: "That code is incorrect.";
|
|
@@ -314,6 +330,194 @@ declare const ERROR_DEFINITIONS: {
|
|
|
314
330
|
readonly status: 400;
|
|
315
331
|
readonly message: "Signing in here needs a device key.";
|
|
316
332
|
};
|
|
333
|
+
readonly "user.last_organization_manager": {
|
|
334
|
+
readonly status: 409;
|
|
335
|
+
readonly message: "This user is the only manager of an organization. Add another manager or delete the organization first.";
|
|
336
|
+
};
|
|
337
|
+
readonly "user.managed": {
|
|
338
|
+
readonly status: 403;
|
|
339
|
+
readonly message: "This account is managed by your organization. Ask your administrator to change it.";
|
|
340
|
+
};
|
|
341
|
+
readonly "organization.disabled": {
|
|
342
|
+
readonly status: 403;
|
|
343
|
+
readonly message: "Organizations are not enabled for this environment.";
|
|
344
|
+
};
|
|
345
|
+
readonly "organization.slug_taken": {
|
|
346
|
+
readonly status: 409;
|
|
347
|
+
readonly message: "An organization with that slug already exists.";
|
|
348
|
+
};
|
|
349
|
+
readonly "organization.member_exists": {
|
|
350
|
+
readonly status: 409;
|
|
351
|
+
readonly message: "That user is already a member of this organization.";
|
|
352
|
+
};
|
|
353
|
+
readonly "organization.last_manager": {
|
|
354
|
+
readonly status: 409;
|
|
355
|
+
readonly message: "An organization must keep at least one member who can manage its members.";
|
|
356
|
+
};
|
|
357
|
+
readonly "organization.role_unknown": {
|
|
358
|
+
readonly status: 422;
|
|
359
|
+
readonly message: "That role does not exist in this environment.";
|
|
360
|
+
};
|
|
361
|
+
readonly "organization.not_a_member": {
|
|
362
|
+
readonly status: 403;
|
|
363
|
+
readonly message: "You are not a member of that organization.";
|
|
364
|
+
};
|
|
365
|
+
readonly "organization.creation_disabled": {
|
|
366
|
+
readonly status: 403;
|
|
367
|
+
readonly message: "Creating an organization is not allowed in this environment.";
|
|
368
|
+
};
|
|
369
|
+
readonly "organization.limit_reached": {
|
|
370
|
+
readonly status: 403;
|
|
371
|
+
readonly message: "You have reached the number of organizations you can create.";
|
|
372
|
+
};
|
|
373
|
+
readonly "organization.personal_account_disabled": {
|
|
374
|
+
readonly status: 409;
|
|
375
|
+
readonly message: "An organization must be active in this environment.";
|
|
376
|
+
};
|
|
377
|
+
readonly "organization.role_exists": {
|
|
378
|
+
readonly status: 409;
|
|
379
|
+
readonly message: "A role with that key already exists in this environment.";
|
|
380
|
+
};
|
|
381
|
+
readonly "organization.role_in_use": {
|
|
382
|
+
readonly status: 409;
|
|
383
|
+
readonly message: "Members still hold this role. Give them another role first.";
|
|
384
|
+
};
|
|
385
|
+
readonly "organization.role_built_in": {
|
|
386
|
+
readonly status: 409;
|
|
387
|
+
readonly message: "A built-in role cannot be changed or deleted.";
|
|
388
|
+
};
|
|
389
|
+
readonly "organization.role_limit": {
|
|
390
|
+
readonly status: 409;
|
|
391
|
+
readonly message: "This environment has the most custom roles it can have.";
|
|
392
|
+
};
|
|
393
|
+
readonly "organization.permission_required": {
|
|
394
|
+
readonly status: 403;
|
|
395
|
+
readonly message: "Your role does not allow that.";
|
|
396
|
+
};
|
|
397
|
+
readonly "organization.role_not_assignable": {
|
|
398
|
+
readonly status: 403;
|
|
399
|
+
readonly message: "You can only invite to a role no stronger than yours.";
|
|
400
|
+
};
|
|
401
|
+
readonly "organization.invitation_exists": {
|
|
402
|
+
readonly status: 409;
|
|
403
|
+
readonly message: "That address already has a pending invitation.";
|
|
404
|
+
};
|
|
405
|
+
readonly "organization.invitation_not_found": {
|
|
406
|
+
readonly status: 404;
|
|
407
|
+
readonly message: "This invitation does not exist.";
|
|
408
|
+
};
|
|
409
|
+
readonly "organization.invitation_expired": {
|
|
410
|
+
readonly status: 410;
|
|
411
|
+
readonly message: "This invitation has expired.";
|
|
412
|
+
};
|
|
413
|
+
readonly "organization.invitation_revoked": {
|
|
414
|
+
readonly status: 410;
|
|
415
|
+
readonly message: "This invitation was withdrawn.";
|
|
416
|
+
};
|
|
417
|
+
readonly "organization.invitation_used": {
|
|
418
|
+
readonly status: 410;
|
|
419
|
+
readonly message: "This invitation was already used.";
|
|
420
|
+
};
|
|
421
|
+
readonly "organization.invitation_wrong_account": {
|
|
422
|
+
readonly status: 403;
|
|
423
|
+
readonly message: "This invitation was sent to another email address.";
|
|
424
|
+
};
|
|
425
|
+
readonly "organization.invitation_email_unverified": {
|
|
426
|
+
readonly status: 403;
|
|
427
|
+
readonly message: "Verify your email address to accept this invitation.";
|
|
428
|
+
};
|
|
429
|
+
readonly "organization.invitation_role_gone": {
|
|
430
|
+
readonly status: 409;
|
|
431
|
+
readonly message: "The role this invitation offers no longer exists.";
|
|
432
|
+
};
|
|
433
|
+
readonly "organization.domain_public": {
|
|
434
|
+
readonly status: 422;
|
|
435
|
+
readonly message: "A public email domain cannot be claimed by an organization.";
|
|
436
|
+
};
|
|
437
|
+
readonly "organization.domain_taken": {
|
|
438
|
+
readonly status: 409;
|
|
439
|
+
readonly message: "That domain is verified for an organization already.";
|
|
440
|
+
};
|
|
441
|
+
readonly "organization.domain_address_mismatch": {
|
|
442
|
+
readonly status: 422;
|
|
443
|
+
readonly message: "That address is not on the domain.";
|
|
444
|
+
};
|
|
445
|
+
readonly "organization.domain_limit": {
|
|
446
|
+
readonly status: 409;
|
|
447
|
+
readonly message: "This organization has the most domains it can have.";
|
|
448
|
+
};
|
|
449
|
+
readonly "organization.suggestion_unavailable": {
|
|
450
|
+
readonly status: 403;
|
|
451
|
+
readonly message: "You cannot join that organization.";
|
|
452
|
+
};
|
|
453
|
+
readonly "sso.disabled": {
|
|
454
|
+
readonly status: 403;
|
|
455
|
+
readonly message: "Single sign-on is not enabled for this environment.";
|
|
456
|
+
};
|
|
457
|
+
readonly "sso.connection_not_found": {
|
|
458
|
+
readonly status: 404;
|
|
459
|
+
readonly message: "No such single sign-on connection.";
|
|
460
|
+
};
|
|
461
|
+
readonly "sso.name_taken": {
|
|
462
|
+
readonly status: 409;
|
|
463
|
+
readonly message: "The organization already has a connection with that name.";
|
|
464
|
+
};
|
|
465
|
+
readonly "sso.domain_taken": {
|
|
466
|
+
readonly status: 409;
|
|
467
|
+
readonly message: "Another active connection already serves one of those domains.";
|
|
468
|
+
};
|
|
469
|
+
readonly "sso.domain_not_verified": {
|
|
470
|
+
readonly status: 422;
|
|
471
|
+
readonly message: "Every domain must be one of the organization's verified domains.";
|
|
472
|
+
};
|
|
473
|
+
readonly "sso.issuer_not_allowed": {
|
|
474
|
+
readonly status: 422;
|
|
475
|
+
readonly message: "The issuer is not an address the server may call.";
|
|
476
|
+
};
|
|
477
|
+
readonly "sso.saml_invalid": {
|
|
478
|
+
readonly status: 422;
|
|
479
|
+
readonly message: "The SAML settings could not be read or are incomplete.";
|
|
480
|
+
};
|
|
481
|
+
readonly "sso.connection_in_use": {
|
|
482
|
+
readonly status: 409;
|
|
483
|
+
readonly message: "The connection is active; disable it before deleting it.";
|
|
484
|
+
};
|
|
485
|
+
readonly "sso.invalid_transition": {
|
|
486
|
+
readonly status: 409;
|
|
487
|
+
readonly message: "A connection cannot move to that status from its current one.";
|
|
488
|
+
};
|
|
489
|
+
readonly "sso.url_not_https": {
|
|
490
|
+
readonly status: 422;
|
|
491
|
+
readonly message: "The identity provider’s sign-in address must start with https.";
|
|
492
|
+
};
|
|
493
|
+
readonly "sso.test_required": {
|
|
494
|
+
readonly status: 409;
|
|
495
|
+
readonly message: "Run a passing test sign-in of the connection’s current settings before activating it.";
|
|
496
|
+
};
|
|
497
|
+
readonly "sso.test_not_found": {
|
|
498
|
+
readonly status: 404;
|
|
499
|
+
readonly message: "No such test sign-in.";
|
|
500
|
+
};
|
|
501
|
+
readonly "sso.role_not_allowed": {
|
|
502
|
+
readonly status: 403;
|
|
503
|
+
readonly message: "A setup link cannot map a group to a role that holds system permissions.";
|
|
504
|
+
};
|
|
505
|
+
readonly "sso.unavailable": {
|
|
506
|
+
readonly status: 400;
|
|
507
|
+
readonly message: "Single sign-on is not available for that address.";
|
|
508
|
+
};
|
|
509
|
+
readonly "sso.provider_error": {
|
|
510
|
+
readonly status: 401;
|
|
511
|
+
readonly message: "The identity provider did not sign you in.";
|
|
512
|
+
};
|
|
513
|
+
readonly "sso.user_not_provisioned": {
|
|
514
|
+
readonly status: 403;
|
|
515
|
+
readonly message: "Your account has not been set up for this organization. Ask its administrator.";
|
|
516
|
+
};
|
|
517
|
+
readonly "sso.email_domain_mismatch": {
|
|
518
|
+
readonly status: 403;
|
|
519
|
+
readonly message: "Your email address is not on this connection's domains.";
|
|
520
|
+
};
|
|
317
521
|
readonly rate_limited: {
|
|
318
522
|
readonly status: 429;
|
|
319
523
|
readonly message: "Too many requests. Try again shortly.";
|
package/dist/error-codes.js
CHANGED
package/dist/event-types.d.ts
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* }
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
declare const ACTIVITY_TYPES: readonly ["user.created", "user.email_verified", "user.banned", "user.unbanned", "user.deleted", "user.password_changed", "user.mfa_enabled", "user.mfa_disabled", "user.backup_codes_regenerated", "user.backup_code_used", "user.identity_linked", "user.identity_unlinked", "user.email_added", "user.passkey_added", "user.passkey_renamed", "user.passkey_removed", "user.passkey_counter_regressed", "user.phone_number_added", "user.phone_number_removed", "user.sms_factor_enabled", "user.sms_factor_removed", "session.created", "session.revoked", "session.reuse_detected", "session.stepped_up", "session.refresh_proof_refused", "api_key.created", "api_key.revoked", "signing_key.rotated", "environment.settings_updated", "oauth_provider.updated", "oauth_provider.deleted", "webhook_endpoint.created", "webhook_endpoint.updated", "webhook_endpoint.deleted", "webhook_endpoint.disabled", "webhook_endpoint.secret_rotated", "webhook_endpoint.previous_secret_revoked", "hook.created", "hook.updated", "hook.deleted", "native_app.created", "native_app.updated", "native_app.deleted", "proposal.created", "proposal.approved", "proposal.executed", "proposal.failed", "sign_in_test.started", "sign_in_test.finished"];
|
|
22
|
+
declare const ACTIVITY_TYPES: readonly ["user.created", "user.imported", "user.email_verified", "user.banned", "user.unbanned", "user.deleted", "user.password_changed", "user.mfa_enabled", "user.mfa_disabled", "user.backup_codes_regenerated", "user.backup_code_used", "user.identity_linked", "user.identity_unlinked", "user.sso_linked", "user.email_added", "user.passkey_added", "user.passkey_renamed", "user.passkey_removed", "user.passkey_counter_regressed", "user.phone_number_added", "user.phone_number_removed", "user.sms_factor_enabled", "user.sms_factor_removed", "session.created", "session.revoked", "session.reuse_detected", "session.stepped_up", "session.refresh_proof_refused", "api_key.created", "api_key.revoked", "signing_key.rotated", "environment.settings_updated", "oauth_provider.updated", "oauth_provider.deleted", "webhook_endpoint.created", "webhook_endpoint.updated", "webhook_endpoint.deleted", "webhook_endpoint.disabled", "webhook_endpoint.secret_rotated", "webhook_endpoint.previous_secret_revoked", "hook.created", "hook.updated", "hook.deleted", "native_app.created", "native_app.updated", "native_app.deleted", "sso_connection.created", "sso_connection.updated", "sso_connection.deleted", "scim_token.created", "scim_token.revoked", "setup_link.created", "setup_link.revoked", "user.scim_provisioned", "user.scim_updated", "scim_group.created", "scim_group.updated", "scim_group.deleted", "organization.created", "organization.updated", "organization.deleted", "organization_membership.added", "organization_membership.role_changed", "organization_membership.removed", "organization_role.created", "organization_role.updated", "organization_role.deleted", "organization_domain.claimed", "organization_domain.verified", "organization_domain.updated", "organization_domain.removed", "organization_invitation.created", "organization_invitation.accepted", "organization_invitation.declined", "organization_invitation.revoked", "proposal.created", "proposal.approved", "proposal.rejected", "proposal.executed", "proposal.failed", "audit.exported", "sign_in_test.started", "sign_in_test.finished"];
|
|
23
23
|
/** A recorded action type: one of {@link ACTIVITY_TYPES}. */
|
|
24
24
|
type ActivityType = (typeof ACTIVITY_TYPES)[number];
|
|
25
25
|
/**
|
|
@@ -52,6 +52,7 @@ declare const EVENT_SCHEMA_VERSION = 1;
|
|
|
52
52
|
*/
|
|
53
53
|
declare const EVENT_TARGET_TYPES: {
|
|
54
54
|
readonly "user.created": "user";
|
|
55
|
+
readonly "user.imported": "user";
|
|
55
56
|
readonly "user.email_verified": "user";
|
|
56
57
|
readonly "user.banned": "user";
|
|
57
58
|
readonly "user.unbanned": "user";
|
|
@@ -64,6 +65,7 @@ declare const EVENT_TARGET_TYPES: {
|
|
|
64
65
|
readonly "user.identity_linked": "user";
|
|
65
66
|
readonly "user.email_added": "user";
|
|
66
67
|
readonly "user.identity_unlinked": "user";
|
|
68
|
+
readonly "user.sso_linked": "user";
|
|
67
69
|
readonly "user.passkey_added": "user";
|
|
68
70
|
readonly "user.passkey_renamed": "user";
|
|
69
71
|
readonly "user.passkey_removed": "user";
|
|
@@ -95,10 +97,41 @@ declare const EVENT_TARGET_TYPES: {
|
|
|
95
97
|
readonly "native_app.created": "native_app";
|
|
96
98
|
readonly "native_app.updated": "native_app";
|
|
97
99
|
readonly "native_app.deleted": "native_app";
|
|
100
|
+
readonly "sso_connection.created": "sso_connection";
|
|
101
|
+
readonly "sso_connection.updated": "sso_connection";
|
|
102
|
+
readonly "sso_connection.deleted": "sso_connection";
|
|
103
|
+
readonly "scim_token.created": "scim_token";
|
|
104
|
+
readonly "scim_token.revoked": "scim_token";
|
|
105
|
+
readonly "setup_link.created": "setup_link";
|
|
106
|
+
readonly "setup_link.revoked": "setup_link";
|
|
107
|
+
readonly "user.scim_provisioned": "user";
|
|
108
|
+
readonly "user.scim_updated": "user";
|
|
109
|
+
readonly "scim_group.created": "scim_group";
|
|
110
|
+
readonly "scim_group.updated": "scim_group";
|
|
111
|
+
readonly "scim_group.deleted": "scim_group";
|
|
112
|
+
readonly "organization.created": "organization";
|
|
113
|
+
readonly "organization.updated": "organization";
|
|
114
|
+
readonly "organization.deleted": "organization";
|
|
115
|
+
readonly "organization_membership.added": "organization_membership";
|
|
116
|
+
readonly "organization_membership.role_changed": "organization_membership";
|
|
117
|
+
readonly "organization_membership.removed": "organization_membership";
|
|
118
|
+
readonly "organization_role.created": "organization_role";
|
|
119
|
+
readonly "organization_role.updated": "organization_role";
|
|
120
|
+
readonly "organization_role.deleted": "organization_role";
|
|
121
|
+
readonly "organization_domain.claimed": "organization_domain";
|
|
122
|
+
readonly "organization_domain.verified": "organization_domain";
|
|
123
|
+
readonly "organization_domain.updated": "organization_domain";
|
|
124
|
+
readonly "organization_domain.removed": "organization_domain";
|
|
125
|
+
readonly "organization_invitation.created": "organization_invitation";
|
|
126
|
+
readonly "organization_invitation.accepted": "organization_invitation";
|
|
127
|
+
readonly "organization_invitation.declined": "organization_invitation";
|
|
128
|
+
readonly "organization_invitation.revoked": "organization_invitation";
|
|
98
129
|
readonly "proposal.created": "proposal";
|
|
99
130
|
readonly "proposal.approved": "proposal";
|
|
131
|
+
readonly "proposal.rejected": "proposal";
|
|
100
132
|
readonly "proposal.executed": "proposal";
|
|
101
133
|
readonly "proposal.failed": "proposal";
|
|
134
|
+
readonly "audit.exported": "environment";
|
|
102
135
|
readonly "sign_in_test.started": "environment";
|
|
103
136
|
readonly "sign_in_test.finished": "environment";
|
|
104
137
|
};
|
package/dist/event-types.js
CHANGED