@oxy.so/core 1.13.0 → 1.18.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/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/crypto/identityLink.js +18 -0
- package/dist/cjs/i18n/locales/ar-SA.json +105 -85
- package/dist/cjs/i18n/locales/ca-ES.json +107 -87
- package/dist/cjs/i18n/locales/de-DE.json +105 -85
- package/dist/cjs/i18n/locales/en-US.json +105 -164
- package/dist/cjs/i18n/locales/es-ES.json +104 -163
- package/dist/cjs/i18n/locales/fr-FR.json +105 -85
- package/dist/cjs/i18n/locales/it-IT.json +105 -85
- package/dist/cjs/i18n/locales/ja-JP.json +105 -85
- package/dist/cjs/i18n/locales/ko-KR.json +105 -85
- package/dist/cjs/i18n/locales/locales/ar-SA.json +105 -85
- package/dist/cjs/i18n/locales/locales/ca-ES.json +107 -87
- package/dist/cjs/i18n/locales/locales/de-DE.json +105 -85
- package/dist/cjs/i18n/locales/locales/en-US.json +105 -164
- package/dist/cjs/i18n/locales/locales/es-ES.json +104 -163
- package/dist/cjs/i18n/locales/locales/fr-FR.json +105 -85
- package/dist/cjs/i18n/locales/locales/it-IT.json +105 -85
- package/dist/cjs/i18n/locales/locales/ja-JP.json +105 -85
- package/dist/cjs/i18n/locales/locales/ko-KR.json +105 -85
- package/dist/cjs/i18n/locales/locales/pt-PT.json +105 -85
- package/dist/cjs/i18n/locales/locales/zh-CN.json +106 -86
- package/dist/cjs/i18n/locales/pt-PT.json +105 -85
- package/dist/cjs/i18n/locales/zh-CN.json +106 -86
- package/dist/cjs/index.js +10 -43
- package/dist/cjs/mixins/OxyServices.auth.js +54 -16
- package/dist/cjs/mixins/OxyServices.identity.js +128 -8
- package/dist/cjs/mixins/OxyServices.user.js +42 -7
- package/dist/cjs/server/securityHeaders.js +19 -1
- package/dist/cjs/session/accountDialogController.js +37 -142
- package/dist/cjs/utils/authWebUrl.js +6 -6
- package/dist/cjs/utils/oauthPkce.js +5 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/crypto/identityLink.js +15 -0
- package/dist/esm/i18n/locales/ar-SA.json +105 -85
- package/dist/esm/i18n/locales/ca-ES.json +107 -87
- package/dist/esm/i18n/locales/de-DE.json +105 -85
- package/dist/esm/i18n/locales/en-US.json +105 -164
- package/dist/esm/i18n/locales/es-ES.json +104 -163
- package/dist/esm/i18n/locales/fr-FR.json +105 -85
- package/dist/esm/i18n/locales/it-IT.json +105 -85
- package/dist/esm/i18n/locales/ja-JP.json +105 -85
- package/dist/esm/i18n/locales/ko-KR.json +105 -85
- package/dist/esm/i18n/locales/locales/ar-SA.json +105 -85
- package/dist/esm/i18n/locales/locales/ca-ES.json +107 -87
- package/dist/esm/i18n/locales/locales/de-DE.json +105 -85
- package/dist/esm/i18n/locales/locales/en-US.json +105 -164
- package/dist/esm/i18n/locales/locales/es-ES.json +104 -163
- package/dist/esm/i18n/locales/locales/fr-FR.json +105 -85
- package/dist/esm/i18n/locales/locales/it-IT.json +105 -85
- package/dist/esm/i18n/locales/locales/ja-JP.json +105 -85
- package/dist/esm/i18n/locales/locales/ko-KR.json +105 -85
- package/dist/esm/i18n/locales/locales/pt-PT.json +105 -85
- package/dist/esm/i18n/locales/locales/zh-CN.json +106 -86
- package/dist/esm/i18n/locales/pt-PT.json +105 -85
- package/dist/esm/i18n/locales/zh-CN.json +106 -86
- package/dist/esm/index.js +5 -5
- package/dist/esm/mixins/OxyServices.auth.js +55 -17
- package/dist/esm/mixins/OxyServices.identity.js +128 -8
- package/dist/esm/mixins/OxyServices.user.js +42 -7
- package/dist/esm/server/securityHeaders.js +19 -1
- package/dist/esm/session/accountDialogController.js +37 -142
- package/dist/esm/utils/authWebUrl.js +5 -5
- package/dist/esm/utils/oauthPkce.js +5 -1
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/crypto/identityLink.d.ts +8 -0
- package/dist/types/index.d.ts +5 -8
- package/dist/types/mixins/OxyServices.auth.d.ts +35 -25
- package/dist/types/mixins/OxyServices.identity.d.ts +34 -34
- package/dist/types/mixins/OxyServices.user.d.ts +29 -6
- package/dist/types/models/interfaces.d.ts +46 -13
- package/dist/types/server/securityHeaders.d.ts +9 -12
- package/dist/types/session/accountDialogController.d.ts +29 -98
- package/dist/types/utils/authWebUrl.d.ts +5 -5
- package/dist/types/utils/oauthPkce.d.ts +10 -0
- package/package.json +3 -3
- package/src/crypto/__tests__/identityLink.test.ts +18 -0
- package/src/crypto/__tests__/identityProof.test.ts +6 -6
- package/src/crypto/identityLink.ts +18 -0
- package/src/i18n/locales/ar-SA.json +105 -85
- package/src/i18n/locales/ca-ES.json +107 -87
- package/src/i18n/locales/de-DE.json +105 -85
- package/src/i18n/locales/en-US.json +105 -164
- package/src/i18n/locales/es-ES.json +104 -163
- package/src/i18n/locales/fr-FR.json +105 -85
- package/src/i18n/locales/it-IT.json +105 -85
- package/src/i18n/locales/ja-JP.json +105 -85
- package/src/i18n/locales/ko-KR.json +105 -85
- package/src/i18n/locales/pt-PT.json +105 -85
- package/src/i18n/locales/zh-CN.json +106 -86
- package/src/index.ts +8 -51
- package/src/mixins/OxyServices.auth.ts +72 -24
- package/src/mixins/OxyServices.identity.ts +126 -8
- package/src/mixins/OxyServices.user.ts +51 -10
- package/src/mixins/__tests__/identityLink.test.ts +89 -0
- package/src/mixins/__tests__/notifications.read.test.ts +82 -0
- package/src/mixins/__tests__/preSessionSkipAuth.test.ts +1 -1
- package/src/mixins/__tests__/webauthnAuth.test.ts +75 -1
- package/src/models/interfaces.ts +46 -12
- package/src/server/__tests__/securityHeaders.test.ts +17 -0
- package/src/server/securityHeaders.ts +29 -1
- package/src/session/__tests__/accountDialogController.test.ts +42 -184
- package/src/session/accountDialogController.ts +50 -185
- package/src/utils/__tests__/oauthPkce.test.ts +7 -0
- package/src/utils/authWebUrl.ts +5 -5
- package/src/utils/oauthPkce.ts +16 -1
- package/dist/cjs/crypto/identityMove.js +0 -142
- package/dist/cjs/crypto/webIdentityCarrier.js +0 -325
- package/dist/esm/crypto/identityMove.js +0 -128
- package/dist/esm/crypto/webIdentityCarrier.js +0 -303
- package/dist/types/crypto/identityMove.d.ts +0 -75
- package/dist/types/crypto/webIdentityCarrier.d.ts +0 -150
- package/src/crypto/__tests__/identityMove.test.ts +0 -146
- package/src/crypto/__tests__/webIdentityCarrier.test.ts +0 -169
- package/src/crypto/__tests__/webIdentityRecoveryMaterial.test.ts +0 -117
- package/src/crypto/identityMove.ts +0 -167
- package/src/crypto/webIdentityCarrier.ts +0 -394
|
@@ -1,53 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"signin": {
|
|
3
3
|
"title": "登录",
|
|
4
|
-
"subtitle": "
|
|
5
|
-
"addAccountTitle": "
|
|
4
|
+
"subtitle": "使用你的 Oxy 账号",
|
|
5
|
+
"addAccountTitle": "添加其他账号",
|
|
6
6
|
"addAccountSubtitle": "使用另一个账户登录",
|
|
7
7
|
"username": {
|
|
8
8
|
"label": "用户名",
|
|
9
|
-
"placeholder": "
|
|
10
|
-
"
|
|
11
|
-
"required": "请输入您的用户名。",
|
|
12
|
-
"minLength": "用户名必须至少包含3个字符。"
|
|
13
|
-
},
|
|
14
|
-
"password": {
|
|
15
|
-
"label": "密码",
|
|
16
|
-
"placeholder": "输入您的密码",
|
|
17
|
-
"required": "请输入您的密码。",
|
|
18
|
-
"hint": "输入您的密码以登录"
|
|
9
|
+
"placeholder": "yourname",
|
|
10
|
+
"required": "请输入您的用户名。"
|
|
19
11
|
},
|
|
20
12
|
"actions": {
|
|
21
13
|
"continue": "继续",
|
|
22
|
-
"back": "返回"
|
|
23
|
-
"signIn": "登录",
|
|
24
|
-
"verify": "验证",
|
|
25
|
-
"openAccountSwitcher": "切换到另一个账户",
|
|
26
|
-
"openAccountSwitcherSubtitle": "{{count}}个其他账户可用",
|
|
27
|
-
"openAccountSwitcherSubtitle_singular": "1个其他账户可用",
|
|
28
|
-
"openAccountSwitcherSubtitle_zero": "查看您保存的账户",
|
|
29
|
-
"manageAccounts": "管理保存的账户",
|
|
30
|
-
"manageAccountsSubtitle": "查看会话、删除或登出",
|
|
31
|
-
"loadingOtherAccounts": "加载其他账户中…",
|
|
32
|
-
"switchAccountFailed": "无法切换账户。请重试。"
|
|
14
|
+
"back": "返回"
|
|
33
15
|
},
|
|
34
|
-
"forgotPrompt": "忘记密码?",
|
|
35
|
-
"security": {
|
|
36
|
-
"dataSecure": "您的数据已加密且安全"
|
|
37
|
-
},
|
|
38
|
-
"currentlySignedInAs": "当前登录为",
|
|
39
|
-
"alreadySignedInWith": "已登录",
|
|
40
|
-
"alreadySignedIn": "已登录",
|
|
41
|
-
"alreadySignedInMessage": "此账户已登录。继续使用此账户?",
|
|
42
|
-
"continueWithAccount": "继续",
|
|
43
|
-
"currentAccount": "当前",
|
|
44
|
-
"or": "或",
|
|
45
|
-
"viewAllAccounts": "查看更多{{count}}个",
|
|
46
16
|
"createAccountLink": "初次使用 Oxy?创建账户",
|
|
47
17
|
"status": {
|
|
48
|
-
"accountSwitched": "正在使用{{name}}",
|
|
49
18
|
"signingIn": "正在登录…"
|
|
50
|
-
}
|
|
19
|
+
},
|
|
20
|
+
"subtitleToApp": "以继续前往 {{app}}",
|
|
21
|
+
"orContinueWith": "或使用以下方式继续",
|
|
22
|
+
"qr": {
|
|
23
|
+
"caption": "使用 Commons 扫码登录",
|
|
24
|
+
"renew": "显示新的二维码"
|
|
25
|
+
},
|
|
26
|
+
"methods": {
|
|
27
|
+
"passkey": "使用通行密钥登录"
|
|
28
|
+
},
|
|
29
|
+
"errors": {
|
|
30
|
+
"rateLimited": "尝试次数过多。请在 {{seconds}} 秒后重试。",
|
|
31
|
+
"passkeyCancelled": "通行密钥提示已关闭。准备好后请重试。",
|
|
32
|
+
"passkeyFailed": "无法完成通行密钥验证。请重试。"
|
|
33
|
+
},
|
|
34
|
+
"chooser": {
|
|
35
|
+
"subtitleToApp": "以继续前往 {{app}}"
|
|
36
|
+
},
|
|
37
|
+
"terms": {
|
|
38
|
+
"before": "继续即表示你同意我们的",
|
|
39
|
+
"termsLink": "服务条款",
|
|
40
|
+
"and": "和",
|
|
41
|
+
"privacyLink": "隐私政策"
|
|
42
|
+
},
|
|
43
|
+
"noAccount": "还没有账号?",
|
|
44
|
+
"createAccount": "创建账号",
|
|
45
|
+
"recoverLink": "丢失了通行密钥?恢复你的账号"
|
|
51
46
|
},
|
|
52
47
|
"signup": {
|
|
53
48
|
"title": "创建您的账户",
|
|
@@ -55,56 +50,28 @@
|
|
|
55
50
|
"createInCommons": "在 Commons 中创建您的身份",
|
|
56
51
|
"createAccount": "创建账户",
|
|
57
52
|
"backToSignInLink": "已有账户?登录",
|
|
58
|
-
"
|
|
59
|
-
"title": "欢迎使用Oxy!",
|
|
60
|
-
"subtitle": "只需几步即可创建您的账户",
|
|
61
|
-
"haveAccount": "已有账户?",
|
|
62
|
-
"signInCta": "登录"
|
|
63
|
-
},
|
|
64
|
-
"identity": {
|
|
65
|
-
"title": "您是谁?",
|
|
66
|
-
"subtitle": "选择您的用户名并输入您的电子邮件"
|
|
67
|
-
},
|
|
53
|
+
"webSubtitle": "无需密码:你的设备会创建一个通行密钥,丢失时可通过恢复邮箱重新进入。",
|
|
68
54
|
"username": {
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
55
|
+
"label": "用户名",
|
|
56
|
+
"placeholder": "yourname",
|
|
57
|
+
"required": "请选择用户名。",
|
|
58
|
+
"taken": "该用户名已被占用。"
|
|
72
59
|
},
|
|
73
60
|
"email": {
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
"title": "保护您的账户",
|
|
80
|
-
"subtitle": "创建强密码以保护您的账户"
|
|
61
|
+
"title": "你的恢复邮箱",
|
|
62
|
+
"subtitle": "如果你丢失了通行密钥,我们会向这里发送验证码让你重新进入。它不作其他用途。",
|
|
63
|
+
"label": "邮箱",
|
|
64
|
+
"placeholder": "you@example.com",
|
|
65
|
+
"invalid": "请输入有效的邮箱地址。"
|
|
81
66
|
},
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"confirmRequired": "请确认您的密码",
|
|
87
|
-
"mismatch": "密码不匹配",
|
|
88
|
-
"confirmHint": "重新输入您的密码以确认"
|
|
67
|
+
"passkey": {
|
|
68
|
+
"title": "创建你的通行密钥",
|
|
69
|
+
"subtitle": "用指纹、面容或屏幕锁登录,无需记住任何东西。",
|
|
70
|
+
"action": "创建通行密钥"
|
|
89
71
|
},
|
|
90
|
-
"
|
|
91
|
-
"title": "快完成了!",
|
|
92
|
-
"subtitle": "查看您的信息并创建账户",
|
|
93
|
-
"sectionTitle": "账户信息",
|
|
94
|
-
"fields": {
|
|
95
|
-
"username": "用户名",
|
|
96
|
-
"email": "电子邮件",
|
|
97
|
-
"password": "密码"
|
|
98
|
-
},
|
|
99
|
-
"notSet": "未设置",
|
|
100
|
-
"securityTip": "为了更强的安全性,创建账户后,请在账户设置中启用生物识别身份验证。",
|
|
101
|
-
"legalReminder": "创建账户即表示您同意我们的服务条款和隐私政策。"
|
|
102
|
-
}
|
|
72
|
+
"commonsInstead": "想自己保管密钥?改用 Commons"
|
|
103
73
|
},
|
|
104
74
|
"accountSwitcher": {
|
|
105
|
-
"sections": {
|
|
106
|
-
"yourAccounts": "你的账户"
|
|
107
|
-
},
|
|
108
75
|
"loading": "正在加载账户...",
|
|
109
76
|
"qrHeadline": "使用您的 Oxy 身份登录",
|
|
110
77
|
"signInWithOxy": "使用 Oxy 登录",
|
|
@@ -136,10 +103,7 @@
|
|
|
136
103
|
"claimFailed": "登录已获批准,但未能完成。请重试。",
|
|
137
104
|
"generic": "登录失败,请重试。"
|
|
138
105
|
},
|
|
139
|
-
"linkOpenFailed": "无法打开链接,请重试。"
|
|
140
|
-
"continueWithPasskey": "继续",
|
|
141
|
-
"passkeyHint": "使用指纹、面容或设备 PIN 码。",
|
|
142
|
-
"otherDeviceCommons": "账号在其他设备上?用 Commons 扫码"
|
|
106
|
+
"linkOpenFailed": "无法打开链接,请重试。"
|
|
143
107
|
},
|
|
144
108
|
"common": {
|
|
145
109
|
"actions": {
|
|
@@ -280,7 +244,63 @@
|
|
|
280
244
|
"help": "帮助与反馈",
|
|
281
245
|
"signOut": "退出登录",
|
|
282
246
|
"privacy": "隐私政策",
|
|
283
|
-
"terms": "服务条款"
|
|
284
|
-
|
|
247
|
+
"terms": "服务条款"
|
|
248
|
+
},
|
|
249
|
+
"recover": {
|
|
250
|
+
"title": "恢复你的账户",
|
|
251
|
+
"subtitle": "输入你的用户名或恢复邮箱。我们会向该账户的恢复邮箱发送验证码。",
|
|
252
|
+
"identifier": {
|
|
253
|
+
"label": "用户名或邮箱",
|
|
254
|
+
"placeholder": "yourname",
|
|
255
|
+
"required": "请输入用户名或邮箱。"
|
|
256
|
+
},
|
|
257
|
+
"commonsNote": "你的账户使用 Commons?请在 Commons 中用恢复短语恢复。",
|
|
258
|
+
"passkey": {
|
|
259
|
+
"title": "添加新的通行密钥",
|
|
260
|
+
"subtitle": "用于 @{{username}}。丢失的通行密钥可稍后在账户安全设置中移除。",
|
|
261
|
+
"action": "创建通行密钥"
|
|
262
|
+
},
|
|
263
|
+
"backToSignIn": "返回登录"
|
|
264
|
+
},
|
|
265
|
+
"emailCode": {
|
|
266
|
+
"title": "查看你的邮箱",
|
|
267
|
+
"sentTo": "我们已向 {{email}} 发送了 6 位验证码。",
|
|
268
|
+
"sentIfAccount": "如果该账户有恢复邮箱,我们已向其发送 6 位验证码。",
|
|
269
|
+
"label": "验证码",
|
|
270
|
+
"resend": "发送新验证码",
|
|
271
|
+
"resent": "我们已发送新验证码。",
|
|
272
|
+
"changeEmail": "使用其他邮箱",
|
|
273
|
+
"errors": {
|
|
274
|
+
"codeInvalid": "验证码不正确或已过期。",
|
|
275
|
+
"tooManyAttempts": "错误次数过多,请重新获取验证码。",
|
|
276
|
+
"expired": "耗时过长,请重新开始。",
|
|
277
|
+
"unavailable": "Oxy 暂时无法发送邮件,请稍后再试。",
|
|
278
|
+
"rateLimited": "验证码请求过多,请稍后再试。"
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
"deleteAccount": {
|
|
282
|
+
"passkey": {
|
|
283
|
+
"subtitle": "请用通行密钥确认。@{{username}} 及其中的一切将被永久删除。",
|
|
284
|
+
"action": "用通行密钥删除",
|
|
285
|
+
"done": "你的账户已删除。",
|
|
286
|
+
"closeWindow": "你可以关闭此窗口。",
|
|
287
|
+
"commons": "此账户使用 Commons:请在 Oxy Commons 的“设置 > 删除账户”中用其密钥删除。"
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
"linkCommons": {
|
|
291
|
+
"title": "关联 Commons",
|
|
292
|
+
"subtitle": "自己保管密钥:关联 Commons 后,此账户只属于你。它的恢复邮箱会被删除,今后用 Commons 中的恢复短语找回账户。",
|
|
293
|
+
"scan": "在手机上打开 Commons,选择“我在网页上有 Oxy 账户”,然后扫描此码。",
|
|
294
|
+
"waiting": "正在等待 Commons…",
|
|
295
|
+
"compareTitle": "核对代码",
|
|
296
|
+
"compare": "Commons 会显示相同的代码。如果不同,请取消。",
|
|
297
|
+
"confirm": "是的,关联 Commons",
|
|
298
|
+
"cancel": "取消",
|
|
299
|
+
"doneTitle": "Commons 已关联",
|
|
300
|
+
"done": "此账户现在由你自己保管。它的恢复邮箱已删除:今后用 Commons 中的恢复短语找回账户。",
|
|
301
|
+
"already": "此账户已在使用 Commons。",
|
|
302
|
+
"expired": "此代码已过期。",
|
|
303
|
+
"renew": "显示新代码",
|
|
304
|
+
"failed": "无法关联 Commons,请重试。"
|
|
285
305
|
}
|
|
286
306
|
}
|
package/dist/cjs/index.js
CHANGED
|
@@ -19,11 +19,10 @@
|
|
|
19
19
|
*/
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.encryptAead = exports.hkdfSha256 = exports.RecoveryPhraseService = exports.SignatureService = exports.updateIdentityMarker = exports.readIdentityMarker = exports.IdentityUnavailableError = exports.IdentityPersistError = exports.IdentityAlreadyExistsError = exports.KeyManager = exports.sessionsArraysEqual = exports.normalizeAndSortSessions = exports.mergeSessions = exports.authenticatedApiCall = exports.withAuthErrorHandling = exports.isAuthenticationError = exports.ensureValidToken = exports.AuthenticationFailedError = exports.SessionSyncRequiredError = exports.verifyPublicCardAttestation = exports.parseAttestPayload = exports.parseIdPayload = exports.buildUserDid = exports.kindAcceptsAccountCategories = exports.isSelectableAccountCategoryId = exports.SELECTABLE_ACCOUNT_CATEGORY_IDS = exports.MAX_ACCOUNT_CATEGORIES = exports.ACCOUNT_CATEGORY_IDS = exports.normalizeProfileLinks = exports.getNormalizedUserHandle = exports.getCanonicalUserHandle = exports.normalizeUserIdentityOrNull = exports.normalizeUserIdentity = exports.getNormalizedUserId = exports.OxyAppDataIdentifierError = exports.OXY_ACCOUNT_DELETED_EVENT_URI = exports.OxyAccountEventError = exports.commonsDeliveryPlatform = exports.pushTargetsFromDelivery = exports.selectCommonsDelivery = exports.parseCommonsApprovalExpiresAt = exports.getCommonsApprovalBlockingReason = exports.ServiceCredentialMismatchError = exports.oxyClient = exports.OXY_CLOUD_URL = exports.ServiceAssetMetadataError = exports.OxyAuthenticationTimeoutError = exports.OxyAuthenticationError = exports.AssetUrlResolutionError = exports.OxyServices = void 0;
|
|
22
|
-
exports.
|
|
23
|
-
exports.
|
|
24
|
-
exports.
|
|
25
|
-
exports.
|
|
26
|
-
exports.packageInfo = exports.runSessionColdBoot = exports.OXY_INFERENCE_BASE_URL = exports.OxyInferenceProtocolError = exports.OxyInferenceError = exports.OxyInferenceClient = exports.TOKEN_REFRESH_LEAD_MS = exports.startTokenRefreshScheduler = exports.installAuthRefreshHandler = exports.createAuthRefreshHandler = exports.refreshDeviceSecretArm = exports.refreshPersistedSession = exports.AccountNotOnDeviceError = exports.establishIdentitySession = exports.resolveIdentityPin = exports.IDENTITY_PIN_STORAGE_KEY = exports.identityPinMatches = exports.createMemoryIdentityPinStore = exports.createNativeIdentityPinStore = exports.createWebIdentityPinStore = exports.readLocalDeviceCredential = exports.publishProvenDeviceCredential = exports.normalizeSharedDeviceSessionRead = exports.decideSharedDevicePublish = exports.decideSharedDeviceJoin = exports.createSharedMirroringAuthStateStore = void 0;
|
|
22
|
+
exports.isRateLimitError = exports.isNotFoundError = exports.isForbiddenError = exports.isUnauthorizedError = exports.isAlreadyRegisteredError = exports.getErrorMessage = exports.getErrorStatus = exports.HttpStatus = exports.getSystemColorScheme = exports.systemPrefersDarkMode = exports.getOppositeTheme = exports.normalizeColorScheme = exports.normalizeTheme = exports.getContrastTextColor = exports.isLightColor = exports.withOpacity = exports.rgbToHex = exports.hexToRgb = exports.lightenColor = exports.darkenColor = exports.isWebBrowser = exports.isAndroid = exports.isIOS = exports.isNative = exports.isWeb = exports.setPlatformOS = exports.getPlatformOS = exports.coerceToSupportedLocale = exports.getPrimaryLanguage = exports.getUserLanguages = exports.isRTLLocale = exports.getNativeLanguageName = exports.getLanguageName = exports.getLanguageMetadata = exports.isSupportedLocale = exports.normalizeLocale = exports.getBaseLanguage = exports.FALLBACK_LOCALE = exports.SUPPORTED_LANGUAGES = exports.TopicSource = exports.TopicType = exports.SECURITY_EVENT_SEVERITY_MAP = exports.DeviceManager = exports.deriveIdentityLinkCode = exports.signIdentityProof = exports.digestIdentityPayload = exports.deriveSharedSecret = exports.AEAD_NONCE_LENGTH = exports.AEAD_KEY_LENGTH = exports.decryptAead = void 0;
|
|
23
|
+
exports.sanitizeString = exports.isValidFileType = exports.isValidFileSize = exports.isValidDate = exports.isValidURL = exports.isValidUUID = exports.isValidObject = exports.isValidArray = exports.isRequiredBoolean = exports.isRequiredNumber = exports.isRequiredString = exports.DISPLAY_NAME_UNFLANKED_SEPARATOR_SOURCE = exports.DISPLAY_NAME_ORPHANED_MARK_SOURCE = exports.DISPLAY_NAME_DISALLOWED_SOURCE = exports.DISPLAY_NAME_ALLOWED_SCRIPTS = exports.isValidDisplayName = exports.isValidPassword = exports.isValidEmail = exports.DISPLAY_NAME_INVALID_MESSAGE = exports.MAX_DISPLAY_NAME_LENGTH = exports.PASSWORD_REGEX = exports.EMAIL_REGEX = exports.retryAsync = exports.validateRequiredFields = exports.parseHttpErrorBody = exports.isHttpRequestError = exports.handleHttpError = exports.createApiError = exports.ErrorCodes = exports.safeJsonParse = exports.buildPaginationParams = exports.buildUrl = exports.buildSearchParams = exports.buildQueryParams = exports.trustTierLabel = exports.reputationCategoryLabel = exports.accountRoleLabel = exports.accountCategoryLabel = exports.translate = exports.withRetry = exports.delay = exports.shouldAllowRequest = exports.recordSuccess = exports.recordFailure = exports.calculateBackoffInterval = exports.createCircuitBreakerState = exports.DEFAULT_CIRCUIT_BREAKER_CONFIG = exports.isRetryableError = exports.isNetworkError = exports.isServerError = void 0;
|
|
24
|
+
exports.activeUserOf = exports.activeSessionIdOf = exports.deviceStateToClientSessions = exports.createSessionClient = exports.createSessionClientHost = exports.SessionClient = exports.isAllowedDeviceJoinOrigin = exports.isOfficialWebOrigin = exports.isLoopbackOrigin = exports.consumeOAuthReturnPath = exports.persistOAuthReturnPath = exports.clearOAuthHandshake = exports.readOAuthHandshake = exports.persistOAuthHandshake = exports.canonicalizeOAuthRedirectUri = exports.normalizeOAuthRedirectUri = exports.OXY_OAUTH_RETURN_PATH_STORAGE_KEY = exports.OXY_OAUTH_REDIRECT_URI_STORAGE_KEY = exports.OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY = exports.OXY_OAUTH_STATE_STORAGE_KEY = exports.OXY_AUTHORIZE_URL = exports.DEFAULT_OAUTH_SCOPE = exports.generatePkcePair = exports.generateOAuthState = exports.computeCodeChallenge = exports.buildOAuthAuthorizeUrl = exports.runColdBoot = exports.isOxyRpOrigin = exports.CENTRAL_IDP_APEX = exports.AUTH_WEB_ORIGIN = exports.registrableApex = exports.getAccountColor = exports.formatPublicKeyHandle = exports.getAccountFallbackHandle = exports.getAccountDisplayName = exports.createQuickAccount = exports.buildAccountsArray = exports.updateAvatarVisibility = exports.isDev = exports.consoleSink = exports.resetLoggerConfig = exports.getLoggerConfig = exports.configureLogger = exports.createLogger = exports.logger = exports.normalizeMultilineText = exports.normalizeInlineText = exports.validateAndSanitizeUserInput = exports.isValidObjectId = exports.sanitizeHTML = void 0;
|
|
25
|
+
exports.packageInfo = exports.runSessionColdBoot = exports.OXY_INFERENCE_BASE_URL = exports.OxyInferenceProtocolError = exports.OxyInferenceError = exports.OxyInferenceClient = exports.TOKEN_REFRESH_LEAD_MS = exports.startTokenRefreshScheduler = exports.installAuthRefreshHandler = exports.createAuthRefreshHandler = exports.refreshDeviceSecretArm = exports.refreshPersistedSession = exports.AccountNotOnDeviceError = exports.establishIdentitySession = exports.resolveIdentityPin = exports.IDENTITY_PIN_STORAGE_KEY = exports.identityPinMatches = exports.createMemoryIdentityPinStore = exports.createNativeIdentityPinStore = exports.createWebIdentityPinStore = exports.readLocalDeviceCredential = exports.publishProvenDeviceCredential = exports.normalizeSharedDeviceSessionRead = exports.decideSharedDevicePublish = exports.decideSharedDeviceJoin = exports.createSharedMirroringAuthStateStore = exports.AUTH_STATE_STORAGE_KEY = exports.createMemoryAuthStateStore = exports.createNativeAuthStateStore = exports.createWebAuthStateStore = exports.createAccountDialogController = exports.AccountDialogController = exports.resolveAccountDelegationAccess = exports.canSwitchIntoAccount = exports.isSwitchTargetAccount = exports.showsPrincipalHeaders = exports.buildSwitcherRows = exports.resolveDeviceContext = exports.resolveActiveContext = exports.projectDevicePrincipals = exports.directoryHandle = exports.directoryDisplayName = exports.canActivateContext = exports.accountIdsOf = void 0;
|
|
27
26
|
// Ensure crypto polyfills are loaded before anything else. This MUST stay the
|
|
28
27
|
// first import: `@noble/hashes` captures `globalThis.crypto` when it is
|
|
29
28
|
// evaluated, so the shim has to exist before any module below reaches it (see
|
|
@@ -147,46 +146,14 @@ Object.defineProperty(exports, "AEAD_KEY_LENGTH", { enumerable: true, get: funct
|
|
|
147
146
|
Object.defineProperty(exports, "AEAD_NONCE_LENGTH", { enumerable: true, get: function () { return aead_1.AEAD_NONCE_LENGTH; } });
|
|
148
147
|
var ecdh_1 = require("./crypto/ecdh");
|
|
149
148
|
Object.defineProperty(exports, "deriveSharedSecret", { enumerable: true, get: function () { return ecdh_1.deriveSharedSecret; } });
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
var webIdentityCarrier_1 = require("./crypto/webIdentityCarrier");
|
|
153
|
-
Object.defineProperty(exports, "WEB_IDENTITY_PRF_INPUT", { enumerable: true, get: function () { return webIdentityCarrier_1.WEB_IDENTITY_PRF_INPUT; } });
|
|
154
|
-
Object.defineProperty(exports, "WebIdentityUnlockError", { enumerable: true, get: function () { return webIdentityCarrier_1.WebIdentityUnlockError; } });
|
|
155
|
-
Object.defineProperty(exports, "WEB_IDENTITY_PRF_OUTPUT_LENGTH", { enumerable: true, get: function () { return webIdentityCarrier_1.WEB_IDENTITY_PRF_OUTPUT_LENGTH; } });
|
|
156
|
-
Object.defineProperty(exports, "addWrap", { enumerable: true, get: function () { return webIdentityCarrier_1.addWrap; } });
|
|
157
|
-
Object.defineProperty(exports, "deriveIdentityFromMnemonic", { enumerable: true, get: function () { return webIdentityCarrier_1.deriveIdentityFromMnemonic; } });
|
|
158
|
-
Object.defineProperty(exports, "deriveIdentityFromPrivateKey", { enumerable: true, get: function () { return webIdentityCarrier_1.deriveIdentityFromPrivateKey; } });
|
|
159
|
-
Object.defineProperty(exports, "deriveIdentityFromRecoveryMaterial", { enumerable: true, get: function () { return webIdentityCarrier_1.deriveIdentityFromRecoveryMaterial; } });
|
|
160
|
-
Object.defineProperty(exports, "deriveKeyEncryptionKey", { enumerable: true, get: function () { return webIdentityCarrier_1.deriveKeyEncryptionKey; } });
|
|
161
|
-
Object.defineProperty(exports, "generateDataKey", { enumerable: true, get: function () { return webIdentityCarrier_1.generateDataKey; } });
|
|
162
|
-
Object.defineProperty(exports, "generateWebIdentity", { enumerable: true, get: function () { return webIdentityCarrier_1.generateWebIdentity; } });
|
|
163
|
-
Object.defineProperty(exports, "isUsablePrfOutput", { enumerable: true, get: function () { return webIdentityCarrier_1.isUsablePrfOutput; } });
|
|
164
|
-
Object.defineProperty(exports, "markWrapVerified", { enumerable: true, get: function () { return webIdentityCarrier_1.markWrapVerified; } });
|
|
165
|
-
Object.defineProperty(exports, "normalizeMnemonic", { enumerable: true, get: function () { return webIdentityCarrier_1.normalizeMnemonic; } });
|
|
166
|
-
Object.defineProperty(exports, "openWebIdentity", { enumerable: true, get: function () { return webIdentityCarrier_1.openWebIdentity; } });
|
|
167
|
-
Object.defineProperty(exports, "parseRecoveryMaterial", { enumerable: true, get: function () { return webIdentityCarrier_1.parseRecoveryMaterial; } });
|
|
168
|
-
Object.defineProperty(exports, "removeWrap", { enumerable: true, get: function () { return webIdentityCarrier_1.removeWrap; } });
|
|
169
|
-
Object.defineProperty(exports, "sealWebIdentity", { enumerable: true, get: function () { return webIdentityCarrier_1.sealWebIdentity; } });
|
|
170
|
-
Object.defineProperty(exports, "unlockWebIdentity", { enumerable: true, get: function () { return webIdentityCarrier_1.unlockWebIdentity; } });
|
|
171
|
-
Object.defineProperty(exports, "unwrapDataKey", { enumerable: true, get: function () { return webIdentityCarrier_1.unwrapDataKey; } });
|
|
172
|
-
Object.defineProperty(exports, "wipeBytes", { enumerable: true, get: function () { return webIdentityCarrier_1.wipeBytes; } });
|
|
173
|
-
Object.defineProperty(exports, "wipeOpenedIdentity", { enumerable: true, get: function () { return webIdentityCarrier_1.wipeOpenedIdentity; } });
|
|
149
|
+
// Identity proofs — the one signed format for operations on a personal root
|
|
150
|
+
// (docs/adr/0024-one-oxy-account-root-holders.md D7)
|
|
174
151
|
var identityProof_1 = require("./crypto/identityProof");
|
|
175
152
|
Object.defineProperty(exports, "digestIdentityPayload", { enumerable: true, get: function () { return identityProof_1.digestIdentityPayload; } });
|
|
176
153
|
Object.defineProperty(exports, "signIdentityProof", { enumerable: true, get: function () { return identityProof_1.signIdentityProof; } });
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
Object.defineProperty(exports, "
|
|
180
|
-
Object.defineProperty(exports, "deriveMoveKey", { enumerable: true, get: function () { return identityMove_1.deriveMoveKey; } });
|
|
181
|
-
Object.defineProperty(exports, "deriveMoveSas", { enumerable: true, get: function () { return identityMove_1.deriveMoveSas; } });
|
|
182
|
-
Object.defineProperty(exports, "digestMoveCiphertext", { enumerable: true, get: function () { return identityMove_1.digestMoveCiphertext; } });
|
|
183
|
-
Object.defineProperty(exports, "generateMoveEphemeralKeyPair", { enumerable: true, get: function () { return identityMove_1.generateMoveEphemeralKeyPair; } });
|
|
184
|
-
Object.defineProperty(exports, "openMovedIdentity", { enumerable: true, get: function () { return identityMove_1.openMovedIdentity; } });
|
|
185
|
-
Object.defineProperty(exports, "parseMoveQrPayload", { enumerable: true, get: function () { return identityMove_1.parseMoveQrPayload; } });
|
|
186
|
-
Object.defineProperty(exports, "sealIdentityForMove", { enumerable: true, get: function () { return identityMove_1.sealIdentityForMove; } });
|
|
187
|
-
Object.defineProperty(exports, "signMoveReceipt", { enumerable: true, get: function () { return identityMove_1.signMoveReceipt; } });
|
|
188
|
-
Object.defineProperty(exports, "verifyMoveCommitment", { enumerable: true, get: function () { return identityMove_1.verifyMoveCommitment; } });
|
|
189
|
-
Object.defineProperty(exports, "verifyMoveReceipt", { enumerable: true, get: function () { return identityMove_1.verifyMoveReceipt; } });
|
|
154
|
+
// Linking Commons to a passkey account: the code both screens compare (ADR 0029 D3)
|
|
155
|
+
var identityLink_1 = require("./crypto/identityLink");
|
|
156
|
+
Object.defineProperty(exports, "deriveIdentityLinkCode", { enumerable: true, get: function () { return identityLink_1.deriveIdentityLinkCode; } });
|
|
190
157
|
// ---------------------------------------------------------------------------
|
|
191
158
|
// Devices
|
|
192
159
|
// ---------------------------------------------------------------------------
|
|
@@ -369,8 +336,8 @@ Object.defineProperty(exports, "getAccountColor", { enumerable: true, get: funct
|
|
|
369
336
|
var registrableApex_1 = require("./utils/registrableApex");
|
|
370
337
|
Object.defineProperty(exports, "registrableApex", { enumerable: true, get: function () { return registrableApex_1.registrableApex; } });
|
|
371
338
|
var authWebUrl_1 = require("./utils/authWebUrl");
|
|
339
|
+
Object.defineProperty(exports, "AUTH_WEB_ORIGIN", { enumerable: true, get: function () { return authWebUrl_1.AUTH_WEB_ORIGIN; } });
|
|
372
340
|
Object.defineProperty(exports, "CENTRAL_IDP_APEX", { enumerable: true, get: function () { return authWebUrl_1.CENTRAL_IDP_APEX; } });
|
|
373
|
-
Object.defineProperty(exports, "IDENTITY_WEB_ORIGIN", { enumerable: true, get: function () { return authWebUrl_1.IDENTITY_WEB_ORIGIN; } });
|
|
374
341
|
// WebAuthn relying-party origin guard (client side). Mirrors the server's
|
|
375
342
|
// `isOxyApexOrigin` so consumers can decide whether to offer passkey UI on the
|
|
376
343
|
// current page (first-party Oxy origin / loopback only).
|
|
@@ -1166,12 +1166,40 @@ function OxyServicesAuthMixin(Base) {
|
|
|
1166
1166
|
}
|
|
1167
1167
|
}
|
|
1168
1168
|
/**
|
|
1169
|
-
*
|
|
1169
|
+
* Send a 6-digit code to an email (ADR 0029 D3): the recovery email of a
|
|
1170
|
+
* new passkey account (`purpose: 'signup'`), or — named by username or
|
|
1171
|
+
* email — the recovery email of an account being recovered
|
|
1172
|
+
* (`purpose: 'recovery'`). The answer is the same whether or not an
|
|
1173
|
+
* account exists; nothing is revealed about who has one.
|
|
1170
1174
|
*/
|
|
1171
|
-
async
|
|
1175
|
+
async startEmailVerification(request) {
|
|
1172
1176
|
try {
|
|
1173
|
-
|
|
1174
|
-
|
|
1177
|
+
const res = await this.makeRequest('POST', '/auth/email/verify/start', request, {
|
|
1178
|
+
cache: false,
|
|
1179
|
+
skipAuth: true,
|
|
1180
|
+
});
|
|
1181
|
+
const parsed = (0, contracts_1.safeParseContract)(contracts_1.emailVerificationStartResponseSchema, res);
|
|
1182
|
+
if (!parsed)
|
|
1183
|
+
throw new Error('auth/email/verify/start returned an unexpected response shape');
|
|
1184
|
+
return parsed;
|
|
1185
|
+
}
|
|
1186
|
+
catch (error) {
|
|
1187
|
+
throw this.handleError(error);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Confirm the code {@link startEmailVerification} sent. Resolves to a
|
|
1192
|
+
* short-lived one-use ticket: a sign-up passes it (with the email) to
|
|
1193
|
+
* {@link webauthnRegisterVerify}; a recovery passes it as `recoveryTicket`
|
|
1194
|
+
* to {@link webauthnRegisterOptions} and {@link webauthnRegisterVerify}.
|
|
1195
|
+
*/
|
|
1196
|
+
async confirmEmailVerification(verificationId, code) {
|
|
1197
|
+
try {
|
|
1198
|
+
const res = await this.makeRequest('POST', '/auth/email/verify/confirm', { verificationId, code }, { cache: false, skipAuth: true });
|
|
1199
|
+
const parsed = (0, contracts_1.safeParseContract)(contracts_1.emailVerificationConfirmResponseSchema, res);
|
|
1200
|
+
if (!parsed)
|
|
1201
|
+
throw new Error('auth/email/verify/confirm returned an unexpected response shape');
|
|
1202
|
+
return parsed;
|
|
1175
1203
|
}
|
|
1176
1204
|
catch (error) {
|
|
1177
1205
|
throw this.handleError(error);
|
|
@@ -1182,15 +1210,20 @@ function OxyServicesAuthMixin(Base) {
|
|
|
1182
1210
|
* `PublicKeyCredentialCreationOptions` the browser's `navigator.credentials
|
|
1183
1211
|
* .create()` (or `@simplewebauthn/browser`'s `startRegistration`) needs.
|
|
1184
1212
|
*
|
|
1185
|
-
* With a bearer token planted this
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
1188
|
-
*
|
|
1189
|
-
* as `unknown` for the caller
|
|
1213
|
+
* With a bearer token planted this adds a passkey to the signed-in account.
|
|
1214
|
+
* Without one, `username` is a prospective sign-up's handle, and
|
|
1215
|
+
* `recoveryTicket` a recovery's new passkey for the account it names. The
|
|
1216
|
+
* returned options are OPAQUE — Oxy does not own their shape (the browser /
|
|
1217
|
+
* `@simplewebauthn` does), so they pass through as `unknown` for the caller
|
|
1218
|
+
* to hand straight to the ceremony.
|
|
1190
1219
|
*/
|
|
1191
|
-
async webauthnRegisterOptions(
|
|
1220
|
+
async webauthnRegisterOptions(request = {}) {
|
|
1221
|
+
const signedOut = request.username !== undefined || request.recoveryTicket !== undefined;
|
|
1192
1222
|
try {
|
|
1193
|
-
return await this.makeRequest('POST', '/auth/webauthn/register/options', {
|
|
1223
|
+
return await this.makeRequest('POST', '/auth/webauthn/register/options', {
|
|
1224
|
+
...(request.username !== undefined ? { username: request.username } : {}),
|
|
1225
|
+
...(request.recoveryTicket !== undefined ? { recoveryTicket: request.recoveryTicket } : {}),
|
|
1226
|
+
}, { cache: false, ...(signedOut ? { skipAuth: true } : {}) });
|
|
1194
1227
|
}
|
|
1195
1228
|
catch (error) {
|
|
1196
1229
|
throw this.handleError(error);
|
|
@@ -1201,17 +1234,22 @@ function OxyServicesAuthMixin(Base) {
|
|
|
1201
1234
|
* browser `RegistrationResponseJSON` (`response`) alongside the Oxy envelope
|
|
1202
1235
|
* (desired `username` for signup + the device-session naming fields).
|
|
1203
1236
|
*
|
|
1204
|
-
*
|
|
1205
|
-
* - **
|
|
1206
|
-
*
|
|
1207
|
-
*
|
|
1237
|
+
* Server branches, disambiguated by the response shape:
|
|
1238
|
+
* - **Sign-up** (no bearer; `username`, `email`, `emailTicket`) and
|
|
1239
|
+
* **recovery** (no bearer; `recoveryTicket`): the account is created, or
|
|
1240
|
+
* gains the passkey, and a session is minted — the response carries
|
|
1241
|
+
* `sessionId`, is the SAME {@link LoginResult} contract as
|
|
1242
|
+
* `POST /auth/verify`, and its access token is planted here.
|
|
1208
1243
|
* - **Link** (bearer present): the passkey is attached to the signed-in
|
|
1209
1244
|
* account and the server returns `{ success, message }` with no session,
|
|
1210
1245
|
* which is returned verbatim (no token planting).
|
|
1211
1246
|
*/
|
|
1212
1247
|
async webauthnRegisterVerify(response, envelope = {}) {
|
|
1213
1248
|
try {
|
|
1214
|
-
const res = await this.makeRequest('POST', '/auth/webauthn/register/verify', { response, ...envelope }, {
|
|
1249
|
+
const res = await this.makeRequest('POST', '/auth/webauthn/register/verify', { response, ...envelope }, {
|
|
1250
|
+
cache: false,
|
|
1251
|
+
...(envelope.username !== undefined || envelope.recoveryTicket !== undefined ? { skipAuth: true } : {}),
|
|
1252
|
+
});
|
|
1215
1253
|
if (res && typeof res === 'object') {
|
|
1216
1254
|
const record = res;
|
|
1217
1255
|
// Signup branch: mints a session (LoginSessionResult, carries
|
|
@@ -2,8 +2,36 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildUserDid = buildUserDid;
|
|
4
4
|
exports.OxyServicesIdentityMixin = OxyServicesIdentityMixin;
|
|
5
|
+
/**
|
|
6
|
+
* Identity Methods Mixin (self-sovereign identity layer)
|
|
7
|
+
*
|
|
8
|
+
* Provides typed access to Oxy's AtProto/Bluesky-flavoured identity &
|
|
9
|
+
* portability layer:
|
|
10
|
+
* - DID resolution (`did:web:oxy.so:u:<userId>`, derived on demand by the API).
|
|
11
|
+
* - The auth-method ↔ DID verification-method mapping (`GET /auth/methods`) and
|
|
12
|
+
* passkey removal. A root is linked by the holder flows (ADR 0024) and never
|
|
13
|
+
* unlinked; it is replaced by rotation.
|
|
14
|
+
* - Signed records: clients sign an envelope with their own cryptographic key
|
|
15
|
+
* (`SignatureService.signRecord` + the shared `canonicalize`) and publish it;
|
|
16
|
+
* anyone can fetch and verify it.
|
|
17
|
+
* - The signed data-export ("credible exit") bundle.
|
|
18
|
+
* - Verified-domain badges (prove ownership of `nate.com`).
|
|
19
|
+
*
|
|
20
|
+
* Wire shapes come from `@oxy.so/contracts` (`DidDocument`,
|
|
21
|
+
* `SignedRecordEnvelope`, `AuthMethodsResponse`, `VerifiedDomain`,
|
|
22
|
+
* `DomainVerificationInstructions`, `ExportBundle`) — the single source of truth
|
|
23
|
+
* the API validates its output against, so producer and consumer cannot drift.
|
|
24
|
+
*
|
|
25
|
+
* Identity signing is NATIVE-ONLY: the private key lives in native secure
|
|
26
|
+
* storage, so `signRecord` and `publishRecord` require an
|
|
27
|
+
* on-device identity and throw on web (where `KeyManager.getPublicKey()` is
|
|
28
|
+
* always `null`).
|
|
29
|
+
*/
|
|
30
|
+
const contracts_1 = require("@oxy.so/contracts");
|
|
5
31
|
const protocol_1 = require("@oxy.so/protocol");
|
|
6
32
|
const keyManager_1 = require("../crypto/keyManager");
|
|
33
|
+
const identityLink_1 = require("../crypto/identityLink");
|
|
34
|
+
const identityProof_1 = require("../crypto/identityProof");
|
|
7
35
|
const signatureService_1 = require("../crypto/signatureService");
|
|
8
36
|
const recoveryPhrase_1 = require("../crypto/recoveryPhrase");
|
|
9
37
|
const platform_1 = require("../utils/platform");
|
|
@@ -75,11 +103,9 @@ function OxyServicesIdentityMixin(Base) {
|
|
|
75
103
|
}
|
|
76
104
|
}
|
|
77
105
|
/**
|
|
78
|
-
*
|
|
79
|
-
* linked,
|
|
80
|
-
*
|
|
81
|
-
* nothing here can open the root — so any first-party surface may show a
|
|
82
|
-
* "save your recovery phrase" reminder from it.
|
|
106
|
+
* How the signed-in account is kept (ADR 0029 D3): whether Commons' root is
|
|
107
|
+
* linked (self-custody), or the recovery email of a passkey account. Any
|
|
108
|
+
* first-party surface may read it to recommend linking Commons.
|
|
83
109
|
*/
|
|
84
110
|
async getIdentityRootStatus() {
|
|
85
111
|
try {
|
|
@@ -89,15 +115,109 @@ function OxyServicesIdentityMixin(Base) {
|
|
|
89
115
|
throw this.handleError(error);
|
|
90
116
|
}
|
|
91
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* Open a request to link Commons to the signed-in passkey account (ADR 0029
|
|
120
|
+
* D3). auth.oxy.so shows its `qrPayload`; Commons scans it and signs with
|
|
121
|
+
* {@link signIdentityLink}.
|
|
122
|
+
*/
|
|
123
|
+
async createIdentityLink() {
|
|
124
|
+
try {
|
|
125
|
+
const res = await this.makeRequest('POST', '/identity/link', undefined, { cache: false });
|
|
126
|
+
const parsed = (0, contracts_1.safeParseContract)(contracts_1.identityLinkCreateResponseSchema, res);
|
|
127
|
+
if (!parsed)
|
|
128
|
+
throw new Error('identity/link returned an unexpected response shape');
|
|
129
|
+
return parsed;
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
throw this.handleError(error);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/** Where a link request stands. Both devices poll it; it needs no session. */
|
|
136
|
+
async getIdentityLink(linkId) {
|
|
137
|
+
try {
|
|
138
|
+
const res = await this.makeRequest('GET', `/identity/link/${encodeURIComponent(linkId)}`, undefined, {
|
|
139
|
+
cache: false,
|
|
140
|
+
skipAuth: true,
|
|
141
|
+
});
|
|
142
|
+
const parsed = (0, contracts_1.safeParseContract)(contracts_1.identityLinkStateSchema, res);
|
|
143
|
+
if (!parsed)
|
|
144
|
+
throw new Error('identity/link returned an unexpected response shape');
|
|
145
|
+
return parsed;
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw this.handleError(error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Commons' half: sign the scanned request's `link_identity` proof with THIS
|
|
153
|
+
* device's identity key and post it. NATIVE-ONLY (the key lives in native
|
|
154
|
+
* secure storage). Resolves to the key and the 6-digit code auth.oxy.so will
|
|
155
|
+
* show for it, for the person to compare.
|
|
156
|
+
*/
|
|
157
|
+
async signIdentityLink(linkId, challenge) {
|
|
158
|
+
try {
|
|
159
|
+
const [privateKey, publicKey] = await Promise.all([keyManager_1.KeyManager.getPrivateKey(), keyManager_1.KeyManager.getPublicKey()]);
|
|
160
|
+
if (!privateKey || !publicKey) {
|
|
161
|
+
throw new Error('No identity on this device to link');
|
|
162
|
+
}
|
|
163
|
+
const root = publicKey.trim().toLowerCase();
|
|
164
|
+
const state = await this.getIdentityLink(linkId);
|
|
165
|
+
const proof = await (0, identityProof_1.signIdentityProof)({ privateKey, publicKey: root }, {
|
|
166
|
+
action: contracts_1.IDENTITY_PROOF_ACTIONS.link,
|
|
167
|
+
subject: state.userId,
|
|
168
|
+
actor: state.userId,
|
|
169
|
+
rootPublicKey: root,
|
|
170
|
+
payloadDigest: null,
|
|
171
|
+
expectedRevision: null,
|
|
172
|
+
audience: state.audience,
|
|
173
|
+
challenge,
|
|
174
|
+
expiresAt: state.expiresAt,
|
|
175
|
+
});
|
|
176
|
+
await this.makeRequest('POST', `/identity/link/${encodeURIComponent(linkId)}/proof`, { publicKey: root, proof }, { cache: false, skipAuth: true });
|
|
177
|
+
return { publicKey: root, code: (0, identityLink_1.deriveIdentityLinkCode)(linkId, root), username: state.username };
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
throw this.handleError(error);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/** WebAuthn request options over the account's passkeys, whose challenge is the link's. Opaque. */
|
|
184
|
+
async getIdentityLinkAssertionOptions(linkId, challenge) {
|
|
185
|
+
try {
|
|
186
|
+
return await this.makeRequest('POST', `/identity/link/${encodeURIComponent(linkId)}/options`, { challenge }, { cache: false });
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
throw this.handleError(error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* auth.oxy.so's half: the passkey assertion over the link's challenge. The
|
|
194
|
+
* account gains Commons' root and loses its recovery email.
|
|
195
|
+
*/
|
|
196
|
+
async completeIdentityLink(linkId, assertion) {
|
|
197
|
+
try {
|
|
198
|
+
const result = await this.makeRequest('POST', `/identity/link/${encodeURIComponent(linkId)}/complete`, { assertion }, { cache: false });
|
|
199
|
+
this._invalidateIdentityCaches(this.getCurrentUserId());
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
throw this.handleError(error);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** Withdraw a link request that has not completed. */
|
|
207
|
+
async cancelIdentityLink(linkId) {
|
|
208
|
+
try {
|
|
209
|
+
await this.makeRequest('DELETE', `/identity/link/${encodeURIComponent(linkId)}`, undefined, { cache: false });
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
throw this.handleError(error);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
92
215
|
/**
|
|
93
216
|
* Remove ONE passkey (WebAuthn credential) from the current account.
|
|
94
217
|
*
|
|
95
218
|
* Passkeys are per-credential, so this targets a specific credential id.
|
|
96
219
|
* The server refuses to remove the last remaining auth method (the account
|
|
97
220
|
* would become inaccessible) and deletes the stored `WebauthnCredential`.
|
|
98
|
-
* A passkey that also opens the account's root on the web takes its wrap with
|
|
99
|
-
* it, and the ONLY such passkey is refused with
|
|
100
|
-
* `code: 'IDENTITY_LAST_WEB_HOLDER'` (ADR 0024 D6).
|
|
101
221
|
*
|
|
102
222
|
* @param credentialId - The passkey's public credential id
|
|
103
223
|
* (`AuthMethodEntry.credentialId`).
|