@_mustachio/openauth 0.11.0 → 0.13.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/dist/esm/adapters/d1/index.js +18 -9
- package/dist/esm/adapters/d1/migrations.js +7 -2
- package/dist/esm/adapters/d1/session-store.js +39 -0
- package/dist/esm/adapters/d1/session.js +2 -2
- package/dist/esm/adapters/durable-object/index.js +4 -0
- package/dist/esm/adapters/durable-object/session-store.js +42 -0
- package/dist/esm/adapters/dynamo/index.js +24 -7
- package/dist/esm/adapters/dynamo/session-store.js +49 -0
- package/dist/esm/adapters/kms/index.js +6 -2
- package/dist/esm/adapters/kv/index.js +6 -2
- package/dist/esm/adapters/memory/index.js +24 -7
- package/dist/esm/adapters/memory/session-store.js +23 -0
- package/dist/esm/adapters/postgres/executor.js +2 -2
- package/dist/esm/adapters/postgres/index.js +33 -10
- package/dist/esm/adapters/postgres/migrations.js +8 -2
- package/dist/esm/adapters/postgres/session-store.js +42 -0
- package/dist/esm/client.js +27 -27
- package/dist/esm/domain/authorize.js +2 -2
- package/dist/esm/domain/callback.js +121 -7
- package/dist/esm/domain/client-auth.js +2 -2
- package/dist/esm/domain/crypto.js +14 -14
- package/dist/esm/domain/discovery.js +2 -2
- package/dist/esm/domain/dpop.js +4 -4
- package/dist/esm/domain/id-token.js +5 -5
- package/dist/esm/domain/jwt.js +4 -4
- package/dist/esm/domain/method-dispatch.js +27 -1
- package/dist/esm/domain/method-route.js +80 -1
- package/dist/esm/domain/par.js +2 -2
- package/dist/esm/domain/pkce.js +2 -2
- package/dist/esm/domain/revoke.js +2 -2
- package/dist/esm/domain/scim/discovery.js +206 -0
- package/dist/esm/domain/scim/filter.js +177 -0
- package/dist/esm/domain/scim/handle.js +338 -0
- package/dist/esm/domain/scim/patch.js +568 -0
- package/dist/esm/domain/scim/resource.js +256 -0
- package/dist/esm/domain/state-envelope.js +2 -2
- package/dist/esm/domain/token.js +5 -5
- package/dist/esm/error.js +8 -8
- package/dist/esm/http/cookies.js +19 -2
- package/dist/esm/http/errors.js +4 -4
- package/dist/esm/http/handlers/authorize.js +3 -19
- package/dist/esm/http/handlers/callback.js +7 -1
- package/dist/esm/http/handlers/metadata.js +2 -2
- package/dist/esm/http/handlers/method-route.js +47 -2
- package/dist/esm/http/handlers/revocation.js +2 -2
- package/dist/esm/http/handlers/scim.js +59 -0
- package/dist/esm/http/middleware/tenant.js +31 -6
- package/dist/esm/http/router.js +3 -0
- package/dist/esm/http/schemas/authorize.js +2 -2
- package/dist/esm/http/schemas/revocation.js +2 -2
- package/dist/esm/http/schemas/token.js +7 -7
- package/dist/esm/index.js +34 -32
- package/dist/esm/methods/oauth2-factory.js +2 -2
- package/dist/esm/methods/password-hash.js +2 -2
- package/dist/esm/methods/providers/index.js +30 -14
- package/dist/esm/methods/saml-sp/acs.js +232 -0
- package/dist/esm/methods/saml-sp/attributes.js +77 -0
- package/dist/esm/methods/saml-sp/authnrequest.js +63 -0
- package/dist/esm/methods/saml-sp/cache-provider.js +33 -0
- package/dist/esm/methods/saml-sp/cert-rotation.js +13 -0
- package/dist/esm/methods/saml-sp/factory.js +81 -0
- package/dist/esm/methods/saml-sp/index.js +7 -0
- package/dist/esm/methods/saml-sp/metadata.js +71 -0
- package/dist/esm/methods/saml-sp/method.js +29 -0
- package/dist/esm/methods/saml-sp/parse-idp-metadata.js +102 -0
- package/dist/esm/methods/saml-sp/saml-instance.js +59 -0
- package/dist/esm/methods/saml-sp/slo-initiate.js +84 -0
- package/dist/esm/methods/saml-sp/sls.js +154 -0
- package/dist/esm/methods/saml-sp/types.js +0 -0
- package/dist/esm/ports/scim-directory.js +0 -0
- package/dist/esm/types/error.js +5 -0
- package/dist/esm/types/result.js +3 -3
- package/dist/esm/types/scim.js +0 -0
- package/dist/esm/ui/forms.js +3 -3
- package/dist/esm/ui/index.js +8 -5
- package/dist/esm/ui/picker.js +2 -2
- package/dist/types/adapters/d1/migrations.d.ts.map +1 -1
- package/dist/types/adapters/d1/session-store.d.ts +3 -0
- package/dist/types/adapters/d1/session-store.d.ts.map +1 -1
- package/dist/types/adapters/durable-object/session-store.d.ts +3 -0
- package/dist/types/adapters/durable-object/session-store.d.ts.map +1 -1
- package/dist/types/adapters/dynamo/session-store.d.ts +3 -0
- package/dist/types/adapters/dynamo/session-store.d.ts.map +1 -1
- package/dist/types/adapters/memory/session-store.d.ts +3 -0
- package/dist/types/adapters/memory/session-store.d.ts.map +1 -1
- package/dist/types/adapters/postgres/migrations.d.ts +1 -1
- package/dist/types/adapters/postgres/migrations.d.ts.map +1 -1
- package/dist/types/adapters/postgres/session-store.d.ts +3 -0
- package/dist/types/adapters/postgres/session-store.d.ts.map +1 -1
- package/dist/types/domain/callback.d.ts +16 -1
- package/dist/types/domain/callback.d.ts.map +1 -1
- package/dist/types/domain/method-dispatch.d.ts.map +1 -1
- package/dist/types/domain/method-route.d.ts +57 -1
- package/dist/types/domain/method-route.d.ts.map +1 -1
- package/dist/types/domain/scim/discovery.d.ts +11 -0
- package/dist/types/domain/scim/discovery.d.ts.map +1 -0
- package/dist/types/domain/scim/filter.d.ts +51 -0
- package/dist/types/domain/scim/filter.d.ts.map +1 -0
- package/dist/types/domain/scim/handle.d.ts +35 -0
- package/dist/types/domain/scim/handle.d.ts.map +1 -0
- package/dist/types/domain/scim/patch.d.ts +73 -0
- package/dist/types/domain/scim/patch.d.ts.map +1 -0
- package/dist/types/domain/scim/resource.d.ts +56 -0
- package/dist/types/domain/scim/resource.d.ts.map +1 -0
- package/dist/types/http/context.d.ts +6 -1
- package/dist/types/http/context.d.ts.map +1 -1
- package/dist/types/http/cookies.d.ts +9 -1
- package/dist/types/http/cookies.d.ts.map +1 -1
- package/dist/types/http/handlers/callback.d.ts.map +1 -1
- package/dist/types/http/handlers/method-route.d.ts.map +1 -1
- package/dist/types/http/handlers/scim.d.ts +3 -0
- package/dist/types/http/handlers/scim.d.ts.map +1 -0
- package/dist/types/http/middleware/tenant.d.ts.map +1 -1
- package/dist/types/http/router.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/methods/saml-sp/acs.d.ts +4 -0
- package/dist/types/methods/saml-sp/acs.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/attributes.d.ts +45 -0
- package/dist/types/methods/saml-sp/attributes.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/authnrequest.d.ts +4 -0
- package/dist/types/methods/saml-sp/authnrequest.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/cache-provider.d.ts +38 -0
- package/dist/types/methods/saml-sp/cache-provider.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/cert-rotation.d.ts +23 -0
- package/dist/types/methods/saml-sp/cert-rotation.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/factory.d.ts +920 -0
- package/dist/types/methods/saml-sp/factory.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/index.d.ts +24 -0
- package/dist/types/methods/saml-sp/index.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/metadata.d.ts +50 -0
- package/dist/types/methods/saml-sp/metadata.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/method.d.ts +17 -0
- package/dist/types/methods/saml-sp/method.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts +4 -0
- package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/saml-instance.d.ts +93 -0
- package/dist/types/methods/saml-sp/saml-instance.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/slo-initiate.d.ts +4 -0
- package/dist/types/methods/saml-sp/slo-initiate.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/sls.d.ts +4 -0
- package/dist/types/methods/saml-sp/sls.d.ts.map +1 -0
- package/dist/types/methods/saml-sp/types.d.ts +299 -0
- package/dist/types/methods/saml-sp/types.d.ts.map +1 -0
- package/dist/types/ports/audit-log.d.ts +18 -5
- package/dist/types/ports/audit-log.d.ts.map +1 -1
- package/dist/types/ports/scim-directory.d.ts +104 -0
- package/dist/types/ports/scim-directory.d.ts.map +1 -0
- package/dist/types/ports/session-store.d.ts +23 -0
- package/dist/types/ports/session-store.d.ts.map +1 -1
- package/dist/types/types/error.d.ts +5 -0
- package/dist/types/types/error.d.ts.map +1 -1
- package/dist/types/types/idp.d.ts +83 -0
- package/dist/types/types/idp.d.ts.map +1 -1
- package/dist/types/types/method.d.ts +109 -0
- package/dist/types/types/method.d.ts.map +1 -1
- package/dist/types/types/scim.d.ts +269 -0
- package/dist/types/types/scim.d.ts.map +1 -0
- package/dist/types/types/tenant.d.ts +10 -0
- package/dist/types/types/tenant.d.ts.map +1 -1
- package/package.json +9 -1
- package/src/adapters/d1/migrations.ts +5 -0
- package/src/adapters/d1/session-store.ts +68 -0
- package/src/adapters/durable-object/session-store.ts +61 -0
- package/src/adapters/dynamo/session-store.ts +63 -0
- package/src/adapters/memory/session-store.ts +39 -0
- package/src/adapters/postgres/migrations.ts +6 -0
- package/src/adapters/postgres/session-store.ts +69 -0
- package/src/domain/callback.ts +215 -7
- package/src/domain/method-dispatch.ts +53 -1
- package/src/domain/method-route.ts +190 -1
- package/src/domain/scim/discovery.ts +262 -0
- package/src/domain/scim/filter.ts +295 -0
- package/src/domain/scim/handle.ts +597 -0
- package/src/domain/scim/patch.ts +807 -0
- package/src/domain/scim/resource.ts +358 -0
- package/src/http/context.ts +9 -0
- package/src/http/cookies.ts +19 -1
- package/src/http/handlers/authorize.ts +2 -22
- package/src/http/handlers/callback.ts +9 -1
- package/src/http/handlers/method-route.ts +72 -2
- package/src/http/handlers/scim.ts +107 -0
- package/src/http/middleware/tenant.ts +47 -1
- package/src/http/router.ts +9 -0
- package/src/index.ts +23 -0
- package/src/methods/saml-sp/acs.ts +449 -0
- package/src/methods/saml-sp/attributes.ts +146 -0
- package/src/methods/saml-sp/authnrequest.ts +129 -0
- package/src/methods/saml-sp/cache-provider.ts +76 -0
- package/src/methods/saml-sp/cert-rotation.ts +34 -0
- package/src/methods/saml-sp/factory.ts +139 -0
- package/src/methods/saml-sp/index.ts +34 -0
- package/src/methods/saml-sp/metadata.ts +215 -0
- package/src/methods/saml-sp/method.ts +65 -0
- package/src/methods/saml-sp/parse-idp-metadata.ts +158 -0
- package/src/methods/saml-sp/saml-instance.ts +210 -0
- package/src/methods/saml-sp/slo-initiate.ts +180 -0
- package/src/methods/saml-sp/sls.ts +307 -0
- package/src/methods/saml-sp/types.ts +298 -0
- package/src/ports/CONSISTENCY.md +52 -24
- package/src/ports/audit-log.ts +18 -5
- package/src/ports/scim-directory.ts +166 -0
- package/src/ports/session-store.ts +24 -0
- package/src/types/error.ts +11 -0
- package/src/types/idp.ts +86 -0
- package/src/types/method.ts +110 -0
- package/src/types/scim.ts +280 -0
- package/src/types/tenant.ts +10 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// src/domain/scim/resource.ts
|
|
2
|
+
import { err, ok } from "../../types/result";
|
|
3
|
+
var SCIM_USER_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:User";
|
|
4
|
+
var SCIM_GROUP_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:Group";
|
|
5
|
+
var SCIM_ENTERPRISE_SCHEMA = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User";
|
|
6
|
+
var SCIM_LIST_SCHEMA = "urn:ietf:params:scim:api:messages:2.0:ListResponse";
|
|
7
|
+
var SCIM_ERROR_SCHEMA = "urn:ietf:params:scim:api:messages:2.0:Error";
|
|
8
|
+
var SCIM_PATCH_SCHEMA = "urn:ietf:params:scim:api:messages:2.0:PatchOp";
|
|
9
|
+
function scimErrorBody(status, detail, scimType) {
|
|
10
|
+
return {
|
|
11
|
+
schemas: [SCIM_ERROR_SCHEMA],
|
|
12
|
+
status: String(status),
|
|
13
|
+
...scimType !== undefined ? { scimType } : {},
|
|
14
|
+
detail
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
18
|
+
var asString = (v) => typeof v === "string" && v.length > 0 ? v : undefined;
|
|
19
|
+
function parseName(v) {
|
|
20
|
+
if (!isRecord(v))
|
|
21
|
+
return;
|
|
22
|
+
const out = {};
|
|
23
|
+
for (const k of [
|
|
24
|
+
"formatted",
|
|
25
|
+
"familyName",
|
|
26
|
+
"givenName",
|
|
27
|
+
"middleName",
|
|
28
|
+
"honorificPrefix",
|
|
29
|
+
"honorificSuffix"
|
|
30
|
+
]) {
|
|
31
|
+
const s = asString(v[k]);
|
|
32
|
+
if (s !== undefined)
|
|
33
|
+
out[k] = s;
|
|
34
|
+
}
|
|
35
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
36
|
+
}
|
|
37
|
+
function parseMultiValues(v) {
|
|
38
|
+
if (!Array.isArray(v))
|
|
39
|
+
return;
|
|
40
|
+
const out = [];
|
|
41
|
+
for (const entry of v) {
|
|
42
|
+
if (!isRecord(entry))
|
|
43
|
+
continue;
|
|
44
|
+
const value = asString(entry["value"]);
|
|
45
|
+
if (value === undefined)
|
|
46
|
+
continue;
|
|
47
|
+
const type = asString(entry["type"]);
|
|
48
|
+
const primary = entry["primary"];
|
|
49
|
+
out.push({
|
|
50
|
+
value,
|
|
51
|
+
...type !== undefined ? { type } : {},
|
|
52
|
+
...typeof primary === "boolean" ? { primary } : {}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return out.length > 0 ? out : undefined;
|
|
56
|
+
}
|
|
57
|
+
function parseEnterprise(v) {
|
|
58
|
+
if (!isRecord(v))
|
|
59
|
+
return;
|
|
60
|
+
const out = {};
|
|
61
|
+
for (const k of [
|
|
62
|
+
"employeeNumber",
|
|
63
|
+
"costCenter",
|
|
64
|
+
"organization",
|
|
65
|
+
"division",
|
|
66
|
+
"department"
|
|
67
|
+
]) {
|
|
68
|
+
const s = asString(v[k]);
|
|
69
|
+
if (s !== undefined)
|
|
70
|
+
out[k] = s;
|
|
71
|
+
}
|
|
72
|
+
const mgr = v["manager"];
|
|
73
|
+
if (isRecord(mgr)) {
|
|
74
|
+
const value = asString(mgr["value"]);
|
|
75
|
+
const displayName = asString(mgr["displayName"]);
|
|
76
|
+
if (value !== undefined || displayName !== undefined) {
|
|
77
|
+
out.manager = {
|
|
78
|
+
...value !== undefined ? { value } : {},
|
|
79
|
+
...displayName !== undefined ? { displayName } : {}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
84
|
+
}
|
|
85
|
+
function parseUserWrite(body) {
|
|
86
|
+
if (!isRecord(body)) {
|
|
87
|
+
return err({
|
|
88
|
+
status: 400,
|
|
89
|
+
scimType: "invalidSyntax",
|
|
90
|
+
detail: "request body must be a JSON object"
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (body["password"] !== undefined) {
|
|
94
|
+
return err({
|
|
95
|
+
status: 400,
|
|
96
|
+
scimType: "invalidValue",
|
|
97
|
+
detail: "this Service Provider does not accept 'password' over SCIM; " + "credentials are managed by the authentication methods, not the " + "directory feed"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const userName = asString(body["userName"]);
|
|
101
|
+
if (userName === undefined) {
|
|
102
|
+
return err({
|
|
103
|
+
status: 400,
|
|
104
|
+
scimType: "invalidValue",
|
|
105
|
+
detail: "userName is required and must be a non-empty string"
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const activeRaw = body["active"];
|
|
109
|
+
if (activeRaw !== undefined && typeof activeRaw !== "boolean") {
|
|
110
|
+
return err({
|
|
111
|
+
status: 400,
|
|
112
|
+
scimType: "invalidValue",
|
|
113
|
+
detail: "active must be a boolean"
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const externalId = asString(body["externalId"]);
|
|
117
|
+
const displayName = asString(body["displayName"]);
|
|
118
|
+
const name = parseName(body["name"]);
|
|
119
|
+
const emails = parseMultiValues(body["emails"]);
|
|
120
|
+
const phoneNumbers = parseMultiValues(body["phoneNumbers"]);
|
|
121
|
+
const enterprise = parseEnterprise(body[SCIM_ENTERPRISE_SCHEMA]);
|
|
122
|
+
return ok({
|
|
123
|
+
userName,
|
|
124
|
+
active: activeRaw === undefined ? true : activeRaw,
|
|
125
|
+
...externalId !== undefined ? { externalId } : {},
|
|
126
|
+
...displayName !== undefined ? { displayName } : {},
|
|
127
|
+
...name !== undefined ? { name } : {},
|
|
128
|
+
...emails !== undefined ? { emails } : {},
|
|
129
|
+
...phoneNumbers !== undefined ? { phoneNumbers } : {},
|
|
130
|
+
...enterprise !== undefined ? { enterprise } : {}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
var iso = (ms) => ms === undefined ? undefined : new Date(ms).toISOString();
|
|
134
|
+
function serializeUser(user, baseUrl) {
|
|
135
|
+
const schemas = [SCIM_USER_SCHEMA];
|
|
136
|
+
if (user.enterprise !== undefined)
|
|
137
|
+
schemas.push(SCIM_ENTERPRISE_SCHEMA);
|
|
138
|
+
const created = iso(user.createdAt);
|
|
139
|
+
const lastModified = iso(user.updatedAt);
|
|
140
|
+
return {
|
|
141
|
+
schemas,
|
|
142
|
+
id: user.id,
|
|
143
|
+
...user.externalId !== undefined ? { externalId: user.externalId } : {},
|
|
144
|
+
userName: user.userName,
|
|
145
|
+
...user.name !== undefined ? { name: user.name } : {},
|
|
146
|
+
...user.displayName !== undefined ? { displayName: user.displayName } : {},
|
|
147
|
+
...user.emails !== undefined ? { emails: user.emails } : {},
|
|
148
|
+
...user.phoneNumbers !== undefined ? { phoneNumbers: user.phoneNumbers } : {},
|
|
149
|
+
active: user.active,
|
|
150
|
+
...user.enterprise !== undefined ? { [SCIM_ENTERPRISE_SCHEMA]: user.enterprise } : {},
|
|
151
|
+
meta: {
|
|
152
|
+
resourceType: "User",
|
|
153
|
+
...created !== undefined ? { created } : {},
|
|
154
|
+
...lastModified !== undefined ? { lastModified } : {},
|
|
155
|
+
location: `${baseUrl}/Users/${encodeURIComponent(user.id)}`
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function serializeList(users, totalResults, startIndex, baseUrl) {
|
|
160
|
+
return {
|
|
161
|
+
schemas: [SCIM_LIST_SCHEMA],
|
|
162
|
+
totalResults,
|
|
163
|
+
startIndex,
|
|
164
|
+
itemsPerPage: users.length,
|
|
165
|
+
Resources: users.map((u) => serializeUser(u, baseUrl))
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function parseMembers(v) {
|
|
169
|
+
const list = Array.isArray(v) ? v : isRecord(v) ? [v] : undefined;
|
|
170
|
+
if (list === undefined)
|
|
171
|
+
return;
|
|
172
|
+
const out = [];
|
|
173
|
+
for (const entry of list) {
|
|
174
|
+
if (!isRecord(entry))
|
|
175
|
+
continue;
|
|
176
|
+
const value = asString(entry["value"]);
|
|
177
|
+
if (value === undefined)
|
|
178
|
+
continue;
|
|
179
|
+
const display = asString(entry["display"]);
|
|
180
|
+
out.push({ value, ...display !== undefined ? { display } : {} });
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
function parseGroupWrite(body) {
|
|
185
|
+
if (!isRecord(body)) {
|
|
186
|
+
return err({
|
|
187
|
+
status: 400,
|
|
188
|
+
scimType: "invalidSyntax",
|
|
189
|
+
detail: "request body must be a JSON object"
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
const displayName = asString(body["displayName"]);
|
|
193
|
+
if (displayName === undefined) {
|
|
194
|
+
return err({
|
|
195
|
+
status: 400,
|
|
196
|
+
scimType: "invalidValue",
|
|
197
|
+
detail: "displayName is required and must be a non-empty string"
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const externalId = asString(body["externalId"]);
|
|
201
|
+
const members = parseMembers(body["members"]);
|
|
202
|
+
return ok({
|
|
203
|
+
displayName,
|
|
204
|
+
...externalId !== undefined ? { externalId } : {},
|
|
205
|
+
...members !== undefined ? { members } : {}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function serializeGroup(group, baseUrl) {
|
|
209
|
+
const created = iso(group.createdAt);
|
|
210
|
+
const lastModified = iso(group.updatedAt);
|
|
211
|
+
return {
|
|
212
|
+
schemas: [SCIM_GROUP_SCHEMA],
|
|
213
|
+
id: group.id,
|
|
214
|
+
...group.externalId !== undefined ? { externalId: group.externalId } : {},
|
|
215
|
+
displayName: group.displayName,
|
|
216
|
+
...group.members !== undefined ? {
|
|
217
|
+
members: group.members.map((m) => ({
|
|
218
|
+
value: m.value,
|
|
219
|
+
...m.display !== undefined ? { display: m.display } : {},
|
|
220
|
+
$ref: `${baseUrl}/Users/${encodeURIComponent(m.value)}`,
|
|
221
|
+
type: "User"
|
|
222
|
+
}))
|
|
223
|
+
} : {},
|
|
224
|
+
meta: {
|
|
225
|
+
resourceType: "Group",
|
|
226
|
+
...created !== undefined ? { created } : {},
|
|
227
|
+
...lastModified !== undefined ? { lastModified } : {},
|
|
228
|
+
location: `${baseUrl}/Groups/${encodeURIComponent(group.id)}`
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function serializeGroupList(groups, totalResults, startIndex, baseUrl) {
|
|
233
|
+
return {
|
|
234
|
+
schemas: [SCIM_LIST_SCHEMA],
|
|
235
|
+
totalResults,
|
|
236
|
+
startIndex,
|
|
237
|
+
itemsPerPage: groups.length,
|
|
238
|
+
Resources: groups.map((g) => serializeGroup(g, baseUrl))
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
export {
|
|
242
|
+
SCIM_ENTERPRISE_SCHEMA,
|
|
243
|
+
SCIM_ERROR_SCHEMA,
|
|
244
|
+
SCIM_GROUP_SCHEMA,
|
|
245
|
+
SCIM_LIST_SCHEMA,
|
|
246
|
+
SCIM_PATCH_SCHEMA,
|
|
247
|
+
SCIM_USER_SCHEMA,
|
|
248
|
+
parseGroupWrite,
|
|
249
|
+
parseMembers,
|
|
250
|
+
parseUserWrite,
|
|
251
|
+
scimErrorBody,
|
|
252
|
+
serializeGroup,
|
|
253
|
+
serializeGroupList,
|
|
254
|
+
serializeList,
|
|
255
|
+
serializeUser
|
|
256
|
+
};
|
|
@@ -75,6 +75,6 @@ function isEnvelopeShape(value) {
|
|
|
75
75
|
return typeof v.tenantId === "string" && typeof v.flowId === "string" && typeof v.nonce === "string" && typeof v.kid === "string";
|
|
76
76
|
}
|
|
77
77
|
export {
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
mintStateEnvelope,
|
|
79
|
+
verifyStateEnvelope
|
|
80
80
|
};
|
package/dist/esm/domain/token.js
CHANGED
|
@@ -237,10 +237,10 @@ async function hashTokenForAudit(token) {
|
|
|
237
237
|
return base64url.encode(await sha256(utf8.encode(token))).slice(0, 16);
|
|
238
238
|
}
|
|
239
239
|
export {
|
|
240
|
-
|
|
241
|
-
mintTokens,
|
|
242
|
-
hashClientSecret,
|
|
243
|
-
exchangeCode,
|
|
240
|
+
DEFAULT_ACCESS_TTL_MS,
|
|
244
241
|
DEFAULT_REFRESH_TTL_MS,
|
|
245
|
-
|
|
242
|
+
exchangeCode,
|
|
243
|
+
hashClientSecret,
|
|
244
|
+
mintTokens,
|
|
245
|
+
saveEncryptedCode
|
|
246
246
|
};
|
package/dist/esm/error.js
CHANGED
|
@@ -61,13 +61,13 @@ class InvalidAuthorizationCodeError extends Error {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
export {
|
|
64
|
-
|
|
65
|
-
UnauthorizedClientError,
|
|
66
|
-
OauthError,
|
|
67
|
-
MissingProviderError,
|
|
68
|
-
MissingParameterError,
|
|
69
|
-
InvalidSubjectError,
|
|
70
|
-
InvalidRefreshTokenError,
|
|
64
|
+
InvalidAccessTokenError,
|
|
71
65
|
InvalidAuthorizationCodeError,
|
|
72
|
-
|
|
66
|
+
InvalidRefreshTokenError,
|
|
67
|
+
InvalidSubjectError,
|
|
68
|
+
MissingParameterError,
|
|
69
|
+
MissingProviderError,
|
|
70
|
+
OauthError,
|
|
71
|
+
UnauthorizedClientError,
|
|
72
|
+
UnknownStateError
|
|
73
73
|
};
|
package/dist/esm/http/cookies.js
CHANGED
|
@@ -67,6 +67,22 @@ var STRIPPED_HEADERS = [
|
|
|
67
67
|
"permissions-policy",
|
|
68
68
|
"cache-control"
|
|
69
69
|
];
|
|
70
|
+
function cacheControlHeader(cache) {
|
|
71
|
+
if (!cache)
|
|
72
|
+
return "no-store";
|
|
73
|
+
if ((cache.maxAge ?? -1) === 0)
|
|
74
|
+
return "no-store";
|
|
75
|
+
const parts = [];
|
|
76
|
+
if (cache.isPrivate)
|
|
77
|
+
parts.push("private");
|
|
78
|
+
if (cache.maxAge !== undefined)
|
|
79
|
+
parts.push(`max-age=${cache.maxAge}`);
|
|
80
|
+
if (cache.sMaxAge !== undefined)
|
|
81
|
+
parts.push(`s-maxage=${cache.sMaxAge}`);
|
|
82
|
+
if (cache.immutable)
|
|
83
|
+
parts.push("immutable");
|
|
84
|
+
return parts.length ? parts.join(", ") : "no-store";
|
|
85
|
+
}
|
|
70
86
|
function applyResponsePolicy(response, opts = {}) {
|
|
71
87
|
const headers = new Headers(response.headers);
|
|
72
88
|
const stripped = [];
|
|
@@ -92,7 +108,8 @@ function applyResponsePolicy(response, opts = {}) {
|
|
|
92
108
|
});
|
|
93
109
|
}
|
|
94
110
|
export {
|
|
95
|
-
|
|
111
|
+
applyResponsePolicy,
|
|
112
|
+
cacheControlHeader,
|
|
96
113
|
parseCookieHeader,
|
|
97
|
-
|
|
114
|
+
serializeSetCookie
|
|
98
115
|
};
|
package/dist/esm/http/errors.js
CHANGED
|
@@ -63,9 +63,9 @@ function isNonRecoverable(error) {
|
|
|
63
63
|
return false;
|
|
64
64
|
}
|
|
65
65
|
export {
|
|
66
|
-
|
|
67
|
-
publicErrorCode,
|
|
68
|
-
isNonRecoverable,
|
|
66
|
+
authorizeDirectErrorResponse,
|
|
69
67
|
authorizeRedirectErrorResponse,
|
|
70
|
-
|
|
68
|
+
isNonRecoverable,
|
|
69
|
+
publicErrorCode,
|
|
70
|
+
tokenEndpointErrorResponse
|
|
71
71
|
};
|
|
@@ -4,7 +4,7 @@ import { startAuthorize } from "../../domain/authorize";
|
|
|
4
4
|
import { asTenantId } from "../../types/tenant";
|
|
5
5
|
import { authError } from "../../types/error";
|
|
6
6
|
import { renderPicker as renderDefaultPicker } from "../../ui/picker";
|
|
7
|
-
import { applyResponsePolicy } from "../cookies";
|
|
7
|
+
import { applyResponsePolicy, cacheControlHeader } from "../cookies";
|
|
8
8
|
import {
|
|
9
9
|
authorizeDirectErrorResponse,
|
|
10
10
|
authorizeRedirectErrorResponse,
|
|
@@ -48,7 +48,7 @@ function makeAuthorizeHandler(deps) {
|
|
|
48
48
|
}
|
|
49
49
|
const q = parsed.data;
|
|
50
50
|
if (raw.request_uri === undefined) {
|
|
51
|
-
const client = tenant.config.clients.find((
|
|
51
|
+
const client = tenant.config.clients.find((c) => c.id === q.client_id);
|
|
52
52
|
if (client?.requirePushedAuthorizationRequests) {
|
|
53
53
|
return authorizeDirectErrorResponse(authError.invalidRequest("this client requires Pushed Authorization Requests (RFC 9126)", "request_uri"));
|
|
54
54
|
}
|
|
@@ -123,7 +123,7 @@ function makeAuthorizeHandler(deps) {
|
|
|
123
123
|
return applyResponsePolicy(out.response, {
|
|
124
124
|
setCookies: [flowCookie, ...out.setCookies],
|
|
125
125
|
cookieDefaults: deps.cookieDefaults,
|
|
126
|
-
cacheControl:
|
|
126
|
+
cacheControl: cacheControlHeader(out.cache)
|
|
127
127
|
});
|
|
128
128
|
}
|
|
129
129
|
case "issue-code": {
|
|
@@ -229,22 +229,6 @@ function clearFlowCookie() {
|
|
|
229
229
|
maxAge: 0
|
|
230
230
|
};
|
|
231
231
|
}
|
|
232
|
-
function cacheControlFor(cache) {
|
|
233
|
-
if (!cache)
|
|
234
|
-
return "no-store";
|
|
235
|
-
if ((cache.maxAge ?? -1) === 0)
|
|
236
|
-
return "no-store";
|
|
237
|
-
const parts = [];
|
|
238
|
-
if (cache.isPrivate)
|
|
239
|
-
parts.push("private");
|
|
240
|
-
if (cache.maxAge !== undefined)
|
|
241
|
-
parts.push(`max-age=${cache.maxAge}`);
|
|
242
|
-
if (cache.sMaxAge !== undefined)
|
|
243
|
-
parts.push(`s-maxage=${cache.sMaxAge}`);
|
|
244
|
-
if (cache.immutable)
|
|
245
|
-
parts.push("immutable");
|
|
246
|
-
return parts.length ? parts.join(", ") : "no-store";
|
|
247
|
-
}
|
|
248
232
|
export {
|
|
249
233
|
makeAuthorizeHandler
|
|
250
234
|
};
|
|
@@ -5,7 +5,13 @@ import { applyResponsePolicy } from "../cookies";
|
|
|
5
5
|
import { authorizeDirectErrorResponse } from "../errors";
|
|
6
6
|
function makeCallbackHandler(deps) {
|
|
7
7
|
return async (c) => {
|
|
8
|
-
const
|
|
8
|
+
const tenant = c.get("tenant");
|
|
9
|
+
const result = await handleCallback({
|
|
10
|
+
rawRequest: c.req.raw,
|
|
11
|
+
cookies: c.get("cookies"),
|
|
12
|
+
...tenant ? { tenant } : {},
|
|
13
|
+
issuerUrl: c.get("issuerUrl")
|
|
14
|
+
}, {
|
|
9
15
|
configStore: deps.configStore,
|
|
10
16
|
sessionStore: deps.sessionStore,
|
|
11
17
|
tokenStore: deps.tokenStore,
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
// src/http/handlers/method-route.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
handleMethodRoute,
|
|
4
|
+
handlePublicMethodRoute
|
|
5
|
+
} from "../../domain/method-route";
|
|
3
6
|
import { authError } from "../../types/error";
|
|
4
7
|
import { isErr } from "../../types/result";
|
|
5
|
-
import { applyResponsePolicy } from "../cookies";
|
|
8
|
+
import { applyResponsePolicy, cacheControlHeader } from "../cookies";
|
|
6
9
|
import { authorizeDirectErrorResponse } from "../errors";
|
|
7
10
|
function makeMethodRouteHandler(deps) {
|
|
8
11
|
return async (c) => {
|
|
@@ -10,6 +13,48 @@ function makeMethodRouteHandler(deps) {
|
|
|
10
13
|
if (!tenant) {
|
|
11
14
|
return authorizeDirectErrorResponse(authError.invalidRequest("tenant unresolved"));
|
|
12
15
|
}
|
|
16
|
+
{
|
|
17
|
+
const u = new URL(c.req.url);
|
|
18
|
+
const segs = u.pathname.split("/").filter(Boolean);
|
|
19
|
+
if (segs.length >= 3 && segs[0] === "m") {
|
|
20
|
+
const methodId = segs[1];
|
|
21
|
+
const subPath = "/" + segs.slice(2).join("/");
|
|
22
|
+
const httpMethod = c.req.method.toUpperCase() === "POST" ? "POST" : "GET";
|
|
23
|
+
const routeKey = `${httpMethod} ${subPath}`;
|
|
24
|
+
const resolved = await deps.methodCache.resolve(tenant.config, methodId);
|
|
25
|
+
if (!isErr(resolved) && resolved.value.publicRoutes?.includes(routeKey)) {
|
|
26
|
+
const issuerUrl = c.get("issuerUrl");
|
|
27
|
+
const callbackHost = deps.callbackHostFor?.(tenant.id) ?? new URL(issuerUrl).host;
|
|
28
|
+
const callbackUrl = `${new URL(issuerUrl).protocol}//${callbackHost}/cb/${methodId}`;
|
|
29
|
+
const pub = await handlePublicMethodRoute({
|
|
30
|
+
rawRequest: c.req.raw,
|
|
31
|
+
tenant,
|
|
32
|
+
method: resolved.value,
|
|
33
|
+
route: routeKey,
|
|
34
|
+
subPath,
|
|
35
|
+
dispatch: { state: "", callbackUrl, issuerUrl }
|
|
36
|
+
}, {
|
|
37
|
+
sessionStore: deps.sessionStore,
|
|
38
|
+
tokenStore: deps.tokenStore,
|
|
39
|
+
clock: deps.clock,
|
|
40
|
+
...deps.auditLog ? { auditLog: deps.auditLog } : {},
|
|
41
|
+
...deps.onLogout ? { onLogout: deps.onLogout } : {}
|
|
42
|
+
});
|
|
43
|
+
if (isErr(pub))
|
|
44
|
+
return authorizeDirectErrorResponse(pub.error);
|
|
45
|
+
if (pub.value.kind === "denied") {
|
|
46
|
+
return applyResponsePolicy(new Response(`access_denied: ${pub.value.reason}`, {
|
|
47
|
+
status: 403,
|
|
48
|
+
headers: { "content-type": "text/plain" }
|
|
49
|
+
}), { cookieDefaults: deps.cookieDefaults });
|
|
50
|
+
}
|
|
51
|
+
return applyResponsePolicy(pub.value.response, {
|
|
52
|
+
cacheControl: cacheControlHeader(pub.value.cache),
|
|
53
|
+
cookieDefaults: deps.cookieDefaults
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
13
58
|
const cookies = c.get("cookies");
|
|
14
59
|
const flowId = cookies.get("idp.flow");
|
|
15
60
|
if (!flowId) {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/http/handlers/scim.ts
|
|
2
|
+
import { handleScimRequest } from "../../domain/scim/handle";
|
|
3
|
+
import { scimErrorBody } from "../../domain/scim/resource";
|
|
4
|
+
var SCIM_CONTENT_TYPE = "application/scim+json;charset=utf-8";
|
|
5
|
+
var MOUNT = "/scim/v2";
|
|
6
|
+
function scimResponse(status, body, extra = {}) {
|
|
7
|
+
if (body === null || status === 204) {
|
|
8
|
+
return new Response(null, { status, headers: extra });
|
|
9
|
+
}
|
|
10
|
+
return new Response(JSON.stringify(body), {
|
|
11
|
+
status,
|
|
12
|
+
headers: {
|
|
13
|
+
"content-type": SCIM_CONTENT_TYPE,
|
|
14
|
+
"cache-control": "no-store",
|
|
15
|
+
...extra
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function makeScimHandler(deps) {
|
|
20
|
+
return async (c) => {
|
|
21
|
+
if (!deps.scimDirectory) {
|
|
22
|
+
return scimResponse(501, scimErrorBody(501, "SCIM is not configured on this deployment (no scimDirectory " + "was supplied to createIdP)"));
|
|
23
|
+
}
|
|
24
|
+
const tenant = c.get("tenant");
|
|
25
|
+
if (!tenant) {
|
|
26
|
+
return scimResponse(403, scimErrorBody(403, "SCIM provisioning is not enabled for this tenant"));
|
|
27
|
+
}
|
|
28
|
+
const url = new URL(c.req.url);
|
|
29
|
+
const path = url.pathname.startsWith(MOUNT) ? url.pathname.slice(MOUNT.length) : url.pathname;
|
|
30
|
+
const method = c.req.method.toUpperCase();
|
|
31
|
+
let body = null;
|
|
32
|
+
if (method === "POST" || method === "PUT" || method === "PATCH") {
|
|
33
|
+
const raw = await c.req.raw.text();
|
|
34
|
+
if (raw.length > 0) {
|
|
35
|
+
try {
|
|
36
|
+
body = JSON.parse(raw);
|
|
37
|
+
} catch {
|
|
38
|
+
return scimResponse(400, scimErrorBody(400, "request body is not valid JSON", "invalidSyntax"));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const issuer = c.get("issuerUrl");
|
|
43
|
+
const base = issuer.endsWith("/") ? issuer.slice(0, -1) : issuer;
|
|
44
|
+
const result = await handleScimRequest({
|
|
45
|
+
tenant,
|
|
46
|
+
method,
|
|
47
|
+
path,
|
|
48
|
+
query: url.searchParams,
|
|
49
|
+
body,
|
|
50
|
+
authorization: c.req.header("authorization") ?? null,
|
|
51
|
+
baseUrl: `${base}${MOUNT}`,
|
|
52
|
+
directory: deps.scimDirectory
|
|
53
|
+
});
|
|
54
|
+
return scimResponse(result.status, result.body, result.headers ?? {});
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
makeScimHandler
|
|
59
|
+
};
|
|
@@ -7,6 +7,25 @@ import {
|
|
|
7
7
|
tokenEndpointErrorResponse
|
|
8
8
|
} from "../errors";
|
|
9
9
|
import { authError } from "../../types/error";
|
|
10
|
+
function isScimPath(pathname) {
|
|
11
|
+
return pathname === "/scim/v2" || pathname.startsWith("/scim/v2/");
|
|
12
|
+
}
|
|
13
|
+
function scimTenantErrorResponse(error) {
|
|
14
|
+
const operational = error.code === "server_error" || error.code === "internal_error";
|
|
15
|
+
const status = operational ? 500 : 403;
|
|
16
|
+
const detail = operational ? "the configuration store backing this endpoint failed the request" : "SCIM provisioning is not enabled for this tenant";
|
|
17
|
+
return new Response(JSON.stringify({
|
|
18
|
+
schemas: ["urn:ietf:params:scim:api:messages:2.0:Error"],
|
|
19
|
+
status: String(status),
|
|
20
|
+
detail
|
|
21
|
+
}), {
|
|
22
|
+
status,
|
|
23
|
+
headers: {
|
|
24
|
+
"content-type": "application/scim+json;charset=utf-8",
|
|
25
|
+
"cache-control": "no-store"
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
10
29
|
function isCallbackPath(pathname) {
|
|
11
30
|
return pathname.startsWith("/cb/");
|
|
12
31
|
}
|
|
@@ -27,12 +46,12 @@ function tenantMiddleware(deps) {
|
|
|
27
46
|
const recovery = await runCallbackRecovery(c.req.raw, deps);
|
|
28
47
|
c.set("recovery", recovery);
|
|
29
48
|
if (recovery.kind !== "fresh-request") {
|
|
30
|
-
const
|
|
31
|
-
if (isErr(
|
|
32
|
-
return tokenEndpointErrorResponse(
|
|
49
|
+
const cfg = await deps.configStore.getTenantConfig(recovery.tenantId);
|
|
50
|
+
if (isErr(cfg)) {
|
|
51
|
+
return tokenEndpointErrorResponse(cfg.error);
|
|
33
52
|
}
|
|
34
53
|
const customCb = deps.buildCustomContext ? await deps.buildCustomContext(c.req.raw) : {};
|
|
35
|
-
c.set("tenant", buildTenantContext(c.req.raw, recovery.tenantId,
|
|
54
|
+
c.set("tenant", buildTenantContext(c.req.raw, recovery.tenantId, cfg.value, customCb));
|
|
36
55
|
return next();
|
|
37
56
|
}
|
|
38
57
|
}
|
|
@@ -41,6 +60,9 @@ function tenantMiddleware(deps) {
|
|
|
41
60
|
if (url.pathname === "/authorize") {
|
|
42
61
|
return authorizeDirectErrorResponse(resolved.error);
|
|
43
62
|
}
|
|
63
|
+
if (isScimPath(url.pathname)) {
|
|
64
|
+
return scimTenantErrorResponse(resolved.error);
|
|
65
|
+
}
|
|
44
66
|
return tokenEndpointErrorResponse(resolved.error);
|
|
45
67
|
}
|
|
46
68
|
const tenantId = resolved.value;
|
|
@@ -50,6 +72,9 @@ function tenantMiddleware(deps) {
|
|
|
50
72
|
if (url.pathname === "/authorize") {
|
|
51
73
|
return authorizeDirectErrorResponse(err);
|
|
52
74
|
}
|
|
75
|
+
if (isScimPath(url.pathname)) {
|
|
76
|
+
return scimTenantErrorResponse(cfg.error);
|
|
77
|
+
}
|
|
53
78
|
return tokenEndpointErrorResponse(err);
|
|
54
79
|
}
|
|
55
80
|
const custom = deps.buildCustomContext ? await deps.buildCustomContext(c.req.raw) : {};
|
|
@@ -97,6 +122,6 @@ async function extractStateParam(req) {
|
|
|
97
122
|
}
|
|
98
123
|
}
|
|
99
124
|
export {
|
|
100
|
-
|
|
101
|
-
|
|
125
|
+
bootstrapMiddleware,
|
|
126
|
+
tenantMiddleware
|
|
102
127
|
};
|
package/dist/esm/http/router.js
CHANGED
|
@@ -7,6 +7,7 @@ import { makeDiscoveryHandler, makeJwksHandler } from "./handlers/metadata";
|
|
|
7
7
|
import { makeMethodRouteHandler } from "./handlers/method-route";
|
|
8
8
|
import { makeParHandler } from "./handlers/par";
|
|
9
9
|
import { makeRegisterHandler } from "./handlers/register";
|
|
10
|
+
import { makeScimHandler } from "./handlers/scim";
|
|
10
11
|
import { makeIntrospectHandler, makeRevokeHandler } from "./handlers/revocation";
|
|
11
12
|
import { makeTokenHandler } from "./handlers/token";
|
|
12
13
|
import { makeUserinfoHandler } from "./handlers/userinfo";
|
|
@@ -34,6 +35,8 @@ function buildRouter(deps) {
|
|
|
34
35
|
app.use("/end_session", tenantMiddleware(deps));
|
|
35
36
|
app.get("/end_session", makeEndSessionHandler(deps));
|
|
36
37
|
app.post("/end_session", makeEndSessionHandler(deps));
|
|
38
|
+
app.use("/scim/v2/*", tenantMiddleware(deps));
|
|
39
|
+
app.all("/scim/v2/*", makeScimHandler(deps));
|
|
37
40
|
app.use("/par", tenantMiddleware(deps));
|
|
38
41
|
app.post("/par", makeParHandler(deps));
|
|
39
42
|
app.use("/register", tenantMiddleware(deps));
|
|
@@ -25,6 +25,6 @@ var authorizeRequestUriQuerySchema = z.object({
|
|
|
25
25
|
request_uri: z.string({ required_error: "missing request_uri" }).min(1)
|
|
26
26
|
}).passthrough();
|
|
27
27
|
export {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
authorizeQuerySchema,
|
|
29
|
+
authorizeRequestUriQuerySchema
|
|
30
30
|
};
|