@logto/connector-google 1.6.0 → 1.6.1

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/index.js CHANGED
@@ -215,9 +215,9 @@ var createGoogleConnector = async ({ getConfig }) => {
215
215
  getUserInfo: getUserInfo(getConfig)
216
216
  };
217
217
  };
218
- var src_default = createGoogleConnector;
218
+ var index_default = createGoogleConnector;
219
219
  export {
220
- src_default as default,
220
+ index_default as default,
221
221
  getAccessToken
222
222
  };
223
223
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constant.ts","../src/types.ts"],"sourcesContent":["/**\n * The Implementation of OpenID Connect of Google Identity Platform.\n * https://developers.google.com/identity/protocols/oauth2/openid-connect\n */\nimport { conditional, assert } from '@silverhand/essentials';\nimport { got, HTTPError } from 'got';\n\nimport type {\n GetAuthorizationUri,\n GetUserInfo,\n GetConnectorConfig,\n CreateConnector,\n SocialConnector,\n GoogleConnectorConfig,\n} from '@logto/connector-kit';\nimport {\n ConnectorError,\n ConnectorErrorCodes,\n validateConfig,\n ConnectorType,\n parseJson,\n GoogleConnector,\n} from '@logto/connector-kit';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\n\nimport {\n accessTokenEndpoint,\n authorizationEndpoint,\n scope as defaultScope,\n userInfoEndpoint,\n defaultMetadata,\n defaultTimeout,\n jwksUri,\n} from './constant.js';\nimport {\n accessTokenResponseGuard,\n userInfoResponseGuard,\n authResponseGuard,\n googleOneTapDataGuard,\n} from './types.js';\n\nconst getAuthorizationUri =\n (getConfig: GetConnectorConfig): GetAuthorizationUri =>\n async ({ state, redirectUri }) => {\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, GoogleConnector.configGuard);\n\n const { clientId, scope, prompts } = config;\n\n const queryParameters = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n state,\n scope: scope ?? defaultScope,\n ...conditional(prompts && prompts.length > 0 && { prompt: prompts.join(' ') }),\n });\n\n return `${authorizationEndpoint}?${queryParameters.toString()}`;\n };\n\nexport const getAccessToken = async (\n config: GoogleConnectorConfig,\n codeObject: { code: string; redirectUri: string }\n) => {\n const { code, redirectUri } = codeObject;\n const { clientId, clientSecret } = config;\n\n // Note:Need to decodeURIComponent on code\n // https://stackoverflow.com/questions/51058256/google-api-node-js-invalid-grant-malformed-auth-code\n const httpResponse = await got.post(accessTokenEndpoint, {\n form: {\n code: decodeURIComponent(code),\n client_id: clientId,\n client_secret: clientSecret,\n redirect_uri: redirectUri,\n grant_type: 'authorization_code',\n },\n timeout: { request: defaultTimeout },\n });\n\n const result = accessTokenResponseGuard.safeParse(parseJson(httpResponse.body));\n\n if (!result.success) {\n throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);\n }\n\n const { access_token: accessToken } = result.data;\n\n assert(accessToken, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid));\n\n return { accessToken };\n};\n\ntype Json = ReturnType<typeof parseJson>;\n\n/**\n * Get user information JSON from Google Identity Platform. It will use the following order to\n * retrieve user information:\n *\n * 1. Google One Tap: https://developers.google.com/identity/gsi/web/guides/verify-google-id-token\n * 2. Normal Google OAuth: https://developers.google.com/identity/protocols/oauth2/openid-connect\n *\n * @param data The data from the client.\n * @param config The configuration of the connector.\n * @returns A Promise that resolves to the user information JSON.\n */\nconst getUserInfoJson = async (data: unknown, config: GoogleConnectorConfig): Promise<Json> => {\n // Google One Tap\n const oneTapResult = googleOneTapDataGuard.safeParse(data);\n\n if (oneTapResult.success) {\n const { payload } = await jwtVerify<Json>(\n oneTapResult.data.credential,\n createRemoteJWKSet(new URL(jwksUri)),\n {\n // https://developers.google.com/identity/gsi/web/guides/verify-google-id-token\n issuer: ['https://accounts.google.com', 'accounts.google.com'],\n audience: config.clientId,\n clockTolerance: 10,\n }\n );\n return payload;\n }\n\n // Normal Google OAuth\n const { code, redirectUri } = await authorizationCallbackHandler(data);\n const { accessToken } = await getAccessToken(config, { code, redirectUri });\n\n const httpResponse = await got.post(userInfoEndpoint, {\n headers: {\n authorization: `Bearer ${accessToken}`,\n },\n timeout: { request: defaultTimeout },\n });\n return parseJson(httpResponse.body);\n};\n\nconst getUserInfo =\n (getConfig: GetConnectorConfig): GetUserInfo =>\n async (data) => {\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, GoogleConnector.configGuard);\n\n try {\n const rawData = await getUserInfoJson(data, config);\n const result = userInfoResponseGuard.safeParse(rawData);\n\n if (!result.success) {\n throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);\n }\n\n const { sub: id, picture: avatar, email, email_verified, name } = result.data;\n\n return {\n id,\n avatar,\n email: conditional(email_verified && email),\n name,\n rawData,\n };\n } catch (error: unknown) {\n return getUserInfoErrorHandler(error);\n }\n };\n\nconst authorizationCallbackHandler = async (parameterObject: unknown) => {\n const result = authResponseGuard.safeParse(parameterObject);\n\n if (!result.success) {\n throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));\n }\n\n return result.data;\n};\n\nconst getUserInfoErrorHandler = (error: unknown) => {\n if (error instanceof HTTPError) {\n const { statusCode, body: rawBody } = error.response;\n\n if (statusCode === 401) {\n throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid);\n }\n\n throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(rawBody));\n }\n\n throw error;\n};\n\nconst createGoogleConnector: CreateConnector<SocialConnector> = async ({ getConfig }) => {\n return {\n metadata: defaultMetadata,\n type: ConnectorType.Social,\n configGuard: GoogleConnector.configGuard,\n getAuthorizationUri: getAuthorizationUri(getConfig),\n getUserInfo: getUserInfo(getConfig),\n };\n};\n\nexport default createGoogleConnector;\n","import type { ConnectorMetadata } from '@logto/connector-kit';\nimport {\n ConnectorConfigFormItemType,\n ConnectorPlatform,\n GoogleConnector,\n OidcPrompt,\n} from '@logto/connector-kit';\n\nexport const authorizationEndpoint = 'https://accounts.google.com/o/oauth2/v2/auth';\nexport const accessTokenEndpoint = 'https://oauth2.googleapis.com/token';\nexport const userInfoEndpoint = 'https://openidconnect.googleapis.com/v1/userinfo';\nexport const scope = 'openid profile email';\n\n// Instead of defining the metadata in the connector, we reuse the metadata from the connector-kit.\n// This is not the normal practice, but Google One Tap is a special case.\n// @see {@link GoogleConnector} for more information.\nexport const defaultMetadata: ConnectorMetadata = {\n id: GoogleConnector.factoryId,\n target: GoogleConnector.target,\n platform: ConnectorPlatform.Universal,\n name: {\n en: 'Google',\n 'zh-CN': 'Google',\n 'tr-TR': 'Google',\n ko: 'Google',\n },\n logo: './logo.svg',\n logoDark: null,\n description: {\n en: 'Google is a principal search engine technology and email service provider.',\n 'zh-CN': 'Google 是全球性的搜索引擎和邮件服务提供商。',\n 'tr-TR': 'Google, en büyük arama motoru teknolojisi ve e-posta servis sağlayıcısıdır.',\n ko: 'Google은 가장 큰 검색 엔진 기술과 이메일 서비스 제공자입니다.',\n },\n readme: './README.md',\n formItems: [\n {\n key: 'clientId',\n type: ConnectorConfigFormItemType.Text,\n label: 'Client ID',\n required: true,\n placeholder: '<client-id>',\n },\n {\n key: 'clientSecret',\n type: ConnectorConfigFormItemType.Text,\n label: 'Client Secret',\n required: true,\n placeholder: '<client-secret>',\n },\n {\n key: 'scope',\n type: ConnectorConfigFormItemType.Text,\n label: 'Scope',\n required: false,\n placeholder: '<scope>',\n description:\n \"The `scope` determines permissions granted by the user's authorization. If you are not sure what to enter, do not worry, just leave it blank.\",\n },\n {\n key: 'prompts',\n type: ConnectorConfigFormItemType.MultiSelect,\n required: false,\n label: 'Prompts',\n // Google does not support `login` prompt.\n // Ref: https://developers.google.com/identity/openid-connect/openid-connect#authenticationuriparameters\n selectItems: Object.values(OidcPrompt)\n .filter((prompt) => prompt !== OidcPrompt.Login)\n .map((prompt) => ({\n value: prompt,\n })),\n defaultValue: [OidcPrompt.SelectAccount],\n },\n ],\n};\n\nexport const defaultTimeout = 5000;\n\n// https://developers.google.com/identity/gsi/web/guides/verify-google-id-token\nexport const jwksUri = 'https://www.googleapis.com/oauth2/v3/certs';\n","import { z } from 'zod';\n\nimport { GoogleConnector } from '@logto/connector-kit';\n\nexport const accessTokenResponseGuard = z.object({\n access_token: z.string(),\n scope: z.string(),\n token_type: z.string(),\n});\n\nexport type AccessTokenResponse = z.infer<typeof accessTokenResponseGuard>;\n\nexport const userInfoResponseGuard = z.object({\n sub: z.string(),\n name: z.string().optional(),\n given_name: z.string().optional(),\n family_name: z.string().optional(),\n picture: z.string().optional(),\n email: z.string().optional(),\n email_verified: z.boolean().optional(),\n locale: z.string().optional(),\n});\n\nexport type UserInfoResponse = z.infer<typeof userInfoResponseGuard>;\n\nexport const authResponseGuard = z.object({\n code: z.string(),\n redirectUri: z.string(),\n});\n\n/**\n * Response payload from Google One Tap. Note the CSRF token is not included since it should be\n * verified by the web server.\n */\nexport const googleOneTapDataGuard = z.object({\n [GoogleConnector.oneTapParams.credential]: z.string(),\n});\n"],"mappings":"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","names":["GoogleConnector","GoogleConnector","GoogleConnector","scope"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constant.ts","../src/types.ts"],"sourcesContent":["/**\n * The Implementation of OpenID Connect of Google Identity Platform.\n * https://developers.google.com/identity/protocols/oauth2/openid-connect\n */\nimport { conditional, assert } from '@silverhand/essentials';\nimport { got, HTTPError } from 'got';\n\nimport type {\n GetAuthorizationUri,\n GetUserInfo,\n GetConnectorConfig,\n CreateConnector,\n SocialConnector,\n GoogleConnectorConfig,\n} from '@logto/connector-kit';\nimport {\n ConnectorError,\n ConnectorErrorCodes,\n validateConfig,\n ConnectorType,\n parseJson,\n GoogleConnector,\n} from '@logto/connector-kit';\nimport { createRemoteJWKSet, jwtVerify } from 'jose';\n\nimport {\n accessTokenEndpoint,\n authorizationEndpoint,\n scope as defaultScope,\n userInfoEndpoint,\n defaultMetadata,\n defaultTimeout,\n jwksUri,\n} from './constant.js';\nimport {\n accessTokenResponseGuard,\n userInfoResponseGuard,\n authResponseGuard,\n googleOneTapDataGuard,\n} from './types.js';\n\nconst getAuthorizationUri =\n (getConfig: GetConnectorConfig): GetAuthorizationUri =>\n async ({ state, redirectUri }) => {\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, GoogleConnector.configGuard);\n\n const { clientId, scope, prompts } = config;\n\n const queryParameters = new URLSearchParams({\n client_id: clientId,\n redirect_uri: redirectUri,\n response_type: 'code',\n state,\n scope: scope ?? defaultScope,\n ...conditional(prompts && prompts.length > 0 && { prompt: prompts.join(' ') }),\n });\n\n return `${authorizationEndpoint}?${queryParameters.toString()}`;\n };\n\nexport const getAccessToken = async (\n config: GoogleConnectorConfig,\n codeObject: { code: string; redirectUri: string }\n) => {\n const { code, redirectUri } = codeObject;\n const { clientId, clientSecret } = config;\n\n // Note:Need to decodeURIComponent on code\n // https://stackoverflow.com/questions/51058256/google-api-node-js-invalid-grant-malformed-auth-code\n const httpResponse = await got.post(accessTokenEndpoint, {\n form: {\n code: decodeURIComponent(code),\n client_id: clientId,\n client_secret: clientSecret,\n redirect_uri: redirectUri,\n grant_type: 'authorization_code',\n },\n timeout: { request: defaultTimeout },\n });\n\n const result = accessTokenResponseGuard.safeParse(parseJson(httpResponse.body));\n\n if (!result.success) {\n throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);\n }\n\n const { access_token: accessToken } = result.data;\n\n assert(accessToken, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid));\n\n return { accessToken };\n};\n\ntype Json = ReturnType<typeof parseJson>;\n\n/**\n * Get user information JSON from Google Identity Platform. It will use the following order to\n * retrieve user information:\n *\n * 1. Google One Tap: https://developers.google.com/identity/gsi/web/guides/verify-google-id-token\n * 2. Normal Google OAuth: https://developers.google.com/identity/protocols/oauth2/openid-connect\n *\n * @param data The data from the client.\n * @param config The configuration of the connector.\n * @returns A Promise that resolves to the user information JSON.\n */\nconst getUserInfoJson = async (data: unknown, config: GoogleConnectorConfig): Promise<Json> => {\n // Google One Tap\n const oneTapResult = googleOneTapDataGuard.safeParse(data);\n\n if (oneTapResult.success) {\n const { payload } = await jwtVerify<Json>(\n oneTapResult.data.credential,\n createRemoteJWKSet(new URL(jwksUri)),\n {\n // https://developers.google.com/identity/gsi/web/guides/verify-google-id-token\n issuer: ['https://accounts.google.com', 'accounts.google.com'],\n audience: config.clientId,\n clockTolerance: 10,\n }\n );\n return payload;\n }\n\n // Normal Google OAuth\n const { code, redirectUri } = await authorizationCallbackHandler(data);\n const { accessToken } = await getAccessToken(config, { code, redirectUri });\n\n const httpResponse = await got.post(userInfoEndpoint, {\n headers: {\n authorization: `Bearer ${accessToken}`,\n },\n timeout: { request: defaultTimeout },\n });\n return parseJson(httpResponse.body);\n};\n\nconst getUserInfo =\n (getConfig: GetConnectorConfig): GetUserInfo =>\n async (data) => {\n const config = await getConfig(defaultMetadata.id);\n validateConfig(config, GoogleConnector.configGuard);\n\n try {\n const rawData = await getUserInfoJson(data, config);\n const result = userInfoResponseGuard.safeParse(rawData);\n\n if (!result.success) {\n throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);\n }\n\n const { sub: id, picture: avatar, email, email_verified, name } = result.data;\n\n return {\n id,\n avatar,\n email: conditional(email_verified && email),\n name,\n rawData,\n };\n } catch (error: unknown) {\n return getUserInfoErrorHandler(error);\n }\n };\n\nconst authorizationCallbackHandler = async (parameterObject: unknown) => {\n const result = authResponseGuard.safeParse(parameterObject);\n\n if (!result.success) {\n throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));\n }\n\n return result.data;\n};\n\nconst getUserInfoErrorHandler = (error: unknown) => {\n if (error instanceof HTTPError) {\n const { statusCode, body: rawBody } = error.response;\n\n if (statusCode === 401) {\n throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid);\n }\n\n throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(rawBody));\n }\n\n throw error;\n};\n\nconst createGoogleConnector: CreateConnector<SocialConnector> = async ({ getConfig }) => {\n return {\n metadata: defaultMetadata,\n type: ConnectorType.Social,\n configGuard: GoogleConnector.configGuard,\n getAuthorizationUri: getAuthorizationUri(getConfig),\n getUserInfo: getUserInfo(getConfig),\n };\n};\n\nexport default createGoogleConnector;\n","import type { ConnectorMetadata } from '@logto/connector-kit';\nimport {\n ConnectorConfigFormItemType,\n ConnectorPlatform,\n GoogleConnector,\n OidcPrompt,\n} from '@logto/connector-kit';\n\nexport const authorizationEndpoint = 'https://accounts.google.com/o/oauth2/v2/auth';\nexport const accessTokenEndpoint = 'https://oauth2.googleapis.com/token';\nexport const userInfoEndpoint = 'https://openidconnect.googleapis.com/v1/userinfo';\nexport const scope = 'openid profile email';\n\n// Instead of defining the metadata in the connector, we reuse the metadata from the connector-kit.\n// This is not the normal practice, but Google One Tap is a special case.\n// @see {@link GoogleConnector} for more information.\nexport const defaultMetadata: ConnectorMetadata = {\n id: GoogleConnector.factoryId,\n target: GoogleConnector.target,\n platform: ConnectorPlatform.Universal,\n name: {\n en: 'Google',\n 'zh-CN': 'Google',\n 'tr-TR': 'Google',\n ko: 'Google',\n },\n logo: './logo.svg',\n logoDark: null,\n description: {\n en: 'Google is a principal search engine technology and email service provider.',\n 'zh-CN': 'Google 是全球性的搜索引擎和邮件服务提供商。',\n 'tr-TR': 'Google, en büyük arama motoru teknolojisi ve e-posta servis sağlayıcısıdır.',\n ko: 'Google은 가장 큰 검색 엔진 기술과 이메일 서비스 제공자입니다.',\n },\n readme: './README.md',\n formItems: [\n {\n key: 'clientId',\n type: ConnectorConfigFormItemType.Text,\n label: 'Client ID',\n required: true,\n placeholder: '<client-id>',\n },\n {\n key: 'clientSecret',\n type: ConnectorConfigFormItemType.Text,\n label: 'Client Secret',\n required: true,\n placeholder: '<client-secret>',\n },\n {\n key: 'scope',\n type: ConnectorConfigFormItemType.Text,\n label: 'Scope',\n required: false,\n placeholder: '<scope>',\n description:\n \"The `scope` determines permissions granted by the user's authorization. If you are not sure what to enter, do not worry, just leave it blank.\",\n },\n {\n key: 'prompts',\n type: ConnectorConfigFormItemType.MultiSelect,\n required: false,\n label: 'Prompts',\n // Google does not support `login` prompt.\n // Ref: https://developers.google.com/identity/openid-connect/openid-connect#authenticationuriparameters\n selectItems: Object.values(OidcPrompt)\n .filter((prompt) => prompt !== OidcPrompt.Login)\n .map((prompt) => ({\n value: prompt,\n })),\n defaultValue: [OidcPrompt.SelectAccount],\n },\n ],\n};\n\nexport const defaultTimeout = 5000;\n\n// https://developers.google.com/identity/gsi/web/guides/verify-google-id-token\nexport const jwksUri = 'https://www.googleapis.com/oauth2/v3/certs';\n","import { z } from 'zod';\n\nimport { GoogleConnector } from '@logto/connector-kit';\n\nexport const accessTokenResponseGuard = z.object({\n access_token: z.string(),\n scope: z.string(),\n token_type: z.string(),\n});\n\nexport type AccessTokenResponse = z.infer<typeof accessTokenResponseGuard>;\n\nexport const userInfoResponseGuard = z.object({\n sub: z.string(),\n name: z.string().optional(),\n given_name: z.string().optional(),\n family_name: z.string().optional(),\n picture: z.string().optional(),\n email: z.string().optional(),\n email_verified: z.boolean().optional(),\n locale: z.string().optional(),\n});\n\nexport type UserInfoResponse = z.infer<typeof userInfoResponseGuard>;\n\nexport const authResponseGuard = z.object({\n code: z.string(),\n redirectUri: z.string(),\n});\n\n/**\n * Response payload from Google One Tap. Note the CSRF token is not included since it should be\n * verified by the web server.\n */\nexport const googleOneTapDataGuard = z.object({\n [GoogleConnector.oneTapParams.credential]: z.string(),\n});\n"],"mappings":"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","names":["GoogleConnector","GoogleConnector","GoogleConnector","scope"]}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@logto/connector-google",
3
- "version": "1.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "Google web connector implementation.",
5
5
  "author": "Silverhand Inc. <contact@silverhand.io>",
6
6
  "dependencies": {
7
- "@logto/connector-kit": "^4.0.0",
7
+ "@logto/connector-kit": "^4.1.1",
8
8
  "@silverhand/essentials": "^2.9.1",
9
9
  "got": "^14.0.0",
10
10
  "jose": "^5.6.3",
@@ -46,15 +46,15 @@
46
46
  "@silverhand/ts-config": "6.0.0",
47
47
  "@types/node": "^20.11.20",
48
48
  "@types/supertest": "^6.0.2",
49
- "@vitest/coverage-v8": "^2.0.0",
49
+ "@vitest/coverage-v8": "^2.1.9",
50
50
  "eslint": "^8.56.0",
51
51
  "lint-staged": "^15.0.2",
52
52
  "nock": "^13.3.1",
53
53
  "prettier": "^3.0.0",
54
54
  "supertest": "^7.0.0",
55
- "tsup": "^8.1.0",
55
+ "tsup": "^8.3.0",
56
56
  "typescript": "^5.5.3",
57
- "vitest": "^2.0.0"
57
+ "vitest": "^2.1.9"
58
58
  },
59
59
  "scripts": {
60
60
  "precommit": "lint-staged",