@logto/connector-feishu-web 1.3.0 → 1.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/README.md +12 -13
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Set up social sign-in with Feishu
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Feishu Web: An advanced enterprise collaboration and management platform, offering seamless office collaboration and aligning team goals to fully activate organizations and individuals.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Getting started with Feishu social sign-in
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
The Feishu web connector is designed for desktop web applications and uses the OAuth 2.0 authentication process.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Register a Feishu developer account
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
If you do not have a Feishu developer account, please register on the [Feishu Open Platform](https://open.feishu.cn/app).
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Create an application
|
|
14
14
|
|
|
15
|
-
1. [
|
|
16
|
-
2.
|
|
17
|
-
3.
|
|
18
|
-
4.
|
|
19
|
-
5. 左侧「应用发布」中需要发布第一个版本「AppID」、「AppSecret」才会生效
|
|
15
|
+
1. On the [Developer Console](https://open.feishu.cn/app), click "Create Custom App".
|
|
16
|
+
2. Fill in the application name, description, select an icon, and click "Create" button.
|
|
17
|
+
3. In the left sidebar, click "Security Settings", fill in the "Redirect URL" as `${logto_endpoint}/callback/${connector_id}`. The corresponding value can be found in the `Callback URI` field on the Feishu connector details page in the Logto Console.
|
|
18
|
+
4. In "Credentials & Basic Info", you can obtain the "App ID" and "App Secret".
|
|
20
19
|
|
|
21
20
|
> ℹ️ **Note**
|
|
22
|
-
>
|
|
21
|
+
> For non-enterprise internal use, you also need to click "Create a version" button in "Version Management and Release" page. The "App ID" and "App Secret" will only take effect after the application status changes to "Enabled."
|
package/lib/index.js
CHANGED
|
@@ -219,11 +219,11 @@ var createFeishuConnector = async ({ getConfig }) => {
|
|
|
219
219
|
getUserInfo: getUserInfo(getConfig)
|
|
220
220
|
};
|
|
221
221
|
};
|
|
222
|
-
var
|
|
222
|
+
var index_default = createFeishuConnector;
|
|
223
223
|
export {
|
|
224
224
|
authorizationCallbackHandler,
|
|
225
225
|
buildAuthorizationUri,
|
|
226
|
-
|
|
226
|
+
index_default as default,
|
|
227
227
|
getAccessToken,
|
|
228
228
|
getAuthorizationUri,
|
|
229
229
|
getUserInfo
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constant.ts","../src/types.ts"],"sourcesContent":["import { assert, conditional } from '@silverhand/essentials';\nimport { got, HTTPError } from 'got';\n\nimport type {\n CreateConnector,\n GetAuthorizationUri,\n GetConnectorConfig,\n GetUserInfo,\n SocialConnector,\n} from '@logto/connector-kit';\nimport {\n ConnectorError,\n ConnectorErrorCodes,\n ConnectorPlatform,\n ConnectorType,\n jsonGuard,\n validateConfig,\n} from '@logto/connector-kit';\n\nimport {\n accessTokenEndpoint,\n codeEndpoint,\n defaultMetadata,\n userInfoEndpoint,\n} from './constant.js';\nimport {\n feishuAccessTokenResponse,\n feishuAuthCodeGuard,\n feishuConfigGuard,\n feishuErrorResponse,\n feishuUserInfoResponse,\n} from './types.js';\n\nexport function buildAuthorizationUri(\n clientId: string,\n redirectUri: string,\n state: string\n): string {\n const queryParameters = new URLSearchParams({\n client_id: clientId,\n redirect_uri: encodeURI(redirectUri),\n response_type: 'code',\n state,\n });\n\n return `${codeEndpoint}?${queryParameters.toString()}`;\n}\n\nexport function getAuthorizationUri(getConfig: GetConnectorConfig): GetAuthorizationUri {\n return async function ({ state, redirectUri }) {\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, feishuConfigGuard);\n\n const { appId } = config;\n\n return buildAuthorizationUri(appId, redirectUri, state);\n };\n}\n\nexport async function authorizationCallbackHandler(data: unknown) {\n const result = feishuAuthCodeGuard.safeParse(data);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(data))\n );\n\n return result.data;\n}\n\nexport async function getAccessToken(\n code: string,\n appId: string,\n appSecret: string,\n redirectUri: string\n) {\n try {\n const response = await got.post(accessTokenEndpoint, {\n headers: {\n contentType: 'application/www-form-urlencoded',\n },\n form: {\n grant_type: 'authorization_code',\n code,\n client_id: appId,\n client_secret: appSecret,\n redirect_uri: redirectUri,\n },\n responseType: 'json',\n });\n\n const result = feishuAccessTokenResponse.safeParse(response.body);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(response.body))\n );\n\n if (result.data.access_token.length === 0) {\n throw new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid, 'access_token is empty');\n }\n\n return { accessToken: result.data.access_token };\n } catch (error: unknown) {\n if (error instanceof ConnectorError) {\n throw error;\n }\n\n if (error instanceof HTTPError) {\n const result = feishuErrorResponse.safeParse(error.response.body);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(error.response.body))\n );\n\n throw new ConnectorError(\n ConnectorErrorCodes.SocialAuthCodeInvalid,\n result.data.error_description\n );\n }\n\n throw new ConnectorError(ConnectorErrorCodes.General, {\n errorDescription: 'Failed to get access token',\n });\n }\n}\n\nexport function getUserInfo(getConfig: GetConnectorConfig): GetUserInfo {\n return async function (data) {\n const { code, redirectUri } = await authorizationCallbackHandler(data);\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, feishuConfigGuard);\n\n const { accessToken } = await getAccessToken(code, config.appId, config.appSecret, redirectUri);\n\n try {\n const response = await got.get(userInfoEndpoint, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n responseType: 'json',\n });\n\n const result = feishuUserInfoResponse.safeParse(response.body);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, `invalid user response`)\n );\n\n const { sub, user_id, name, email, avatar_url: avatar, mobile } = result.data;\n\n return {\n id: sub,\n name,\n avatar,\n email: conditional(email),\n userId: conditional(user_id),\n phone: conditional(mobile?.replace('+', '')),\n rawData: jsonGuard.parse(response.body),\n };\n } catch (error: unknown) {\n if (error instanceof ConnectorError) {\n throw error;\n }\n\n if (error instanceof HTTPError) {\n const result = feishuErrorResponse.safeParse(error.response.body);\n\n assert(\n result.success,\n new ConnectorError(\n ConnectorErrorCodes.InvalidResponse,\n JSON.stringify(error.response.body)\n )\n );\n\n throw new ConnectorError(\n ConnectorErrorCodes.SocialAccessTokenInvalid,\n result.data.error_description\n );\n }\n\n throw new ConnectorError(ConnectorErrorCodes.General, {\n errorDescription: 'Failed to get user info',\n });\n }\n };\n}\n\nconst createFeishuConnector: CreateConnector<SocialConnector> = async ({ getConfig }) => {\n return {\n metadata: defaultMetadata,\n type: ConnectorType.Social,\n platform: ConnectorPlatform.Web,\n configGuard: feishuConfigGuard,\n getAuthorizationUri: getAuthorizationUri(getConfig),\n getUserInfo: getUserInfo(getConfig),\n };\n};\n\nexport default createFeishuConnector;\n","import type { ConnectorMetadata } from '@logto/connector-kit';\nimport { ConnectorConfigFormItemType, ConnectorPlatform } from '@logto/connector-kit';\n\nexport const codeEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/authorize';\n\nexport const accessTokenEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/token';\n\nexport const userInfoEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/userinfo';\n\nexport const defaultMetadata: ConnectorMetadata = {\n id: 'feishu-web',\n target: 'feishu',\n platform: ConnectorPlatform.Web,\n name: {\n en: 'Feishu',\n 'zh-CN': '飞书',\n },\n logo: './logo.svg',\n logoDark: null,\n description: {\n 'zh-CN': '飞书是一个由字节跳动开发的企业协作与管理平台',\n en: 'Feishu is an enterprise collaboration platform developed by ByteDance',\n },\n readme: './README.md',\n formItems: [\n {\n key: 'appId',\n label: 'App ID',\n type: ConnectorConfigFormItemType.Text,\n required: true,\n placeholder: '<app-id>',\n },\n {\n key: 'appSecret',\n label: 'App Secret',\n type: ConnectorConfigFormItemType.Text,\n required: true,\n placeholder: '<app-secret>',\n },\n ],\n};\n","import { z } from 'zod';\n\nexport const feishuConfigGuard = z.object({\n appId: z.string(),\n appSecret: z.string(),\n});\n\nexport type FeishuConfig = z.infer<typeof feishuConfigGuard>;\n\nexport const feishuAuthCodeGuard = z.object({\n code: z.string(),\n redirectUri: z.string(),\n});\n\nexport const feishuErrorResponse = z.object({\n error: z.string(),\n error_description: z.string().optional(),\n});\n\nexport const feishuAccessTokenResponse = z.object({\n access_token: z.string(),\n token_type: z.string(),\n expires_in: z.number(),\n refresh_token: z.string().optional(),\n refresh_expires_in: z.number().optional(),\n});\n\nexport const feishuUserInfoResponse = z.object({\n sub: z.string(),\n name: z.string(),\n picture: z.string(),\n open_id: z.string(),\n union_id: z.string(),\n en_name: z.string(),\n tenant_key: z.string(),\n avatar_url: z.string(),\n avatar_thumb: z.string(),\n avatar_middle: z.string(),\n avatar_big: z.string(),\n email: z.string().nullish(),\n user_id: z.string().nullish(),\n employee_no: z.string().nullish(),\n mobile: z.string().nullish(),\n});\n"],"mappings":"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","names":["ConnectorPlatform","ConnectorPlatform"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constant.ts","../src/types.ts"],"sourcesContent":["import { assert, conditional } from '@silverhand/essentials';\nimport { got, HTTPError } from 'got';\n\nimport type {\n CreateConnector,\n GetAuthorizationUri,\n GetConnectorConfig,\n GetUserInfo,\n SocialConnector,\n} from '@logto/connector-kit';\nimport {\n ConnectorError,\n ConnectorErrorCodes,\n ConnectorPlatform,\n ConnectorType,\n jsonGuard,\n validateConfig,\n} from '@logto/connector-kit';\n\nimport {\n accessTokenEndpoint,\n codeEndpoint,\n defaultMetadata,\n userInfoEndpoint,\n} from './constant.js';\nimport {\n feishuAccessTokenResponse,\n feishuAuthCodeGuard,\n feishuConfigGuard,\n feishuErrorResponse,\n feishuUserInfoResponse,\n} from './types.js';\n\nexport function buildAuthorizationUri(\n clientId: string,\n redirectUri: string,\n state: string\n): string {\n const queryParameters = new URLSearchParams({\n client_id: clientId,\n redirect_uri: encodeURI(redirectUri),\n response_type: 'code',\n state,\n });\n\n return `${codeEndpoint}?${queryParameters.toString()}`;\n}\n\nexport function getAuthorizationUri(getConfig: GetConnectorConfig): GetAuthorizationUri {\n return async function ({ state, redirectUri }) {\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, feishuConfigGuard);\n\n const { appId } = config;\n\n return buildAuthorizationUri(appId, redirectUri, state);\n };\n}\n\nexport async function authorizationCallbackHandler(data: unknown) {\n const result = feishuAuthCodeGuard.safeParse(data);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(data))\n );\n\n return result.data;\n}\n\nexport async function getAccessToken(\n code: string,\n appId: string,\n appSecret: string,\n redirectUri: string\n) {\n try {\n const response = await got.post(accessTokenEndpoint, {\n headers: {\n contentType: 'application/www-form-urlencoded',\n },\n form: {\n grant_type: 'authorization_code',\n code,\n client_id: appId,\n client_secret: appSecret,\n redirect_uri: redirectUri,\n },\n responseType: 'json',\n });\n\n const result = feishuAccessTokenResponse.safeParse(response.body);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(response.body))\n );\n\n if (result.data.access_token.length === 0) {\n throw new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid, 'access_token is empty');\n }\n\n return { accessToken: result.data.access_token };\n } catch (error: unknown) {\n if (error instanceof ConnectorError) {\n throw error;\n }\n\n if (error instanceof HTTPError) {\n const result = feishuErrorResponse.safeParse(error.response.body);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, JSON.stringify(error.response.body))\n );\n\n throw new ConnectorError(\n ConnectorErrorCodes.SocialAuthCodeInvalid,\n result.data.error_description\n );\n }\n\n throw new ConnectorError(ConnectorErrorCodes.General, {\n errorDescription: 'Failed to get access token',\n });\n }\n}\n\nexport function getUserInfo(getConfig: GetConnectorConfig): GetUserInfo {\n return async function (data) {\n const { code, redirectUri } = await authorizationCallbackHandler(data);\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, feishuConfigGuard);\n\n const { accessToken } = await getAccessToken(code, config.appId, config.appSecret, redirectUri);\n\n try {\n const response = await got.get(userInfoEndpoint, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n responseType: 'json',\n });\n\n const result = feishuUserInfoResponse.safeParse(response.body);\n assert(\n result.success,\n new ConnectorError(ConnectorErrorCodes.InvalidResponse, `invalid user response`)\n );\n\n const { sub, user_id, name, email, avatar_url: avatar, mobile } = result.data;\n\n return {\n id: sub,\n name,\n avatar,\n email: conditional(email),\n userId: conditional(user_id),\n phone: conditional(mobile?.replace('+', '')),\n rawData: jsonGuard.parse(response.body),\n };\n } catch (error: unknown) {\n if (error instanceof ConnectorError) {\n throw error;\n }\n\n if (error instanceof HTTPError) {\n const result = feishuErrorResponse.safeParse(error.response.body);\n\n assert(\n result.success,\n new ConnectorError(\n ConnectorErrorCodes.InvalidResponse,\n JSON.stringify(error.response.body)\n )\n );\n\n throw new ConnectorError(\n ConnectorErrorCodes.SocialAccessTokenInvalid,\n result.data.error_description\n );\n }\n\n throw new ConnectorError(ConnectorErrorCodes.General, {\n errorDescription: 'Failed to get user info',\n });\n }\n };\n}\n\nconst createFeishuConnector: CreateConnector<SocialConnector> = async ({ getConfig }) => {\n return {\n metadata: defaultMetadata,\n type: ConnectorType.Social,\n platform: ConnectorPlatform.Web,\n configGuard: feishuConfigGuard,\n getAuthorizationUri: getAuthorizationUri(getConfig),\n getUserInfo: getUserInfo(getConfig),\n };\n};\n\nexport default createFeishuConnector;\n","import type { ConnectorMetadata } from '@logto/connector-kit';\nimport { ConnectorConfigFormItemType, ConnectorPlatform } from '@logto/connector-kit';\n\nexport const codeEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/authorize';\n\nexport const accessTokenEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/token';\n\nexport const userInfoEndpoint = 'https://passport.feishu.cn/suite/passport/oauth/userinfo';\n\nexport const defaultMetadata: ConnectorMetadata = {\n id: 'feishu-web',\n target: 'feishu',\n platform: ConnectorPlatform.Web,\n name: {\n en: 'Feishu',\n 'zh-CN': '飞书',\n },\n logo: './logo.svg',\n logoDark: null,\n description: {\n 'zh-CN': '飞书是一个由字节跳动开发的企业协作与管理平台',\n en: 'Feishu is an enterprise collaboration platform developed by ByteDance',\n },\n readme: './README.md',\n formItems: [\n {\n key: 'appId',\n label: 'App ID',\n type: ConnectorConfigFormItemType.Text,\n required: true,\n placeholder: '<app-id>',\n },\n {\n key: 'appSecret',\n label: 'App Secret',\n type: ConnectorConfigFormItemType.Text,\n required: true,\n placeholder: '<app-secret>',\n },\n ],\n};\n","import { z } from 'zod';\n\nexport const feishuConfigGuard = z.object({\n appId: z.string(),\n appSecret: z.string(),\n});\n\nexport type FeishuConfig = z.infer<typeof feishuConfigGuard>;\n\nexport const feishuAuthCodeGuard = z.object({\n code: z.string(),\n redirectUri: z.string(),\n});\n\nexport const feishuErrorResponse = z.object({\n error: z.string(),\n error_description: z.string().optional(),\n});\n\nexport const feishuAccessTokenResponse = z.object({\n access_token: z.string(),\n token_type: z.string(),\n expires_in: z.number(),\n refresh_token: z.string().optional(),\n refresh_expires_in: z.number().optional(),\n});\n\nexport const feishuUserInfoResponse = z.object({\n sub: z.string(),\n name: z.string(),\n picture: z.string(),\n open_id: z.string(),\n union_id: z.string(),\n en_name: z.string(),\n tenant_key: z.string(),\n avatar_url: z.string(),\n avatar_thumb: z.string(),\n avatar_middle: z.string(),\n avatar_big: z.string(),\n email: z.string().nullish(),\n user_id: z.string().nullish(),\n employee_no: z.string().nullish(),\n mobile: z.string().nullish(),\n});\n"],"mappings":"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","names":["ConnectorPlatform","ConnectorPlatform"]}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/connector-feishu-web",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Feishu web connector.",
|
|
5
5
|
"author": "Silverhand Inc. <contact@silverhand.io>",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@logto/connector-kit": "^4.
|
|
7
|
+
"@logto/connector-kit": "^4.3.0",
|
|
8
8
|
"@silverhand/essentials": "^2.9.1",
|
|
9
9
|
"got": "^14.0.0",
|
|
10
10
|
"snakecase-keys": "^8.0.1",
|
|
11
|
-
"zod": "^3.
|
|
11
|
+
"zod": "^3.24.2"
|
|
12
12
|
},
|
|
13
13
|
"main": "./lib/index.js",
|
|
14
14
|
"module": "./lib/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"logo-dark.svg"
|
|
23
23
|
],
|
|
24
24
|
"engines": {
|
|
25
|
-
"node": "^
|
|
25
|
+
"node": "^22.14.0"
|
|
26
26
|
},
|
|
27
27
|
"eslintConfig": {
|
|
28
28
|
"extends": "@silverhand",
|
|
@@ -43,17 +43,17 @@
|
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@silverhand/eslint-config": "6.0.1",
|
|
45
45
|
"@silverhand/ts-config": "6.0.0",
|
|
46
|
-
"@types/node": "^
|
|
46
|
+
"@types/node": "^22.14.0",
|
|
47
47
|
"@types/supertest": "^6.0.2",
|
|
48
|
-
"@vitest/coverage-v8": "^
|
|
48
|
+
"@vitest/coverage-v8": "^3.1.1",
|
|
49
49
|
"eslint": "^8.56.0",
|
|
50
50
|
"lint-staged": "^15.0.2",
|
|
51
|
-
"nock": "^
|
|
52
|
-
"prettier": "^3.
|
|
51
|
+
"nock": "^14.0.3",
|
|
52
|
+
"prettier": "^3.5.3",
|
|
53
53
|
"supertest": "^7.0.0",
|
|
54
|
-
"tsup": "^8.
|
|
54
|
+
"tsup": "^8.3.0",
|
|
55
55
|
"typescript": "^5.5.3",
|
|
56
|
-
"vitest": "^
|
|
56
|
+
"vitest": "^3.1.1"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"precommit": "lint-staged",
|