@logto/connector-google 1.3.0 → 1.5.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 +25 -19
- package/lib/index.js +203 -182
- package/lib/index.js.map +1 -0
- package/package.json +25 -5
- package/lib/constant.d.ts +0 -7
- package/lib/index.d.ts +0 -10
- package/lib/mock.d.ts +0 -4
- package/lib/types.d.ts +0 -68
package/README.md
CHANGED
|
@@ -3,18 +3,17 @@
|
|
|
3
3
|
The Google connector provides a succinct way for your application to use Google’s OAuth 2.0 authentication system.
|
|
4
4
|
|
|
5
5
|
**Table of contents**
|
|
6
|
-
- [Google
|
|
7
|
-
|
|
8
|
-
- [Configure your
|
|
9
|
-
|
|
10
|
-
- [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- [
|
|
16
|
-
|
|
17
|
-
- [References](#references)
|
|
6
|
+
- [Set up a project in the Google API Console](#set-up-a-project-in-the-google-api-console)
|
|
7
|
+
- [Configure your consent screen](#configure-your-consent-screen)
|
|
8
|
+
- [Configure and register your application](#configure-and-register-your-application)
|
|
9
|
+
- [Edit app registration](#edit-app-registration)
|
|
10
|
+
- [Config OAuth consent screen](#config-oauth-consent-screen)
|
|
11
|
+
- [Config scopes](#config-scopes)
|
|
12
|
+
- [Add test users (External user type only)](#add-test-users-external-user-type-only)
|
|
13
|
+
- [Obtain OAuth 2.0 credentials](#obtain-oauth-20-credentials)
|
|
14
|
+
- [Configure your connector](#configure-your-connector)
|
|
15
|
+
- [Config types](#config-types)
|
|
16
|
+
- [References](#references)
|
|
18
17
|
|
|
19
18
|
## Set up a project in the Google API Console
|
|
20
19
|
|
|
@@ -57,8 +56,8 @@ Now you should have the Google OAuth 2.0 consent screen configured.
|
|
|
57
56
|
- On the **Credentials** page, click the **+ CREATE CREDENTIALS** button on the top menu bar, and select **OAuth client ID**.
|
|
58
57
|
- On the **Create OAuth client ID** page, select **Web application** as the application type.
|
|
59
58
|
- Fill out the basic information for your application.
|
|
60
|
-
- Click **+ Add URI** to add an authorized domain to the **Authorized JavaScript origins** section. This is the domain that your logto authorization page will be served from. In our case, this will be `${
|
|
61
|
-
- Click **+ Add URI** in the ****Authorized redirect URIs**** section to set up the ****Authorized redirect URIs****, which redirect the user to the application after logging in. In our case, this will be `${your_logto_endpoint}/callback/${connector_id}`. e.g. `https://logto.
|
|
59
|
+
- Click **+ Add URI** to add an authorized domain to the **Authorized JavaScript origins** section. This is the domain that your logto authorization page will be served from. In our case, this will be `${your_logto_endpoint_origin}`. e.g.`https://foo.logto.app`.
|
|
60
|
+
- Click **+ Add URI** in the ****Authorized redirect URIs**** section to set up the ****Authorized redirect URIs****, which redirect the user to the application after logging in. In our case, this will be `${your_logto_endpoint}/callback/${connector_id}`. e.g. `https://foo.logto.app/callback/${connector_id}`. The `connector_id` can be found on the top bar of the Logto Admin Console connector details page.
|
|
62
61
|
- Click **Create** to finish and then you will get the **Client ID** and **Client Secret**.
|
|
63
62
|
|
|
64
63
|
## Configure your connector
|
|
@@ -67,13 +66,20 @@ Fill out the `clientId` and `clientSecret` field with _Client ID_ and _Client Se
|
|
|
67
66
|
|
|
68
67
|
`scope` is a space-delimited list of [scopes](https://developers.google.com/identity/protocols/oauth2/scopes). If not provided, scope defaults to be `openid profile email`.
|
|
69
68
|
|
|
69
|
+
`prompts` is an array of strings that specifies the type of user interaction that is required. The string can be one of the following values:
|
|
70
|
+
|
|
71
|
+
- `none`: The authorization server does not display any authentication or user consent screens; it will return an error if the user is not already authenticated and has not pre-configured consent for the requested scopes. You can use none to check for existing authentication and/or consent.
|
|
72
|
+
- `consent`: The authorization server prompts the user for consent before returning information to the client.
|
|
73
|
+
- `select_account`: The authorization server prompts the user to select a user account. This allows a user who has multiple accounts at the authorization server to select amongst the multiple accounts that they may have current sessions for.
|
|
74
|
+
|
|
70
75
|
### Config types
|
|
71
76
|
|
|
72
|
-
| Name | Type
|
|
73
|
-
|
|
74
|
-
| clientId | string
|
|
75
|
-
| clientSecret | string
|
|
76
|
-
| scope | string
|
|
77
|
+
| Name | Type |
|
|
78
|
+
|--------------|----------|
|
|
79
|
+
| clientId | string |
|
|
80
|
+
| clientSecret | string |
|
|
81
|
+
| scope | string |
|
|
82
|
+
| prompts | string[] |
|
|
77
83
|
|
|
78
84
|
## References
|
|
79
85
|
* [Google Identity: Setting up OAuth 2.0](https://developers.google.com/identity/protocols/oauth2/openid-connect#appsetup)
|
package/lib/index.js
CHANGED
|
@@ -1,201 +1,222 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { conditional, assert } from "@silverhand/essentials";
|
|
3
|
+
import { got, HTTPError } from "got";
|
|
4
|
+
import {
|
|
5
|
+
ConnectorError,
|
|
6
|
+
ConnectorErrorCodes,
|
|
7
|
+
validateConfig,
|
|
8
|
+
ConnectorType,
|
|
9
|
+
parseJson,
|
|
10
|
+
GoogleConnector as GoogleConnector3
|
|
11
|
+
} from "@logto/connector-kit";
|
|
12
|
+
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
4
13
|
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
ko: 'Google',
|
|
14
|
+
// src/constant.ts
|
|
15
|
+
import {
|
|
16
|
+
ConnectorConfigFormItemType,
|
|
17
|
+
ConnectorPlatform,
|
|
18
|
+
GoogleConnector,
|
|
19
|
+
OidcPrompt
|
|
20
|
+
} from "@logto/connector-kit";
|
|
21
|
+
var authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
22
|
+
var accessTokenEndpoint = "https://oauth2.googleapis.com/token";
|
|
23
|
+
var userInfoEndpoint = "https://openidconnect.googleapis.com/v1/userinfo";
|
|
24
|
+
var scope = "openid profile email";
|
|
25
|
+
var defaultMetadata = {
|
|
26
|
+
id: GoogleConnector.factoryId,
|
|
27
|
+
target: GoogleConnector.target,
|
|
28
|
+
platform: ConnectorPlatform.Universal,
|
|
29
|
+
name: {
|
|
30
|
+
en: "Google",
|
|
31
|
+
"zh-CN": "Google",
|
|
32
|
+
"tr-TR": "Google",
|
|
33
|
+
ko: "Google"
|
|
34
|
+
},
|
|
35
|
+
logo: "./logo.svg",
|
|
36
|
+
logoDark: null,
|
|
37
|
+
description: {
|
|
38
|
+
en: "Google is a principal search engine technology and email service provider.",
|
|
39
|
+
"zh-CN": "Google \u662F\u5168\u7403\u6027\u7684\u641C\u7D22\u5F15\u64CE\u548C\u90AE\u4EF6\u670D\u52A1\u63D0\u4F9B\u5546\u3002",
|
|
40
|
+
"tr-TR": "Google, en b\xFCy\xFCk arama motoru teknolojisi ve e-posta servis sa\u011Flay\u0131c\u0131s\u0131d\u0131r.",
|
|
41
|
+
ko: "Google\uC740 \uAC00\uC7A5 \uD070 \uAC80\uC0C9 \uC5D4\uC9C4 \uAE30\uC220\uACFC \uC774\uBA54\uC77C \uC11C\uBE44\uC2A4 \uC81C\uACF5\uC790\uC785\uB2C8\uB2E4."
|
|
42
|
+
},
|
|
43
|
+
readme: "./README.md",
|
|
44
|
+
formItems: [
|
|
45
|
+
{
|
|
46
|
+
key: "clientId",
|
|
47
|
+
type: ConnectorConfigFormItemType.Text,
|
|
48
|
+
label: "Client ID",
|
|
49
|
+
required: true,
|
|
50
|
+
placeholder: "<client-id>"
|
|
43
51
|
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
ko: 'Google은 가장 큰 검색 엔진 기술과 이메일 서비스 제공자입니다.',
|
|
52
|
+
{
|
|
53
|
+
key: "clientSecret",
|
|
54
|
+
type: ConnectorConfigFormItemType.Text,
|
|
55
|
+
label: "Client Secret",
|
|
56
|
+
required: true,
|
|
57
|
+
placeholder: "<client-secret>"
|
|
51
58
|
},
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
required: false,
|
|
73
|
-
placeholder: '<scope>',
|
|
74
|
-
description: "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.",
|
|
75
|
-
},
|
|
76
|
-
],
|
|
59
|
+
{
|
|
60
|
+
key: "scope",
|
|
61
|
+
type: ConnectorConfigFormItemType.Text,
|
|
62
|
+
label: "Scope",
|
|
63
|
+
required: false,
|
|
64
|
+
placeholder: "<scope>",
|
|
65
|
+
description: "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."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: "prompts",
|
|
69
|
+
type: ConnectorConfigFormItemType.MultiSelect,
|
|
70
|
+
required: false,
|
|
71
|
+
label: "Prompts",
|
|
72
|
+
// Google does not support `login` prompt.
|
|
73
|
+
// Ref: https://developers.google.com/identity/openid-connect/openid-connect#authenticationuriparameters
|
|
74
|
+
selectItems: Object.values(OidcPrompt).filter((prompt) => prompt !== OidcPrompt.Login).map((prompt) => ({
|
|
75
|
+
value: prompt
|
|
76
|
+
}))
|
|
77
|
+
}
|
|
78
|
+
]
|
|
77
79
|
};
|
|
78
|
-
|
|
80
|
+
var defaultTimeout = 5e3;
|
|
81
|
+
var jwksUri = "https://www.googleapis.com/oauth2/v3/certs";
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
// src/types.ts
|
|
84
|
+
import { z } from "zod";
|
|
85
|
+
import { GoogleConnector as GoogleConnector2 } from "@logto/connector-kit";
|
|
86
|
+
var accessTokenResponseGuard = z.object({
|
|
87
|
+
access_token: z.string(),
|
|
88
|
+
scope: z.string(),
|
|
89
|
+
token_type: z.string()
|
|
84
90
|
});
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
var userInfoResponseGuard = z.object({
|
|
92
|
+
sub: z.string(),
|
|
93
|
+
name: z.string().optional(),
|
|
94
|
+
given_name: z.string().optional(),
|
|
95
|
+
family_name: z.string().optional(),
|
|
96
|
+
picture: z.string().optional(),
|
|
97
|
+
email: z.string().optional(),
|
|
98
|
+
email_verified: z.boolean().optional(),
|
|
99
|
+
locale: z.string().optional()
|
|
89
100
|
});
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
given_name: z.string().optional(),
|
|
94
|
-
family_name: z.string().optional(),
|
|
95
|
-
picture: z.string().optional(),
|
|
96
|
-
email: z.string().optional(),
|
|
97
|
-
email_verified: z.boolean().optional(),
|
|
98
|
-
locale: z.string().optional(),
|
|
101
|
+
var authResponseGuard = z.object({
|
|
102
|
+
code: z.string(),
|
|
103
|
+
redirectUri: z.string()
|
|
99
104
|
});
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
redirectUri: z.string(),
|
|
105
|
+
var googleOneTapDataGuard = z.object({
|
|
106
|
+
[GoogleConnector2.oneTapParams.credential]: z.string()
|
|
103
107
|
});
|
|
104
108
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return `${authorizationEndpoint}?${queryParameters.toString()}`;
|
|
109
|
+
// src/index.ts
|
|
110
|
+
var getAuthorizationUri = (getConfig) => async ({ state, redirectUri }) => {
|
|
111
|
+
const config = await getConfig(defaultMetadata.id);
|
|
112
|
+
validateConfig(config, GoogleConnector3.configGuard);
|
|
113
|
+
const { clientId, scope: scope2, prompts } = config;
|
|
114
|
+
const queryParameters = new URLSearchParams({
|
|
115
|
+
client_id: clientId,
|
|
116
|
+
redirect_uri: redirectUri,
|
|
117
|
+
response_type: "code",
|
|
118
|
+
state,
|
|
119
|
+
scope: scope2 ?? scope,
|
|
120
|
+
...conditional(prompts && prompts.length > 0 && { prompt: prompts.join(" ") })
|
|
121
|
+
});
|
|
122
|
+
return `${authorizationEndpoint}?${queryParameters.toString()}`;
|
|
120
123
|
};
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
assert(accessToken, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid));
|
|
142
|
-
return { accessToken };
|
|
124
|
+
var getAccessToken = async (config, codeObject) => {
|
|
125
|
+
const { code, redirectUri } = codeObject;
|
|
126
|
+
const { clientId, clientSecret } = config;
|
|
127
|
+
const httpResponse = await got.post(accessTokenEndpoint, {
|
|
128
|
+
form: {
|
|
129
|
+
code: decodeURIComponent(code),
|
|
130
|
+
client_id: clientId,
|
|
131
|
+
client_secret: clientSecret,
|
|
132
|
+
redirect_uri: redirectUri,
|
|
133
|
+
grant_type: "authorization_code"
|
|
134
|
+
},
|
|
135
|
+
timeout: { request: defaultTimeout }
|
|
136
|
+
});
|
|
137
|
+
const result = accessTokenResponseGuard.safeParse(parseJson(httpResponse.body));
|
|
138
|
+
if (!result.success) {
|
|
139
|
+
throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);
|
|
140
|
+
}
|
|
141
|
+
const { access_token: accessToken } = result.data;
|
|
142
|
+
assert(accessToken, new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid));
|
|
143
|
+
return { accessToken };
|
|
143
144
|
};
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
catch (error) {
|
|
171
|
-
return getUserInfoErrorHandler(error);
|
|
172
|
-
}
|
|
145
|
+
var getUserInfoJson = async (data, config) => {
|
|
146
|
+
const oneTapResult = googleOneTapDataGuard.safeParse(data);
|
|
147
|
+
if (oneTapResult.success) {
|
|
148
|
+
const { payload } = await jwtVerify(
|
|
149
|
+
oneTapResult.data.credential,
|
|
150
|
+
createRemoteJWKSet(new URL(jwksUri)),
|
|
151
|
+
{
|
|
152
|
+
// https://developers.google.com/identity/gsi/web/guides/verify-google-id-token
|
|
153
|
+
issuer: ["https://accounts.google.com", "accounts.google.com"],
|
|
154
|
+
audience: config.clientId,
|
|
155
|
+
clockTolerance: 10
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
return payload;
|
|
159
|
+
}
|
|
160
|
+
const { code, redirectUri } = await authorizationCallbackHandler(data);
|
|
161
|
+
const { accessToken } = await getAccessToken(config, { code, redirectUri });
|
|
162
|
+
const httpResponse = await got.post(userInfoEndpoint, {
|
|
163
|
+
headers: {
|
|
164
|
+
authorization: `Bearer ${accessToken}`
|
|
165
|
+
},
|
|
166
|
+
timeout: { request: defaultTimeout }
|
|
167
|
+
});
|
|
168
|
+
return parseJson(httpResponse.body);
|
|
173
169
|
};
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
var getUserInfo = (getConfig) => async (data) => {
|
|
171
|
+
const config = await getConfig(defaultMetadata.id);
|
|
172
|
+
validateConfig(config, GoogleConnector3.configGuard);
|
|
173
|
+
try {
|
|
174
|
+
const rawData = await getUserInfoJson(data, config);
|
|
175
|
+
const result = userInfoResponseGuard.safeParse(rawData);
|
|
176
176
|
if (!result.success) {
|
|
177
|
-
|
|
177
|
+
throw new ConnectorError(ConnectorErrorCodes.InvalidResponse, result.error);
|
|
178
178
|
}
|
|
179
|
-
|
|
179
|
+
const { sub: id, picture: avatar, email, email_verified, name } = result.data;
|
|
180
|
+
return {
|
|
181
|
+
id,
|
|
182
|
+
avatar,
|
|
183
|
+
email: conditional(email_verified && email),
|
|
184
|
+
name,
|
|
185
|
+
rawData
|
|
186
|
+
};
|
|
187
|
+
} catch (error) {
|
|
188
|
+
return getUserInfoErrorHandler(error);
|
|
189
|
+
}
|
|
180
190
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
191
|
+
var authorizationCallbackHandler = async (parameterObject) => {
|
|
192
|
+
const result = authResponseGuard.safeParse(parameterObject);
|
|
193
|
+
if (!result.success) {
|
|
194
|
+
throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(parameterObject));
|
|
195
|
+
}
|
|
196
|
+
return result.data;
|
|
197
|
+
};
|
|
198
|
+
var getUserInfoErrorHandler = (error) => {
|
|
199
|
+
if (error instanceof HTTPError) {
|
|
200
|
+
const { statusCode, body: rawBody } = error.response;
|
|
201
|
+
if (statusCode === 401) {
|
|
202
|
+
throw new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid);
|
|
188
203
|
}
|
|
189
|
-
throw
|
|
204
|
+
throw new ConnectorError(ConnectorErrorCodes.General, JSON.stringify(rawBody));
|
|
205
|
+
}
|
|
206
|
+
throw error;
|
|
190
207
|
};
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
208
|
+
var createGoogleConnector = async ({ getConfig }) => {
|
|
209
|
+
return {
|
|
210
|
+
metadata: defaultMetadata,
|
|
211
|
+
type: ConnectorType.Social,
|
|
212
|
+
configGuard: GoogleConnector3.configGuard,
|
|
213
|
+
getAuthorizationUri: getAuthorizationUri(getConfig),
|
|
214
|
+
getUserInfo: getUserInfo(getConfig)
|
|
215
|
+
};
|
|
199
216
|
};
|
|
200
|
-
|
|
201
|
-
export {
|
|
217
|
+
var src_default = createGoogleConnector;
|
|
218
|
+
export {
|
|
219
|
+
src_default as default,
|
|
220
|
+
getAccessToken
|
|
221
|
+
};
|
|
222
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +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 },\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,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/connector-google",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Google web connector implementation.",
|
|
5
5
|
"author": "Silverhand Inc. <contact@silverhand.io>",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@logto/connector-kit": "^
|
|
7
|
+
"@logto/connector-kit": "^4.0.0",
|
|
8
|
+
"@silverhand/essentials": "^2.9.1",
|
|
9
|
+
"got": "^14.0.0",
|
|
10
|
+
"jose": "^5.6.3",
|
|
11
|
+
"snakecase-keys": "^8.0.1",
|
|
12
|
+
"zod": "^3.23.8"
|
|
8
13
|
},
|
|
9
14
|
"main": "./lib/index.js",
|
|
10
15
|
"module": "./lib/index.js",
|
|
@@ -36,11 +41,26 @@
|
|
|
36
41
|
"publishConfig": {
|
|
37
42
|
"access": "public"
|
|
38
43
|
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@silverhand/eslint-config": "6.0.1",
|
|
46
|
+
"@silverhand/ts-config": "6.0.0",
|
|
47
|
+
"@types/node": "^20.11.20",
|
|
48
|
+
"@types/supertest": "^6.0.2",
|
|
49
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
50
|
+
"eslint": "^8.56.0",
|
|
51
|
+
"lint-staged": "^15.0.2",
|
|
52
|
+
"nock": "^13.3.1",
|
|
53
|
+
"prettier": "^3.0.0",
|
|
54
|
+
"supertest": "^7.0.0",
|
|
55
|
+
"tsup": "^8.1.0",
|
|
56
|
+
"typescript": "^5.5.3",
|
|
57
|
+
"vitest": "^2.0.0"
|
|
58
|
+
},
|
|
39
59
|
"scripts": {
|
|
40
60
|
"precommit": "lint-staged",
|
|
41
|
-
"
|
|
42
|
-
"build": "
|
|
43
|
-
"dev": "
|
|
61
|
+
"check": "tsc --noEmit",
|
|
62
|
+
"build": "tsup",
|
|
63
|
+
"dev": "tsup --watch",
|
|
44
64
|
"lint": "eslint --ext .ts src",
|
|
45
65
|
"lint:report": "pnpm lint --format json --output-file report.json",
|
|
46
66
|
"test": "vitest src",
|
package/lib/constant.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { ConnectorMetadata } from '@logto/connector-kit';
|
|
2
|
-
export declare const authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
3
|
-
export declare const accessTokenEndpoint = "https://oauth2.googleapis.com/token";
|
|
4
|
-
export declare const userInfoEndpoint = "https://openidconnect.googleapis.com/v1/userinfo";
|
|
5
|
-
export declare const scope = "openid profile email";
|
|
6
|
-
export declare const defaultMetadata: ConnectorMetadata;
|
|
7
|
-
export declare const defaultTimeout = 5000;
|
package/lib/index.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { CreateConnector, SocialConnector } from '@logto/connector-kit';
|
|
2
|
-
import type { GoogleConfig } from './types.js';
|
|
3
|
-
export declare const getAccessToken: (config: GoogleConfig, codeObject: {
|
|
4
|
-
code: string;
|
|
5
|
-
redirectUri: string;
|
|
6
|
-
}) => Promise<{
|
|
7
|
-
accessToken: string;
|
|
8
|
-
}>;
|
|
9
|
-
declare const createGoogleConnector: CreateConnector<SocialConnector>;
|
|
10
|
-
export default createGoogleConnector;
|
package/lib/mock.d.ts
DELETED
package/lib/types.d.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export declare const googleConfigGuard: z.ZodObject<{
|
|
3
|
-
clientId: z.ZodString;
|
|
4
|
-
clientSecret: z.ZodString;
|
|
5
|
-
scope: z.ZodOptional<z.ZodString>;
|
|
6
|
-
}, "strip", z.ZodTypeAny, {
|
|
7
|
-
clientId: string;
|
|
8
|
-
clientSecret: string;
|
|
9
|
-
scope?: string | undefined;
|
|
10
|
-
}, {
|
|
11
|
-
clientId: string;
|
|
12
|
-
clientSecret: string;
|
|
13
|
-
scope?: string | undefined;
|
|
14
|
-
}>;
|
|
15
|
-
export type GoogleConfig = z.infer<typeof googleConfigGuard>;
|
|
16
|
-
export declare const accessTokenResponseGuard: z.ZodObject<{
|
|
17
|
-
access_token: z.ZodString;
|
|
18
|
-
scope: z.ZodString;
|
|
19
|
-
token_type: z.ZodString;
|
|
20
|
-
}, "strip", z.ZodTypeAny, {
|
|
21
|
-
scope: string;
|
|
22
|
-
access_token: string;
|
|
23
|
-
token_type: string;
|
|
24
|
-
}, {
|
|
25
|
-
scope: string;
|
|
26
|
-
access_token: string;
|
|
27
|
-
token_type: string;
|
|
28
|
-
}>;
|
|
29
|
-
export type AccessTokenResponse = z.infer<typeof accessTokenResponseGuard>;
|
|
30
|
-
export declare const userInfoResponseGuard: z.ZodObject<{
|
|
31
|
-
sub: z.ZodString;
|
|
32
|
-
name: z.ZodOptional<z.ZodString>;
|
|
33
|
-
given_name: z.ZodOptional<z.ZodString>;
|
|
34
|
-
family_name: z.ZodOptional<z.ZodString>;
|
|
35
|
-
picture: z.ZodOptional<z.ZodString>;
|
|
36
|
-
email: z.ZodOptional<z.ZodString>;
|
|
37
|
-
email_verified: z.ZodOptional<z.ZodBoolean>;
|
|
38
|
-
locale: z.ZodOptional<z.ZodString>;
|
|
39
|
-
}, "strip", z.ZodTypeAny, {
|
|
40
|
-
sub: string;
|
|
41
|
-
name?: string | undefined;
|
|
42
|
-
given_name?: string | undefined;
|
|
43
|
-
family_name?: string | undefined;
|
|
44
|
-
picture?: string | undefined;
|
|
45
|
-
email?: string | undefined;
|
|
46
|
-
email_verified?: boolean | undefined;
|
|
47
|
-
locale?: string | undefined;
|
|
48
|
-
}, {
|
|
49
|
-
sub: string;
|
|
50
|
-
name?: string | undefined;
|
|
51
|
-
given_name?: string | undefined;
|
|
52
|
-
family_name?: string | undefined;
|
|
53
|
-
picture?: string | undefined;
|
|
54
|
-
email?: string | undefined;
|
|
55
|
-
email_verified?: boolean | undefined;
|
|
56
|
-
locale?: string | undefined;
|
|
57
|
-
}>;
|
|
58
|
-
export type UserInfoResponse = z.infer<typeof userInfoResponseGuard>;
|
|
59
|
-
export declare const authResponseGuard: z.ZodObject<{
|
|
60
|
-
code: z.ZodString;
|
|
61
|
-
redirectUri: z.ZodString;
|
|
62
|
-
}, "strip", z.ZodTypeAny, {
|
|
63
|
-
code: string;
|
|
64
|
-
redirectUri: string;
|
|
65
|
-
}, {
|
|
66
|
-
code: string;
|
|
67
|
-
redirectUri: string;
|
|
68
|
-
}>;
|