@logto/core-kit 2.3.0 → 2.4.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/lib/openid.d.ts +9 -1
- package/lib/openid.js +30 -1
- package/lib/password-policy.js +4 -0
- package/lib/regex.d.ts +1 -1
- package/lib/regex.js +2 -2
- package/lib/utils/integration-test.d.ts +2 -0
- package/lib/utils/integration-test.js +8 -0
- package/lib/utils/url.d.ts +4 -0
- package/lib/utils/url.js +7 -0
- package/package.json +8 -12
- package/scss/_console-themes.scss +36 -0
- package/scss/_fonts.scss +1 -0
package/lib/openid.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare enum ReservedResource {
|
|
|
12
12
|
*/
|
|
13
13
|
Organization = "urn:logto:resource:organizations"
|
|
14
14
|
}
|
|
15
|
-
export type UserClaim = 'name' | 'picture' | '
|
|
15
|
+
export type UserClaim = 'name' | 'given_name' | 'family_name' | 'middle_name' | 'nickname' | 'preferred_username' | 'profile' | 'picture' | 'website' | 'email' | 'email_verified' | 'gender' | 'birthdate' | 'zoneinfo' | 'locale' | 'phone_number' | 'phone_number_verified' | 'address' | 'updated_at' | 'username' | 'roles' | 'organizations' | 'organization_data' | 'organization_roles' | 'custom_data' | 'identities' | 'created_at';
|
|
16
16
|
/**
|
|
17
17
|
* Scopes for ID Token and Userinfo Endpoint.
|
|
18
18
|
*/
|
|
@@ -35,6 +35,12 @@ export declare enum UserScope {
|
|
|
35
35
|
* See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
|
|
36
36
|
*/
|
|
37
37
|
Phone = "phone",
|
|
38
|
+
/**
|
|
39
|
+
* Scope for user address.
|
|
40
|
+
*
|
|
41
|
+
* See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
|
|
42
|
+
*/
|
|
43
|
+
Address = "address",
|
|
38
44
|
/**
|
|
39
45
|
* Scope for user's custom data.
|
|
40
46
|
*
|
|
@@ -68,6 +74,8 @@ export declare enum UserScope {
|
|
|
68
74
|
}
|
|
69
75
|
/**
|
|
70
76
|
* Mapped claims that ID Token includes.
|
|
77
|
+
*
|
|
78
|
+
* @see {@link https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims | OpenID Connect Core 1.0} for standard scope - claim mapping.
|
|
71
79
|
*/
|
|
72
80
|
export declare const idTokenClaims: Readonly<Record<UserScope, UserClaim[]>>;
|
|
73
81
|
/**
|
package/lib/openid.js
CHANGED
|
@@ -37,6 +37,12 @@ export var UserScope;
|
|
|
37
37
|
* See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
|
|
38
38
|
*/
|
|
39
39
|
UserScope["Phone"] = "phone";
|
|
40
|
+
/**
|
|
41
|
+
* Scope for user address.
|
|
42
|
+
*
|
|
43
|
+
* See {@link idTokenClaims} for mapped claims in ID Token and {@link userinfoClaims} for additional claims in Userinfo Endpoint.
|
|
44
|
+
*/
|
|
45
|
+
UserScope["Address"] = "address";
|
|
40
46
|
/**
|
|
41
47
|
* Scope for user's custom data.
|
|
42
48
|
*
|
|
@@ -70,11 +76,33 @@ export var UserScope;
|
|
|
70
76
|
})(UserScope || (UserScope = {}));
|
|
71
77
|
/**
|
|
72
78
|
* Mapped claims that ID Token includes.
|
|
79
|
+
*
|
|
80
|
+
* @see {@link https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims | OpenID Connect Core 1.0} for standard scope - claim mapping.
|
|
73
81
|
*/
|
|
74
82
|
export const idTokenClaims = Object.freeze({
|
|
75
|
-
[UserScope.Profile]: [
|
|
83
|
+
[UserScope.Profile]: [
|
|
84
|
+
// Standard claims
|
|
85
|
+
'name',
|
|
86
|
+
'family_name',
|
|
87
|
+
'given_name',
|
|
88
|
+
'middle_name',
|
|
89
|
+
'nickname',
|
|
90
|
+
'preferred_username',
|
|
91
|
+
'profile',
|
|
92
|
+
'picture',
|
|
93
|
+
'website',
|
|
94
|
+
'gender',
|
|
95
|
+
'birthdate',
|
|
96
|
+
'zoneinfo',
|
|
97
|
+
'locale',
|
|
98
|
+
'updated_at',
|
|
99
|
+
// Custom claims
|
|
100
|
+
'username',
|
|
101
|
+
'created_at',
|
|
102
|
+
],
|
|
76
103
|
[UserScope.Email]: ['email', 'email_verified'],
|
|
77
104
|
[UserScope.Phone]: ['phone_number', 'phone_number_verified'],
|
|
105
|
+
[UserScope.Address]: ['address'],
|
|
78
106
|
[UserScope.Roles]: ['roles'],
|
|
79
107
|
[UserScope.Organizations]: ['organizations'],
|
|
80
108
|
[UserScope.OrganizationRoles]: ['organization_roles'],
|
|
@@ -88,6 +116,7 @@ export const userinfoClaims = Object.freeze({
|
|
|
88
116
|
[UserScope.Profile]: [],
|
|
89
117
|
[UserScope.Email]: [],
|
|
90
118
|
[UserScope.Phone]: [],
|
|
119
|
+
[UserScope.Address]: [],
|
|
91
120
|
[UserScope.Roles]: [],
|
|
92
121
|
[UserScope.Organizations]: ['organization_data'],
|
|
93
122
|
[UserScope.OrganizationRoles]: [],
|
package/lib/password-policy.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { getPwnPasswordsForTest, isIntegrationTest } from './utils/integration-test.js';
|
|
2
3
|
/** Password policy configuration guard. */
|
|
3
4
|
export const passwordPolicyGuard = z.object({
|
|
4
5
|
length: z
|
|
@@ -213,6 +214,9 @@ export class PasswordPolicyChecker {
|
|
|
213
214
|
* @returns Whether the password has been pwned.
|
|
214
215
|
*/
|
|
215
216
|
async hasBeenPwned(password) {
|
|
217
|
+
if (isIntegrationTest()) {
|
|
218
|
+
return getPwnPasswordsForTest().includes(password);
|
|
219
|
+
}
|
|
216
220
|
const hash = await this.subtle.digest('SHA-1', new TextEncoder().encode(password));
|
|
217
221
|
const hashHex = Array.from(new Uint8Array(hash))
|
|
218
222
|
.map((binary) => binary.toString(16).padStart(2, '0'))
|
package/lib/regex.d.ts
CHANGED
|
@@ -7,5 +7,5 @@ export declare const mobileUriSchemeProtocolRegEx: RegExp;
|
|
|
7
7
|
export declare const hexColorRegEx: RegExp;
|
|
8
8
|
export declare const dateRegex: RegExp;
|
|
9
9
|
export declare const noSpaceRegEx: RegExp;
|
|
10
|
-
/** Full domain that consists of at least 3 parts, e.g. foo.bar.com */
|
|
10
|
+
/** Full domain that consists of at least 3 parts, e.g. foo.bar.com or example-foo.bar.com */
|
|
11
11
|
export declare const domainRegEx: RegExp;
|
package/lib/regex.js
CHANGED
|
@@ -7,5 +7,5 @@ export const mobileUriSchemeProtocolRegEx = /^[a-z][\d+_a-z-]*(\.[\d+_a-z-]+)+:$
|
|
|
7
7
|
export const hexColorRegEx = /^#[\da-f]{3}([\da-f]{3})?$/i;
|
|
8
8
|
export const dateRegex = /^\d{4}(-\d{2}){2}/;
|
|
9
9
|
export const noSpaceRegEx = /^\S+$/;
|
|
10
|
-
/** Full domain that consists of at least 3 parts, e.g. foo.bar.com */
|
|
11
|
-
export const domainRegEx = /^[\dA-Za-z]
|
|
10
|
+
/** Full domain that consists of at least 3 parts, e.g. foo.bar.com or example-foo.bar.com */
|
|
11
|
+
export const domainRegEx = /^[\dA-Za-z](?:[\dA-Za-z-]*[\dA-Za-z])?(?:\.[\dA-Za-z](?:[\dA-Za-z-]*[\dA-Za-z])?){2,}$/;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { yes } from '@silverhand/essentials';
|
|
2
|
+
export const isIntegrationTest = () => yes(process.env.INTEGRATION_TEST);
|
|
3
|
+
export const getPwnPasswordsForTest = () => {
|
|
4
|
+
if (!isIntegrationTest()) {
|
|
5
|
+
throw new Error('This function should only be called in integration tests');
|
|
6
|
+
}
|
|
7
|
+
return Object.freeze(['123456aA', 'test_password']);
|
|
8
|
+
};
|
package/lib/utils/url.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export declare const validateRedirectUrl: (url: string, type: 'web' | 'mobile') => boolean;
|
|
2
2
|
export declare const validateUriOrigin: (url: string) => boolean;
|
|
3
3
|
export declare const isValidUrl: (url?: string) => boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Check if the given URL is localhost
|
|
6
|
+
*/
|
|
7
|
+
export declare const isLocalhost: (url: string) => boolean;
|
package/lib/utils/url.js
CHANGED
|
@@ -25,3 +25,10 @@ export const isValidUrl = (url) => {
|
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Check if the given URL is localhost
|
|
30
|
+
*/
|
|
31
|
+
export const isLocalhost = (url) => {
|
|
32
|
+
const parsedUrl = new URL(url);
|
|
33
|
+
return ['localhost', '127.0.0.1', '::1'].includes(parsedUrl.hostname);
|
|
34
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/core-kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"author": "Silverhand Inc. <contact@silverhand.io>",
|
|
5
5
|
"homepage": "https://github.com/logto-io/toolkit#readme",
|
|
6
6
|
"repository": {
|
|
@@ -31,29 +31,27 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@logto/language-kit": "^1.1.0",
|
|
33
33
|
"@logto/shared": "^3.1.0",
|
|
34
|
+
"@silverhand/essentials": "^2.9.0",
|
|
34
35
|
"color": "^4.2.3"
|
|
35
36
|
},
|
|
36
37
|
"optionalDependencies": {
|
|
37
38
|
"zod": "^3.22.4"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
|
-
"@jest/types": "^29.0.3",
|
|
41
41
|
"@silverhand/eslint-config": "5.0.0",
|
|
42
|
-
"@silverhand/essentials": "^2.9.0",
|
|
43
42
|
"@silverhand/ts-config": "5.0.0",
|
|
44
43
|
"@silverhand/ts-config-react": "5.0.0",
|
|
45
44
|
"@types/color": "^3.0.3",
|
|
46
|
-
"@types/jest": "^29.4.0",
|
|
47
45
|
"@types/node": "^20.9.5",
|
|
48
46
|
"@types/react": "^18.0.31",
|
|
47
|
+
"@vitest/coverage-v8": "^1.4.0",
|
|
49
48
|
"eslint": "^8.44.0",
|
|
50
|
-
"jest": "^29.7.0",
|
|
51
49
|
"lint-staged": "^15.0.0",
|
|
52
50
|
"postcss": "^8.4.31",
|
|
53
51
|
"prettier": "^3.0.0",
|
|
54
52
|
"stylelint": "^15.0.0",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
53
|
+
"typescript": "^5.3.3",
|
|
54
|
+
"vitest": "^1.4.0"
|
|
57
55
|
},
|
|
58
56
|
"eslintConfig": {
|
|
59
57
|
"extends": "@silverhand"
|
|
@@ -69,13 +67,11 @@
|
|
|
69
67
|
"precommit": "lint-staged",
|
|
70
68
|
"dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput --incremental",
|
|
71
69
|
"build": "rm -rf lib/ && tsc -p tsconfig.build.json",
|
|
72
|
-
"build:test": "pnpm build
|
|
70
|
+
"build:test": "pnpm build",
|
|
73
71
|
"lint": "eslint --ext .ts src",
|
|
74
72
|
"lint:report": "pnpm lint --format json --output-file report.json",
|
|
75
73
|
"stylelint": "stylelint \"scss/**/*.scss\"",
|
|
76
|
-
"test
|
|
77
|
-
"test": "pnpm
|
|
78
|
-
"test:ci": "pnpm test:only",
|
|
79
|
-
"test:coverage": "pnpm test:only --silent --coverage"
|
|
74
|
+
"test": "vitest src",
|
|
75
|
+
"test:ci": "pnpm run test --silent --coverage"
|
|
80
76
|
}
|
|
81
77
|
}
|
|
@@ -162,7 +162,17 @@
|
|
|
162
162
|
--color-specific-icon-bg: #f3effa;
|
|
163
163
|
--color-specific-toggle-off-enable: var(--color-neutral-90);
|
|
164
164
|
--color-specific-unselected-disabled: var(--color-hover); // 8% Neutral-10
|
|
165
|
+
--color-specific-selected-disabled: var(--color-primary-80);
|
|
166
|
+
--color-specific-focused-inside: var(--color-primary-30);
|
|
167
|
+
--color-specific-focused-outside: var(--color-primary-40);
|
|
168
|
+
--color-specific-button-icon: rgba(255, 255, 255, 70%); // 70% static white
|
|
169
|
+
--color-overlay-primary-hover: rgba(93, 52, 242, 8%); // 8% Primary-40
|
|
170
|
+
--color-overlay-primary-pressed: rgba(93, 52, 242, 12%); // 12% Primary-40
|
|
165
171
|
--color-function-n-overlay-primary-focused: rgba(93, 52, 242, 16%); // 16% Primary-40
|
|
172
|
+
--color-overlay-danger-hover: rgba(186, 27, 27, 8%); // 8% Error-40
|
|
173
|
+
--color-overlay-dark-bg-hover: rgba(255, 255, 255, 12%); // 12% static white
|
|
174
|
+
--color-overlay-dark-bg-pressed: rgba(255, 255, 255, 18%); // 18% static white
|
|
175
|
+
--color-overlay-dark-bg-focused: rgba(255, 255, 255, 24%); // 24% static white
|
|
166
176
|
|
|
167
177
|
// Shadows
|
|
168
178
|
--shadow-1: 0 4px 8px rgba(0, 0, 0, 8%);
|
|
@@ -201,6 +211,15 @@
|
|
|
201
211
|
--color-bg-state-unselected: var(--color-neutral-90);
|
|
202
212
|
--color-bg-state-disabled: rgba(25, 28, 29, 8%); // 8% --color-neutral-10
|
|
203
213
|
--color-bg-info-tag: rgba(229, 225, 236, 80%); // 80% --color-neutral-variant-90
|
|
214
|
+
|
|
215
|
+
// code editor
|
|
216
|
+
--color-code-bg: #181133;
|
|
217
|
+
--color-code-bg-float: #30314e;
|
|
218
|
+
--color-code-white: #f7f8f8;
|
|
219
|
+
--color-code-grey: #adaab4;
|
|
220
|
+
--color-code-dark-bg-focused: rgba(255, 255, 255, 24%);
|
|
221
|
+
--color-code-dark-bg-hover: rgba(255, 255, 255, 12%);
|
|
222
|
+
--color-code-dark-bg-pressed: rgba(255, 255, 255, 18%);
|
|
204
223
|
}
|
|
205
224
|
|
|
206
225
|
@mixin dark {
|
|
@@ -367,7 +386,17 @@
|
|
|
367
386
|
--color-specific-icon-bg: rgba(247, 248, 248, 12%);
|
|
368
387
|
--color-specific-toggle-off-enable: var(--color-neutral-90);
|
|
369
388
|
--color-specific-unselected-disabled: var(--color-hover); // 8% Neutral-10
|
|
389
|
+
--color-specific-selected-disabled: var(--color-primary-80);
|
|
390
|
+
--color-specific-focused-inside: var(--color-primary-40);
|
|
391
|
+
--color-specific-focused-outside: rgba(#cabeff, 32%); // 32% Primary-40
|
|
392
|
+
--color-specific-button-icon: rgba(255, 255, 255, 60%); // 60% static white
|
|
393
|
+
--color-overlay-primary-hover: rgba(202, 190, 255, 8%); // 8% Primary-40
|
|
394
|
+
--color-overlay-primary-pressed: rgba(202, 190, 255, 12%); // 12% Primary-40
|
|
370
395
|
--color-function-n-overlay-primary-focused: rgba(202, 190, 255, 16%); // 16% Primary-40
|
|
396
|
+
--color-overlay-danger-hover: rgba(186, 27, 27, 8%); // 8% Error-40
|
|
397
|
+
--color-overlay-dark-bg-hover: rgba(255, 255, 255, 12%); // 12% static white
|
|
398
|
+
--color-overlay-dark-bg-pressed: rgba(255, 255, 255, 18%); // 18% static white
|
|
399
|
+
--color-overlay-dark-bg-focused: rgba(255, 255, 255, 24%); // 24% static white
|
|
371
400
|
|
|
372
401
|
// Shadows
|
|
373
402
|
--shadow-1: 0 4px 8px rgba(0, 0, 0, 8%);
|
|
@@ -409,4 +438,11 @@
|
|
|
409
438
|
--color-bg-state-unselected: var(--color-neutral-90);
|
|
410
439
|
--color-bg-state-disabled: rgba(247, 248, 248, 8%); // 8% --color-neutral-10
|
|
411
440
|
--color-bg-info-tag: var(--color-neutral-variant-90);
|
|
441
|
+
|
|
442
|
+
// code editor
|
|
443
|
+
--color-code-bg: #090613;
|
|
444
|
+
--color-code-bg-float: #232439;
|
|
445
|
+
--color-code-white: #f7f8f8;
|
|
446
|
+
--color-code-grey: #adaab4;
|
|
447
|
+
--color-code-dark-bg-focused: rgba(255, 255, 255, 24%);
|
|
412
448
|
}
|
package/scss/_fonts.scss
CHANGED