@feelflow/ffid-sdk 5.17.1 → 5.19.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 +77 -0
- package/dist/{chunk-26X62P76.js → chunk-D3PZ6SZB.js} +138 -18
- package/dist/{chunk-A2KK6L63.cjs → chunk-TW2FXASO.cjs} +137 -16
- package/dist/components/index.cjs +8 -8
- package/dist/components/index.js +1 -1
- package/dist/{ffid-client-Cwk6cG3b.d.cts → ffid-client-BuDM5tmq.d.cts} +214 -1
- package/dist/{ffid-client-w8Twi5lD.d.ts → ffid-client-D0x8TBS5.d.ts} +214 -1
- package/dist/index.cjs +45 -41
- package/dist/index.d.cts +241 -1
- package/dist/index.d.ts +241 -1
- package/dist/index.js +2 -2
- package/dist/server/index.cjs +137 -16
- package/dist/server/index.d.cts +34 -6
- package/dist/server/index.d.ts +34 -6
- package/dist/server/index.js +138 -18
- package/dist/server/test/index.d.cts +1 -1
- package/dist/server/test/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -396,6 +396,83 @@ await ffid.updateMemberRole({
|
|
|
396
396
|
- `role` は `admin` / `member` / `viewer` のみ指定可能です。`owner` は追加・昇格 API では扱いません。
|
|
397
397
|
- agency client と同じ呼び味が必要な場合は `ffid.addMember(organizationId, { email, role })` も使えます。
|
|
398
398
|
|
|
399
|
+
### Provisioning(users / organizations)
|
|
400
|
+
|
|
401
|
+
外部サービスからの一括移行向けに、**service-key 認証**でユーザー・組織を冪等にプロビジョニングするメソッド。REST を直叩きせず `createFFIDClient` 経由で呼べる(SDK-first、#3790 / #4127)。
|
|
402
|
+
|
|
403
|
+
- **`authMode: 'service-key'`(`X-Service-Api-Key`)専用**。`token`(Bearer)/ `cookie` モードで呼ぶとサーバーへ往復せず即座に `VALIDATION_ERROR` を返す(サーバー側エンドポイントが service-key 認証のみを受け付けるため)。
|
|
404
|
+
- 必要 scope: `provisionUser` → `user:provision`、`provisionOrganization` → `organization:write`。
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
import { createFFIDClient } from '@feelflow/ffid-sdk/server'
|
|
408
|
+
|
|
409
|
+
const ffid = createFFIDClient({
|
|
410
|
+
serviceCode: 'praxis',
|
|
411
|
+
scope: '',
|
|
412
|
+
authMode: 'service-key',
|
|
413
|
+
serviceApiKey: process.env.FFID_SERVICE_API_KEY!,
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
// 1. ユーザーを冪等に作成(新規なら created:true / 既存なら created:false)
|
|
417
|
+
const userRes = await ffid.provisionUser({
|
|
418
|
+
email: 'owner@example.com',
|
|
419
|
+
profile: { displayName: '山田 太郎', companyName: 'Example Inc.' },
|
|
420
|
+
})
|
|
421
|
+
if (userRes.error) throw new Error(userRes.error.message)
|
|
422
|
+
if (!userRes.data.dryRun) {
|
|
423
|
+
console.log(userRes.data.created ? '新規作成' : '既存ユーザー', userRes.data.user.id)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// 2. その owner の組織を作成し、メンバーを冪等に追加
|
|
427
|
+
const orgRes = await ffid.provisionOrganization({
|
|
428
|
+
name: 'Example 組織',
|
|
429
|
+
ownerEmail: 'owner@example.com',
|
|
430
|
+
members: [
|
|
431
|
+
{ email: 'member1@example.com', role: 'member' },
|
|
432
|
+
{ email: 'member2@example.com', role: 'viewer' },
|
|
433
|
+
],
|
|
434
|
+
})
|
|
435
|
+
if (orgRes.error) {
|
|
436
|
+
// owner が未登録なら error.code === 'OWNER_NOT_FOUND'(先に provisionUser が必要)
|
|
437
|
+
throw new Error(orgRes.error.message)
|
|
438
|
+
}
|
|
439
|
+
if (!orgRes.data.dryRun) {
|
|
440
|
+
for (const m of orgRes.data.members) {
|
|
441
|
+
console.log(m.email, m.status) // 'added' | 'already_member' | 'user_not_found'
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
#### 冪等セマンティクス
|
|
447
|
+
|
|
448
|
+
- `provisionUser`: **既存メールは `created: false`**(HTTP 200、no-op)、新規メールは `created: true`(HTTP 201、パスワードレス・メール確認済み)。メールはサーバー側で normalize(trim + lowercase)されるため、大文字小文字違いの再送でも同一ユーザーに解決する。新規作成で `profile` を渡した場合のみ `profileWritten` が付き、`false` は「ユーザーは作成されたがプロフィール書き込みに失敗」→ 再送を推奨。
|
|
449
|
+
- `provisionOrganization`: owner が同名の組織を既に持つ場合は `created: false`(HTTP 200)。`members` は冪等に追加され、各要素の `status` が `added` / `already_member` / `user_not_found` を返す。
|
|
450
|
+
|
|
451
|
+
#### dryRun
|
|
452
|
+
|
|
453
|
+
`dryRun: true` を渡すと **一切書き込まず**、実行時に何が起きるかだけを返す。レスポンスは `dryRun` 判別のタグ付きユニオン。
|
|
454
|
+
|
|
455
|
+
```ts
|
|
456
|
+
const preview = await ffid.provisionUser({ email: 'owner@example.com', dryRun: true })
|
|
457
|
+
if (!preview.error && preview.data.dryRun) {
|
|
458
|
+
console.log(preview.data.wouldCreate) // true = 実行すれば新規作成される
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const orgPreview = await ffid.provisionOrganization({
|
|
462
|
+
name: 'Example 組織',
|
|
463
|
+
ownerEmail: 'owner@example.com',
|
|
464
|
+
members: [{ email: 'member1@example.com', role: 'member' }],
|
|
465
|
+
dryRun: true,
|
|
466
|
+
})
|
|
467
|
+
if (!orgPreview.error && orgPreview.data.dryRun) {
|
|
468
|
+
console.log(orgPreview.data.wouldCreate) // 組織を新規作成するか
|
|
469
|
+
// per-member plan status: 'would_add' | 'already_member' | 'user_not_found'
|
|
470
|
+
orgPreview.data.members.forEach((m) => console.log(m.email, m.status))
|
|
471
|
+
}
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
`if (res.data.dryRun)` で TypeScript が型を絞り込むため、dry-run 分岐では `wouldCreate`、実行分岐では `created` がそれぞれ型安全に参照できる。
|
|
475
|
+
|
|
399
476
|
### getProfile() / updateProfile()
|
|
400
477
|
|
|
401
478
|
ログイン中ユーザー自身のプロフィールを取得・更新するメソッド(`createFFIDClient` から呼び出し)。
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContext, useState, useRef, useEffect, useMemo, useCallback, useContext } from 'react';
|
|
2
|
-
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
2
|
+
import { createRemoteJWKSet, jwtVerify, errors } from 'jose';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
// src/constants.ts
|
|
@@ -228,10 +228,29 @@ function createJwtVerifier(deps) {
|
|
|
228
228
|
return { data: userInfo };
|
|
229
229
|
} catch (error) {
|
|
230
230
|
const message = error instanceof Error ? error.message : "JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
|
|
231
|
-
|
|
231
|
+
if (error instanceof errors.JWTExpired) {
|
|
232
|
+
logger.debug("JWT verification failed (token expired):", message);
|
|
233
|
+
return {
|
|
234
|
+
error: createError(
|
|
235
|
+
errorCodes.TOKEN_EXPIRED,
|
|
236
|
+
"\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306E\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u3066\u3044\u307E\u3059"
|
|
237
|
+
)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const isTokenLevelError = error instanceof errors.JWTClaimValidationFailed || error instanceof errors.JWTInvalid || error instanceof errors.JWSInvalid || error instanceof errors.JWSSignatureVerificationFailed || error instanceof errors.JOSEAlgNotAllowed || error instanceof errors.JOSENotSupported || error instanceof errors.JWKSNoMatchingKey || error instanceof errors.JWKSMultipleMatchingKeys;
|
|
241
|
+
if (isTokenLevelError) {
|
|
242
|
+
logger.warn("JWT verification failed:", message);
|
|
243
|
+
return {
|
|
244
|
+
error: createError(
|
|
245
|
+
errorCodes.TOKEN_VERIFICATION_ERROR,
|
|
246
|
+
`JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${message}`
|
|
247
|
+
)
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
logger.error("JWT verification failed (JWKS fetch):", message);
|
|
232
251
|
return {
|
|
233
252
|
error: createError(
|
|
234
|
-
errorCodes.
|
|
253
|
+
errorCodes.NETWORK_ERROR,
|
|
235
254
|
`JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${message}`
|
|
236
255
|
)
|
|
237
256
|
};
|
|
@@ -256,7 +275,9 @@ function createVerifyAccessToken(deps) {
|
|
|
256
275
|
serviceCode,
|
|
257
276
|
logger,
|
|
258
277
|
createError,
|
|
259
|
-
|
|
278
|
+
// structural superset of JwtVerifierDeps.errorCodes — passing it
|
|
279
|
+
// wholesale removes the hand-transcription drift point
|
|
280
|
+
errorCodes
|
|
260
281
|
});
|
|
261
282
|
}
|
|
262
283
|
return jwtVerify2;
|
|
@@ -852,6 +873,93 @@ function createMembersMethods(deps) {
|
|
|
852
873
|
return { listMembers, addMember, updateMemberRole, removeMember };
|
|
853
874
|
}
|
|
854
875
|
|
|
876
|
+
// src/client/provisioning-methods.ts
|
|
877
|
+
var USER_PROVISION_ENDPOINT = "/api/v1/ext/users/provision";
|
|
878
|
+
var ORGANIZATION_PROVISION_ENDPOINT = "/api/v1/ext/organizations/provision";
|
|
879
|
+
var MAX_PROVISION_MEMBERS = 100;
|
|
880
|
+
var ASSIGNABLE_MEMBER_ROLES = {
|
|
881
|
+
admin: true,
|
|
882
|
+
member: true,
|
|
883
|
+
viewer: true
|
|
884
|
+
};
|
|
885
|
+
function isAssignableMemberRole(role) {
|
|
886
|
+
return Object.hasOwn(ASSIGNABLE_MEMBER_ROLES, role);
|
|
887
|
+
}
|
|
888
|
+
function createProvisioningMethods(deps) {
|
|
889
|
+
const { fetchWithAuth, createError, authMode } = deps;
|
|
890
|
+
function serviceKeyModeError() {
|
|
891
|
+
if (authMode === "service-key") return null;
|
|
892
|
+
return createError(
|
|
893
|
+
"VALIDATION_ERROR",
|
|
894
|
+
"provisionUser / provisionOrganization \u306F service-key \u8A8D\u8A3C\uFF08X-Service-Api-Key\uFF09\u3067\u306E\u307F\u5229\u7528\u3067\u304D\u307E\u3059\u3002Bearer / cookie \u30E2\u30FC\u30C9\u3067\u306F\u547C\u3073\u51FA\u305B\u307E\u305B\u3093"
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
async function provisionUser(params) {
|
|
898
|
+
const modeError = serviceKeyModeError();
|
|
899
|
+
if (modeError) return { error: modeError };
|
|
900
|
+
if (!params.email || !params.email.trim()) {
|
|
901
|
+
return { error: createError("VALIDATION_ERROR", "email \u306F\u5FC5\u9808\u3067\u3059") };
|
|
902
|
+
}
|
|
903
|
+
const body = { email: params.email.trim() };
|
|
904
|
+
if (params.profile !== void 0) body.profile = params.profile;
|
|
905
|
+
if (params.dryRun !== void 0) body.dryRun = params.dryRun;
|
|
906
|
+
return fetchWithAuth(USER_PROVISION_ENDPOINT, {
|
|
907
|
+
method: "POST",
|
|
908
|
+
body: JSON.stringify(body)
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
async function provisionOrganization(params) {
|
|
912
|
+
const modeError = serviceKeyModeError();
|
|
913
|
+
if (modeError) return { error: modeError };
|
|
914
|
+
if (!params.name || !params.name.trim()) {
|
|
915
|
+
return { error: createError("VALIDATION_ERROR", "name \u306F\u5FC5\u9808\u3067\u3059") };
|
|
916
|
+
}
|
|
917
|
+
if (!params.ownerEmail || !params.ownerEmail.trim()) {
|
|
918
|
+
return { error: createError("VALIDATION_ERROR", "ownerEmail \u306F\u5FC5\u9808\u3067\u3059") };
|
|
919
|
+
}
|
|
920
|
+
if (params.members) {
|
|
921
|
+
if (params.members.length > MAX_PROVISION_MEMBERS) {
|
|
922
|
+
return {
|
|
923
|
+
error: createError(
|
|
924
|
+
"VALIDATION_ERROR",
|
|
925
|
+
`members \u306F1\u30EA\u30AF\u30A8\u30B9\u30C8\u3042\u305F\u308A\u6700\u5927 ${MAX_PROVISION_MEMBERS} \u4EF6\u307E\u3067\u3067\u3059`
|
|
926
|
+
)
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
for (const member of params.members) {
|
|
930
|
+
if (!member.email || !member.email.trim()) {
|
|
931
|
+
return { error: createError("VALIDATION_ERROR", "members[].email \u306F\u5FC5\u9808\u3067\u3059") };
|
|
932
|
+
}
|
|
933
|
+
if (!isAssignableMemberRole(member.role)) {
|
|
934
|
+
return {
|
|
935
|
+
error: createError(
|
|
936
|
+
"VALIDATION_ERROR",
|
|
937
|
+
"members[].role \u306F admin\u3001member\u3001viewer \u306E\u3044\u305A\u308C\u304B\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
938
|
+
)
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
const body = {
|
|
944
|
+
name: params.name.trim(),
|
|
945
|
+
ownerEmail: params.ownerEmail.trim()
|
|
946
|
+
};
|
|
947
|
+
if (params.billingEmail !== void 0) body.billingEmail = params.billingEmail.trim();
|
|
948
|
+
if (params.members !== void 0) {
|
|
949
|
+
body.members = params.members.map((member) => ({
|
|
950
|
+
email: member.email.trim(),
|
|
951
|
+
role: member.role
|
|
952
|
+
}));
|
|
953
|
+
}
|
|
954
|
+
if (params.dryRun !== void 0) body.dryRun = params.dryRun;
|
|
955
|
+
return fetchWithAuth(ORGANIZATION_PROVISION_ENDPOINT, {
|
|
956
|
+
method: "POST",
|
|
957
|
+
body: JSON.stringify(body)
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
return { provisionUser, provisionOrganization };
|
|
961
|
+
}
|
|
962
|
+
|
|
855
963
|
// src/client/seat-methods.ts
|
|
856
964
|
function seatsEndpoint(subscriptionId) {
|
|
857
965
|
return `/api/v1/subscriptions/ext/${encodeURIComponent(subscriptionId)}/seats/assignments`;
|
|
@@ -1096,7 +1204,7 @@ function createNonContractMethods(deps) {
|
|
|
1096
1204
|
}
|
|
1097
1205
|
|
|
1098
1206
|
// src/client/version-check.ts
|
|
1099
|
-
var SDK_VERSION = "5.
|
|
1207
|
+
var SDK_VERSION = "5.19.0";
|
|
1100
1208
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
1101
1209
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
1102
1210
|
function sdkHeaders() {
|
|
@@ -2693,6 +2801,19 @@ function isBlockedFromUserinfo(sub) {
|
|
|
2693
2801
|
return BLOCKING_EFFECTIVE_STATUSES.includes(effectiveStatus);
|
|
2694
2802
|
}
|
|
2695
2803
|
|
|
2804
|
+
// src/client/error-codes.ts
|
|
2805
|
+
var FFID_ERROR_CODES = {
|
|
2806
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
2807
|
+
PARSE_ERROR: "PARSE_ERROR",
|
|
2808
|
+
UNKNOWN_ERROR: "UNKNOWN_ERROR",
|
|
2809
|
+
TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
|
|
2810
|
+
TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
|
|
2811
|
+
NO_TOKENS: "NO_TOKENS",
|
|
2812
|
+
TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
|
|
2813
|
+
TOKEN_EXPIRED: "TOKEN_EXPIRED",
|
|
2814
|
+
STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
|
|
2815
|
+
};
|
|
2816
|
+
|
|
2696
2817
|
// src/client/ffid-client.ts
|
|
2697
2818
|
var UNAUTHORIZED_STATUS2 = 401;
|
|
2698
2819
|
var SDK_LOG_PREFIX = "[FFID SDK]";
|
|
@@ -2701,8 +2822,10 @@ var noopLogger = {
|
|
|
2701
2822
|
},
|
|
2702
2823
|
info: () => {
|
|
2703
2824
|
},
|
|
2704
|
-
warn: (
|
|
2705
|
-
|
|
2825
|
+
warn: () => {
|
|
2826
|
+
},
|
|
2827
|
+
error: () => {
|
|
2828
|
+
}
|
|
2706
2829
|
};
|
|
2707
2830
|
var consoleLogger = {
|
|
2708
2831
|
debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
|
|
@@ -2710,16 +2833,6 @@ var consoleLogger = {
|
|
|
2710
2833
|
warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
|
|
2711
2834
|
error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
|
|
2712
2835
|
};
|
|
2713
|
-
var FFID_ERROR_CODES = {
|
|
2714
|
-
NETWORK_ERROR: "NETWORK_ERROR",
|
|
2715
|
-
PARSE_ERROR: "PARSE_ERROR",
|
|
2716
|
-
UNKNOWN_ERROR: "UNKNOWN_ERROR",
|
|
2717
|
-
TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
|
|
2718
|
-
TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
|
|
2719
|
-
NO_TOKENS: "NO_TOKENS",
|
|
2720
|
-
TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
|
|
2721
|
-
STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
|
|
2722
|
-
};
|
|
2723
2836
|
var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
|
|
2724
2837
|
var DEFAULT_ALLOW_GRACE = true;
|
|
2725
2838
|
var DEFAULT_SERVICE_ACCESS_FAIL_POLICY = "failClosed";
|
|
@@ -3102,6 +3215,11 @@ function createFFIDClient(config) {
|
|
|
3102
3215
|
createError,
|
|
3103
3216
|
serviceCode: config.serviceCode
|
|
3104
3217
|
});
|
|
3218
|
+
const { provisionUser, provisionOrganization } = createProvisioningMethods({
|
|
3219
|
+
fetchWithAuth,
|
|
3220
|
+
createError,
|
|
3221
|
+
authMode
|
|
3222
|
+
});
|
|
3105
3223
|
const { getProfile, updateProfile } = createProfileMethods({
|
|
3106
3224
|
fetchWithAuth,
|
|
3107
3225
|
createError
|
|
@@ -3197,6 +3315,8 @@ function createFFIDClient(config) {
|
|
|
3197
3315
|
addMember,
|
|
3198
3316
|
updateMemberRole,
|
|
3199
3317
|
removeMember,
|
|
3318
|
+
provisionUser,
|
|
3319
|
+
provisionOrganization,
|
|
3200
3320
|
getProfile,
|
|
3201
3321
|
updateProfile,
|
|
3202
3322
|
// Non-contract ext API coverage (#3783)
|
|
@@ -5334,4 +5454,4 @@ function FFIDInquiryForm({
|
|
|
5334
5454
|
);
|
|
5335
5455
|
}
|
|
5336
5456
|
|
|
5337
|
-
export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, EFFECTIVE_SUBSCRIPTION_STATUSES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, cleanupStateStorage, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, handleRedirectCallback, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, retrieveState, storeCodeVerifier, storeState, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
|
|
5457
|
+
export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, EFFECTIVE_SUBSCRIPTION_STATUSES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, cleanupStateStorage, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, handleRedirectCallback, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, retrieveState, storeCodeVerifier, storeState, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
|
|
@@ -230,10 +230,29 @@ function createJwtVerifier(deps) {
|
|
|
230
230
|
return { data: userInfo };
|
|
231
231
|
} catch (error) {
|
|
232
232
|
const message = error instanceof Error ? error.message : "JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
|
|
233
|
-
|
|
233
|
+
if (error instanceof jose.errors.JWTExpired) {
|
|
234
|
+
logger.debug("JWT verification failed (token expired):", message);
|
|
235
|
+
return {
|
|
236
|
+
error: createError(
|
|
237
|
+
errorCodes.TOKEN_EXPIRED,
|
|
238
|
+
"\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306E\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u3066\u3044\u307E\u3059"
|
|
239
|
+
)
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const isTokenLevelError = error instanceof jose.errors.JWTClaimValidationFailed || error instanceof jose.errors.JWTInvalid || error instanceof jose.errors.JWSInvalid || error instanceof jose.errors.JWSSignatureVerificationFailed || error instanceof jose.errors.JOSEAlgNotAllowed || error instanceof jose.errors.JOSENotSupported || error instanceof jose.errors.JWKSNoMatchingKey || error instanceof jose.errors.JWKSMultipleMatchingKeys;
|
|
243
|
+
if (isTokenLevelError) {
|
|
244
|
+
logger.warn("JWT verification failed:", message);
|
|
245
|
+
return {
|
|
246
|
+
error: createError(
|
|
247
|
+
errorCodes.TOKEN_VERIFICATION_ERROR,
|
|
248
|
+
`JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${message}`
|
|
249
|
+
)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
logger.error("JWT verification failed (JWKS fetch):", message);
|
|
234
253
|
return {
|
|
235
254
|
error: createError(
|
|
236
|
-
errorCodes.
|
|
255
|
+
errorCodes.NETWORK_ERROR,
|
|
237
256
|
`JWT\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${message}`
|
|
238
257
|
)
|
|
239
258
|
};
|
|
@@ -258,7 +277,9 @@ function createVerifyAccessToken(deps) {
|
|
|
258
277
|
serviceCode,
|
|
259
278
|
logger,
|
|
260
279
|
createError,
|
|
261
|
-
|
|
280
|
+
// structural superset of JwtVerifierDeps.errorCodes — passing it
|
|
281
|
+
// wholesale removes the hand-transcription drift point
|
|
282
|
+
errorCodes
|
|
262
283
|
});
|
|
263
284
|
}
|
|
264
285
|
return jwtVerify2;
|
|
@@ -854,6 +875,93 @@ function createMembersMethods(deps) {
|
|
|
854
875
|
return { listMembers, addMember, updateMemberRole, removeMember };
|
|
855
876
|
}
|
|
856
877
|
|
|
878
|
+
// src/client/provisioning-methods.ts
|
|
879
|
+
var USER_PROVISION_ENDPOINT = "/api/v1/ext/users/provision";
|
|
880
|
+
var ORGANIZATION_PROVISION_ENDPOINT = "/api/v1/ext/organizations/provision";
|
|
881
|
+
var MAX_PROVISION_MEMBERS = 100;
|
|
882
|
+
var ASSIGNABLE_MEMBER_ROLES = {
|
|
883
|
+
admin: true,
|
|
884
|
+
member: true,
|
|
885
|
+
viewer: true
|
|
886
|
+
};
|
|
887
|
+
function isAssignableMemberRole(role) {
|
|
888
|
+
return Object.hasOwn(ASSIGNABLE_MEMBER_ROLES, role);
|
|
889
|
+
}
|
|
890
|
+
function createProvisioningMethods(deps) {
|
|
891
|
+
const { fetchWithAuth, createError, authMode } = deps;
|
|
892
|
+
function serviceKeyModeError() {
|
|
893
|
+
if (authMode === "service-key") return null;
|
|
894
|
+
return createError(
|
|
895
|
+
"VALIDATION_ERROR",
|
|
896
|
+
"provisionUser / provisionOrganization \u306F service-key \u8A8D\u8A3C\uFF08X-Service-Api-Key\uFF09\u3067\u306E\u307F\u5229\u7528\u3067\u304D\u307E\u3059\u3002Bearer / cookie \u30E2\u30FC\u30C9\u3067\u306F\u547C\u3073\u51FA\u305B\u307E\u305B\u3093"
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
async function provisionUser(params) {
|
|
900
|
+
const modeError = serviceKeyModeError();
|
|
901
|
+
if (modeError) return { error: modeError };
|
|
902
|
+
if (!params.email || !params.email.trim()) {
|
|
903
|
+
return { error: createError("VALIDATION_ERROR", "email \u306F\u5FC5\u9808\u3067\u3059") };
|
|
904
|
+
}
|
|
905
|
+
const body = { email: params.email.trim() };
|
|
906
|
+
if (params.profile !== void 0) body.profile = params.profile;
|
|
907
|
+
if (params.dryRun !== void 0) body.dryRun = params.dryRun;
|
|
908
|
+
return fetchWithAuth(USER_PROVISION_ENDPOINT, {
|
|
909
|
+
method: "POST",
|
|
910
|
+
body: JSON.stringify(body)
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
async function provisionOrganization(params) {
|
|
914
|
+
const modeError = serviceKeyModeError();
|
|
915
|
+
if (modeError) return { error: modeError };
|
|
916
|
+
if (!params.name || !params.name.trim()) {
|
|
917
|
+
return { error: createError("VALIDATION_ERROR", "name \u306F\u5FC5\u9808\u3067\u3059") };
|
|
918
|
+
}
|
|
919
|
+
if (!params.ownerEmail || !params.ownerEmail.trim()) {
|
|
920
|
+
return { error: createError("VALIDATION_ERROR", "ownerEmail \u306F\u5FC5\u9808\u3067\u3059") };
|
|
921
|
+
}
|
|
922
|
+
if (params.members) {
|
|
923
|
+
if (params.members.length > MAX_PROVISION_MEMBERS) {
|
|
924
|
+
return {
|
|
925
|
+
error: createError(
|
|
926
|
+
"VALIDATION_ERROR",
|
|
927
|
+
`members \u306F1\u30EA\u30AF\u30A8\u30B9\u30C8\u3042\u305F\u308A\u6700\u5927 ${MAX_PROVISION_MEMBERS} \u4EF6\u307E\u3067\u3067\u3059`
|
|
928
|
+
)
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
for (const member of params.members) {
|
|
932
|
+
if (!member.email || !member.email.trim()) {
|
|
933
|
+
return { error: createError("VALIDATION_ERROR", "members[].email \u306F\u5FC5\u9808\u3067\u3059") };
|
|
934
|
+
}
|
|
935
|
+
if (!isAssignableMemberRole(member.role)) {
|
|
936
|
+
return {
|
|
937
|
+
error: createError(
|
|
938
|
+
"VALIDATION_ERROR",
|
|
939
|
+
"members[].role \u306F admin\u3001member\u3001viewer \u306E\u3044\u305A\u308C\u304B\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
940
|
+
)
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
const body = {
|
|
946
|
+
name: params.name.trim(),
|
|
947
|
+
ownerEmail: params.ownerEmail.trim()
|
|
948
|
+
};
|
|
949
|
+
if (params.billingEmail !== void 0) body.billingEmail = params.billingEmail.trim();
|
|
950
|
+
if (params.members !== void 0) {
|
|
951
|
+
body.members = params.members.map((member) => ({
|
|
952
|
+
email: member.email.trim(),
|
|
953
|
+
role: member.role
|
|
954
|
+
}));
|
|
955
|
+
}
|
|
956
|
+
if (params.dryRun !== void 0) body.dryRun = params.dryRun;
|
|
957
|
+
return fetchWithAuth(ORGANIZATION_PROVISION_ENDPOINT, {
|
|
958
|
+
method: "POST",
|
|
959
|
+
body: JSON.stringify(body)
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
return { provisionUser, provisionOrganization };
|
|
963
|
+
}
|
|
964
|
+
|
|
857
965
|
// src/client/seat-methods.ts
|
|
858
966
|
function seatsEndpoint(subscriptionId) {
|
|
859
967
|
return `/api/v1/subscriptions/ext/${encodeURIComponent(subscriptionId)}/seats/assignments`;
|
|
@@ -1098,7 +1206,7 @@ function createNonContractMethods(deps) {
|
|
|
1098
1206
|
}
|
|
1099
1207
|
|
|
1100
1208
|
// src/client/version-check.ts
|
|
1101
|
-
var SDK_VERSION = "5.
|
|
1209
|
+
var SDK_VERSION = "5.19.0";
|
|
1102
1210
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
1103
1211
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
1104
1212
|
function sdkHeaders() {
|
|
@@ -2695,6 +2803,19 @@ function isBlockedFromUserinfo(sub) {
|
|
|
2695
2803
|
return BLOCKING_EFFECTIVE_STATUSES.includes(effectiveStatus);
|
|
2696
2804
|
}
|
|
2697
2805
|
|
|
2806
|
+
// src/client/error-codes.ts
|
|
2807
|
+
var FFID_ERROR_CODES = {
|
|
2808
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
2809
|
+
PARSE_ERROR: "PARSE_ERROR",
|
|
2810
|
+
UNKNOWN_ERROR: "UNKNOWN_ERROR",
|
|
2811
|
+
TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
|
|
2812
|
+
TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
|
|
2813
|
+
NO_TOKENS: "NO_TOKENS",
|
|
2814
|
+
TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
|
|
2815
|
+
TOKEN_EXPIRED: "TOKEN_EXPIRED",
|
|
2816
|
+
STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
|
|
2817
|
+
};
|
|
2818
|
+
|
|
2698
2819
|
// src/client/ffid-client.ts
|
|
2699
2820
|
var UNAUTHORIZED_STATUS2 = 401;
|
|
2700
2821
|
var SDK_LOG_PREFIX = "[FFID SDK]";
|
|
@@ -2703,8 +2824,10 @@ var noopLogger = {
|
|
|
2703
2824
|
},
|
|
2704
2825
|
info: () => {
|
|
2705
2826
|
},
|
|
2706
|
-
warn: (
|
|
2707
|
-
|
|
2827
|
+
warn: () => {
|
|
2828
|
+
},
|
|
2829
|
+
error: () => {
|
|
2830
|
+
}
|
|
2708
2831
|
};
|
|
2709
2832
|
var consoleLogger = {
|
|
2710
2833
|
debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
|
|
@@ -2712,16 +2835,6 @@ var consoleLogger = {
|
|
|
2712
2835
|
warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
|
|
2713
2836
|
error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
|
|
2714
2837
|
};
|
|
2715
|
-
var FFID_ERROR_CODES = {
|
|
2716
|
-
NETWORK_ERROR: "NETWORK_ERROR",
|
|
2717
|
-
PARSE_ERROR: "PARSE_ERROR",
|
|
2718
|
-
UNKNOWN_ERROR: "UNKNOWN_ERROR",
|
|
2719
|
-
TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
|
|
2720
|
-
TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
|
|
2721
|
-
NO_TOKENS: "NO_TOKENS",
|
|
2722
|
-
TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
|
|
2723
|
-
STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
|
|
2724
|
-
};
|
|
2725
2838
|
var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
|
|
2726
2839
|
var DEFAULT_ALLOW_GRACE = true;
|
|
2727
2840
|
var DEFAULT_SERVICE_ACCESS_FAIL_POLICY = "failClosed";
|
|
@@ -3104,6 +3217,11 @@ function createFFIDClient(config) {
|
|
|
3104
3217
|
createError,
|
|
3105
3218
|
serviceCode: config.serviceCode
|
|
3106
3219
|
});
|
|
3220
|
+
const { provisionUser, provisionOrganization } = createProvisioningMethods({
|
|
3221
|
+
fetchWithAuth,
|
|
3222
|
+
createError,
|
|
3223
|
+
authMode
|
|
3224
|
+
});
|
|
3107
3225
|
const { getProfile, updateProfile } = createProfileMethods({
|
|
3108
3226
|
fetchWithAuth,
|
|
3109
3227
|
createError
|
|
@@ -3199,6 +3317,8 @@ function createFFIDClient(config) {
|
|
|
3199
3317
|
addMember,
|
|
3200
3318
|
updateMemberRole,
|
|
3201
3319
|
removeMember,
|
|
3320
|
+
provisionUser,
|
|
3321
|
+
provisionOrganization,
|
|
3202
3322
|
getProfile,
|
|
3203
3323
|
updateProfile,
|
|
3204
3324
|
// Non-contract ext API coverage (#3783)
|
|
@@ -5351,6 +5471,7 @@ exports.FFIDSDKError = FFIDSDKError;
|
|
|
5351
5471
|
exports.FFIDSubscriptionBadge = FFIDSubscriptionBadge;
|
|
5352
5472
|
exports.FFIDUserMenu = FFIDUserMenu;
|
|
5353
5473
|
exports.FFID_ANNOUNCEMENTS_ERROR_CODES = FFID_ANNOUNCEMENTS_ERROR_CODES;
|
|
5474
|
+
exports.FFID_ERROR_CODES = FFID_ERROR_CODES;
|
|
5354
5475
|
exports.FFID_INQUIRY_CATEGORIES = FFID_INQUIRY_CATEGORIES;
|
|
5355
5476
|
exports.FFID_INQUIRY_CATEGORIES_SITE_2026 = FFID_INQUIRY_CATEGORIES_SITE_2026;
|
|
5356
5477
|
exports.cleanupStateStorage = cleanupStateStorage;
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkTW2FXASO_cjs = require('../chunk-TW2FXASO.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
Object.defineProperty(exports, "FFIDAnnouncementBadge", {
|
|
8
8
|
enumerable: true,
|
|
9
|
-
get: function () { return
|
|
9
|
+
get: function () { return chunkTW2FXASO_cjs.FFIDAnnouncementBadge; }
|
|
10
10
|
});
|
|
11
11
|
Object.defineProperty(exports, "FFIDAnnouncementList", {
|
|
12
12
|
enumerable: true,
|
|
13
|
-
get: function () { return
|
|
13
|
+
get: function () { return chunkTW2FXASO_cjs.FFIDAnnouncementList; }
|
|
14
14
|
});
|
|
15
15
|
Object.defineProperty(exports, "FFIDInquiryForm", {
|
|
16
16
|
enumerable: true,
|
|
17
|
-
get: function () { return
|
|
17
|
+
get: function () { return chunkTW2FXASO_cjs.FFIDInquiryForm; }
|
|
18
18
|
});
|
|
19
19
|
Object.defineProperty(exports, "FFIDLoginButton", {
|
|
20
20
|
enumerable: true,
|
|
21
|
-
get: function () { return
|
|
21
|
+
get: function () { return chunkTW2FXASO_cjs.FFIDLoginButton; }
|
|
22
22
|
});
|
|
23
23
|
Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
|
|
24
24
|
enumerable: true,
|
|
25
|
-
get: function () { return
|
|
25
|
+
get: function () { return chunkTW2FXASO_cjs.FFIDOrganizationSwitcher; }
|
|
26
26
|
});
|
|
27
27
|
Object.defineProperty(exports, "FFIDSubscriptionBadge", {
|
|
28
28
|
enumerable: true,
|
|
29
|
-
get: function () { return
|
|
29
|
+
get: function () { return chunkTW2FXASO_cjs.FFIDSubscriptionBadge; }
|
|
30
30
|
});
|
|
31
31
|
Object.defineProperty(exports, "FFIDUserMenu", {
|
|
32
32
|
enumerable: true,
|
|
33
|
-
get: function () { return
|
|
33
|
+
get: function () { return chunkTW2FXASO_cjs.FFIDUserMenu; }
|
|
34
34
|
});
|
package/dist/components/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-
|
|
1
|
+
export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-D3PZ6SZB.js';
|