@live-change/google-authentication-service 0.8.33 → 0.8.34

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/account.js ADDED
@@ -0,0 +1,82 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+ import definition from './definition.js'
4
+
5
+ export const User = definition.foreignModel("user", "User")
6
+
7
+ export const googleProperties = {
8
+ email: {
9
+ type: String
10
+ },
11
+ email_verified: {
12
+ type: Boolean
13
+ },
14
+ name: {
15
+ type: String
16
+ },
17
+ given_name: {
18
+ type: String
19
+ },
20
+ family_name: {
21
+ type: String
22
+ },
23
+ picture: {
24
+ type: String
25
+ },
26
+ locale: {
27
+ type: String
28
+ }
29
+ }
30
+
31
+ export const Account = definition.model({
32
+ name: "Account",
33
+ properties: {
34
+ ...googleProperties
35
+ },
36
+ userItem: {
37
+ userReadAccess: () => true
38
+ }
39
+ })
40
+
41
+ definition.event({
42
+ name: 'accountConnected',
43
+ properties: {
44
+ account: {
45
+ type: String,
46
+ validation: ['nonEmpty']
47
+ },
48
+ user: {
49
+ type: User,
50
+ validation: ['nonEmpty']
51
+ },
52
+ data: {
53
+ type: Object,
54
+ properties: googleProperties,
55
+ validation: ['nonEmpty']
56
+ }
57
+ },
58
+ async execute({ user, account, data }) {
59
+ await Account.create({
60
+ ...data,
61
+ id: account,
62
+ user
63
+ })
64
+ }
65
+ })
66
+
67
+ definition.event({
68
+ name: "accountDisconnected",
69
+ properties: {
70
+ account: {
71
+ type: String,
72
+ validation: ['nonEmpty']
73
+ },
74
+ user: {
75
+ type: User,
76
+ validation: ['nonEmpty']
77
+ }
78
+ },
79
+ async execute({ account }) {
80
+ await Account.delete(account)
81
+ }
82
+ })
package/connect.js ADDED
@@ -0,0 +1,138 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+ import definition from './definition.js'
4
+
5
+ import Debug from 'debug'
6
+ const debug = Debug('services:googleAuthentication')
7
+
8
+ import { User, googleProperties, Account } from './account.js'
9
+ import { getTokensWithCode, getUserInfo } from './googleClient.js'
10
+
11
+ import { downloadData } from './downloadData.js'
12
+
13
+ definition.trigger({
14
+ name: "connectGoogle",
15
+ properties: {
16
+ user: {
17
+ type: User,
18
+ validation: ['nonEmpty']
19
+ },
20
+ account: {
21
+ type: String,
22
+ validation: ['nonEmpty']
23
+ },
24
+ data: {
25
+ type: Object,
26
+ properties: googleProperties,
27
+ validation: ['nonEmpty']
28
+ },
29
+ transferOwnership: {
30
+ type: Boolean,
31
+ default: false
32
+ }
33
+ },
34
+ async execute({ user, account, data, transferOwnership },
35
+ context, emit) {
36
+ const { trigger } = context
37
+ const accountData = await Account.get(account)
38
+ if(accountData) {
39
+ if(accountData.user !== user) {
40
+ if(transferOwnership) {
41
+ emit({
42
+ 'type': 'userOwnedAccountTransferred',
43
+ account, to: user
44
+ })
45
+ await downloadData(user, data, context)
46
+ return
47
+ }
48
+ throw 'alreadyConnectedElsewhere'
49
+ }
50
+ throw 'alreadyConnected'
51
+ }
52
+ emit({
53
+ type: 'accountConnected',
54
+ account, user, data
55
+ })
56
+ await trigger({ type: 'googleConnected' }, {
57
+ user, account, data
58
+ })
59
+ await downloadData(user, data, context)
60
+ }
61
+ })
62
+
63
+ definition.trigger({
64
+ name: "disconnectGoogle",
65
+ properties: {
66
+ account: {
67
+ type: String,
68
+ validation: ['nonEmpty']
69
+ }
70
+ },
71
+ async execute({ account }, { client, service }, emit) {
72
+ const accountData = await Account.get(account)
73
+ if(!accountData) throw 'notFound'
74
+ const { user } = accountData
75
+ emit({
76
+ type: 'accountDisconnected',
77
+ account, user
78
+ })
79
+ await service.trigger({ type: 'googleDisconnected' }, {
80
+ user, account
81
+ })
82
+ }
83
+ })
84
+
85
+
86
+ definition.action({
87
+ name: "connectGoogle",
88
+ properties: {
89
+ code: {
90
+ type: String,
91
+ validation: ['nonEmpty']
92
+ },
93
+ redirectUri: {
94
+ type: String,
95
+ validation: ['nonEmpty']
96
+ },
97
+ transferOwnership: {
98
+ type: Boolean,
99
+ default: false
100
+ }
101
+ },
102
+ async execute({ accessToken, transferOwnership }, { client, service }, emit) {
103
+ const user = client.user
104
+ if(!user) throw 'notAuthorized'
105
+ const tokens = await getTokensWithCode(code, redirectUri)
106
+ debug("TOKENS", tokens)
107
+ const googleUser = await getUserInfo(tokens.access_token)
108
+ debug("GOOGLE USER", googleUser)
109
+ const account = googleUser.sub
110
+ await service.trigger({ type: 'connectGoogle' }, {
111
+ user, account, data: googleUser,
112
+ transferOwnership
113
+ })
114
+ return {
115
+ action: 'connectGoogle',
116
+ user
117
+ }
118
+ }
119
+ })
120
+
121
+ definition.action({
122
+ name: "disconnectGoogle",
123
+ properties: {
124
+ account: {
125
+ type: String,
126
+ validation: ['nonEmpty']
127
+ }
128
+ },
129
+ async execute({ account }, { client, service }, emit) {
130
+ const accountData = await Account.get(account)
131
+ if(!accountData) throw 'notFound'
132
+ const { user } = accountData
133
+ if(user !== client.user) throw 'notAuthorized'
134
+ await service.trigger({ type: 'disconnectGoogle' }, {
135
+ user, account
136
+ })
137
+ }
138
+ })
package/definition.js ADDED
@@ -0,0 +1,11 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+
4
+ import user from '@live-change/user-service'
5
+
6
+ const definition = app.createServiceDefinition({
7
+ name: "googleAuthentication",
8
+ use: [ user ]
9
+ })
10
+
11
+ export default definition
@@ -0,0 +1,46 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+ import definition from './definition.js'
4
+
5
+ export async function downloadData(user, data, { trigger, triggerService }) {
6
+ console.log("download data", { user, data })
7
+ const res = await trigger({ type: 'setIdentification' }, {
8
+ sessionOrUserType: 'user_User',
9
+ sessionOrUser: user,
10
+ overwrite: false,
11
+ name: data.name,
12
+ givenName: data.given_name,
13
+ firstName: data.given_name,
14
+ familyName: data.family_name,
15
+ lastName: data.family_name,
16
+ })
17
+ //console.log("IDENTIFICATION SET!", res)
18
+ if(data.picture) {
19
+ //console.log("WILL DOWNLOAD PICTURE", data.picture)
20
+ const downloadAndUpdateImage = (async () => {
21
+ //console.log("DOWNLOAD PICTURE", data.picture)
22
+ const image = await triggerService({ service: 'image', type: "createImageFromUrl" }, {
23
+ ownerType: 'user_User',
24
+ owner: user,
25
+ name: "google-profile-picture",
26
+ purpose: "users-updatePicture-picture",
27
+ url: data.picture,
28
+ cropped: true
29
+ })
30
+ //console.log("IMAGE DOWNLOADED", picture)
31
+ await trigger({ type: 'setIdentification' }, {
32
+ sessionOrUserType: 'user_User',
33
+ sessionOrUser: user,
34
+ overwrite: false,
35
+ image
36
+ })
37
+ })
38
+ downloadAndUpdateImage()
39
+ }
40
+ if(data.email_verified) {
41
+ await trigger({ type: 'connectEmail' }, {
42
+ email: data.email,
43
+ user
44
+ })
45
+ }
46
+ }
@@ -0,0 +1,57 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+ import definition from './definition.js'
4
+
5
+ import Debug from 'debug'
6
+ const debug = Debug('services:googleAuthentication')
7
+
8
+ import axios from 'axios'
9
+
10
+ const config = definition.config
11
+
12
+ const googleClientId = config.clientId || process.env.GOOGLE_CLIENT_ID
13
+ const googleClientSecret = config.clientId || process.env.GOOGLE_CLIENT_SECRET
14
+ export { googleClientId, googleClientSecret }
15
+
16
+ export async function getTokensWithCode(code, redirectUri) {
17
+ if(!code) throw new Error("No code provided")
18
+ if(!redirectUri) throw new Error("No redirectUri provided")
19
+
20
+ const options = {
21
+ url: 'https://accounts.google.com/o/oauth2/token',
22
+ method: 'post',
23
+ data: {
24
+ code: code,
25
+ client_id: googleClientId,
26
+ client_secret: googleClientSecret,
27
+ redirect_uri: redirectUri,
28
+ grant_type: "authorization_code"
29
+ }
30
+ }
31
+
32
+ try {
33
+ const response = await axios(options)
34
+ return response.data
35
+ } catch(error) {
36
+ console.error("OAUTH ERROR", error)
37
+ throw error?.response?.data ? new Error(error?.response?.data) : error
38
+ }
39
+ }
40
+
41
+ export async function getUserInfo(accessToken) {
42
+ const options = {
43
+ url: 'https://www.googleapis.com/oauth2/v3/userinfo',
44
+ method: 'get',
45
+ headers: {
46
+ Authorization: `Bearer ${accessToken}`
47
+ }
48
+ }
49
+
50
+ try {
51
+ const response = await axios(options)
52
+ return response.data
53
+ } catch(error) {
54
+ console.error("OAUTH ERROR", error)
55
+ throw error?.response?.data ? new Error(error?.response?.data) : error
56
+ }
57
+ }
package/index.js CHANGED
@@ -1,400 +1,12 @@
1
1
  import App from '@live-change/framework'
2
2
  const app = App.app()
3
+ import definition from './definition.js'
3
4
 
4
- import Debug from 'debug'
5
- const debug = Debug('services:googleAuthentication')
5
+ import "./account.js"
6
+ import "./connect.js"
7
+ import "./sign.js"
8
+ import "./offlineAccess.js"
6
9
 
7
- import { OAuth2Client } from 'google-auth-library'
8
10
 
9
- import user from '@live-change/user-service'
10
-
11
- const definition = app.createServiceDefinition({
12
- name: "googleAuthentication",
13
- use: [ user ]
14
- })
15
- const config = definition.config
16
-
17
- const googleClientId = config.clientId || process.env.GOOGLE_CLIENT_ID
18
- const googleClient = new OAuth2Client(googleClientId)
19
-
20
- const User = definition.foreignModel("user", "User")
21
-
22
- const googleProperties = {
23
- email: {
24
- type: String
25
- },
26
- email_verified: {
27
- type: Boolean
28
- },
29
- name: {
30
- type: String
31
- },
32
- given_name: {
33
- type: String
34
- },
35
- family_name: {
36
- type: String
37
- },
38
- picture: {
39
- type: String
40
- },
41
- locale: {
42
- type: String
43
- }
44
- }
45
-
46
- const Account = definition.model({
47
- name: "Account",
48
- properties: {
49
- ...googleProperties
50
- },
51
- userItem: {
52
- userReadAccess: () => true
53
- }
54
- })
55
-
56
- definition.event({
57
- name: 'accountConnected',
58
- properties: {
59
- account: {
60
- type: String,
61
- validation: ['nonEmpty']
62
- },
63
- user: {
64
- type: User,
65
- validation: ['nonEmpty']
66
- },
67
- data: {
68
- type: Object,
69
- properties: googleProperties,
70
- validation: ['nonEmpty']
71
- }
72
- },
73
- async execute({ user, account, data }) {
74
- await Account.create({
75
- ...data,
76
- id: account,
77
- user
78
- })
79
- }
80
- })
81
-
82
- definition.event({
83
- name: "accountDisconnected",
84
- properties: {
85
- account: {
86
- type: String,
87
- validation: ['nonEmpty']
88
- },
89
- user: {
90
- type: User,
91
- validation: ['nonEmpty']
92
- }
93
- },
94
- async execute({ account }) {
95
- await Account.delete(account)
96
- }
97
- })
98
-
99
- definition.trigger({
100
- name: "signInGoogle",
101
- properties: {
102
- account: {
103
- type: String,
104
- validation: ['nonEmpty']
105
- }
106
- },
107
- async execute({ account, session }, { service }, _emit) {
108
- const accountData = await Account.get(account)
109
- if(!accountData) throw { properties: { email: 'notFound' } }
110
- const { user } = accountData
111
- return service.trigger({ type: 'signIn' }, {
112
- user, session
113
- })
114
- }
115
- })
116
-
117
- async function downloadData(user, data, service) {
118
- await service.trigger({ type: 'setIdentification' }, {
119
- sessionOrUserType: 'user',
120
- sessionOrUser: user,
121
- overwrite: false,
122
- name: data.name,
123
- givenName: data.given_name,
124
- firstName: data.given_name,
125
- familyName: data.family_name,
126
- lastName: data.family_name,
127
- })
128
- if(data.picture) {
129
- const downloadAndUpdateImage = (async () => {
130
- const picture = await service.trigger({ service: 'pictures', type: "createPictureFromUrl" }, {
131
- ownerType: 'user_User',
132
- owner: user,
133
- name: "google-profile-picture",
134
- purpose: "users-updatePicture-picture",
135
- url: data.picture,
136
- cropped: true
137
- })
138
- await service.trigger({ type: 'setIdentification' }, {
139
- sessionOrUserType: 'user',
140
- sessionOrUser: user,
141
- overwrite: false,
142
- picture
143
- })
144
- })
145
- downloadAndUpdateImage()
146
- }
147
- if(data.email_verified) {
148
- await service.trigger({ type: 'connectEmail' }, {
149
- email: data.email,
150
- user
151
- })
152
- }
153
- }
154
-
155
- definition.trigger({
156
- name: "connectGoogle",
157
- properties: {
158
- user: {
159
- type: User,
160
- validation: ['nonEmpty']
161
- },
162
- account: {
163
- type: String,
164
- validation: ['nonEmpty']
165
- },
166
- data: {
167
- type: Object,
168
- properties: googleProperties,
169
- validation: ['nonEmpty']
170
- },
171
- transferOwnership: {
172
- type: Boolean,
173
- default: false
174
- }
175
- },
176
- async execute({ user, account, data, transferOwnership }, { service }, emit) {
177
- const accountData = await Account.get(account)
178
- if(accountData) {
179
- if(accountData.user !== user) {
180
- if(transferOwnership) {
181
- emit({
182
- 'type': 'userOwnedAccountTransferred',
183
- account, to: user
184
- })
185
- await downloadData(user, data, service)
186
- return
187
- }
188
- throw 'alreadyConnectedElsewhere'
189
- }
190
- throw 'alreadyConnected'
191
- }
192
- emit({
193
- type: 'accountConnected',
194
- account, user, data
195
- })
196
- await service.trigger({ type: 'googleConnected' }, {
197
- user, account, data
198
- })
199
- await downloadData(user, data, service)
200
- }
201
- })
202
-
203
- definition.trigger({
204
- name: "disconnectGoogle",
205
- properties: {
206
- account: {
207
- type: String,
208
- validation: ['nonEmpty']
209
- }
210
- },
211
- async execute({ account }, { client, service }, emit) {
212
- const accountData = await Account.get(account)
213
- if(!accountData) throw 'notFound'
214
- const { user } = accountData
215
- emit({
216
- type: 'accountDisconnected',
217
- account, user
218
- })
219
- await service.trigger({ type: 'googleDisconnected' }, {
220
- user, account
221
- })
222
- }
223
- })
224
-
225
- definition.action({
226
- name: "signIn",
227
- properties: {
228
- accessToken: {
229
- type: String
230
- }
231
- },
232
- returns: {
233
- type: User,
234
- idOnly: true
235
- },
236
- waitForEvents: true,
237
- async execute({ accessToken }, { client, service }, emit) {
238
- const ticket = await googleClient.verifyIdToken({
239
- idToken: accessToken,
240
- audience: googleClientId
241
- })
242
- const googleUser = ticket.getPayload()
243
- debug("GOOGLE USER", googleUser)
244
- const account = googleUser.sub
245
- const existingLogin = await Account.get(account)
246
- const { session } = client
247
- if(existingLogin) { /// Sign In
248
- const { user } = existingLogin
249
- await service.trigger({ type: 'signIn' }, {
250
- user, session
251
- })
252
- return {
253
- action: 'signIn',
254
- user
255
- }
256
- } else { // Sign up
257
- throw 'notFound'
258
- }
259
- }
260
- })
261
-
262
- definition.action({
263
- name: "signUp",
264
- properties: {
265
- accessToken: {
266
- type: String
267
- }
268
- },
269
- returns: {
270
- type: User,
271
- idOnly: true
272
- },
273
- waitForEvents: true,
274
- async execute({ accessToken }, { client, service }, emit) {
275
- const ticket = await googleClient.verifyIdToken({
276
- idToken: accessToken,
277
- audience: googleClientId
278
- })
279
- const googleUser = ticket.getPayload()
280
- debug("GOOGLE USER", googleUser)
281
- const account = googleUser.sub
282
- const existingLogin = await Account.get(account)
283
- const { session } = client
284
- if(existingLogin) { /// Sign In
285
- throw 'alreadyConnected'
286
- } else { // Sign up
287
- const user = app.generateUid()
288
- await service.trigger({ type: 'connectGoogle' }, {
289
- user, account, data: googleUser
290
- })
291
- await service.trigger({ type: 'signUpAndSignIn' }, {
292
- user, session
293
- })
294
- return {
295
- action: 'signUp',
296
- user
297
- }
298
- }
299
- }
300
- })
301
-
302
- definition.action({
303
- name: "signInOrSignUp",
304
- properties: {
305
- accessToken: {
306
- type: String
307
- }
308
- },
309
- returns: {
310
- type: User,
311
- idOnly: true
312
- },
313
- waitForEvents: true,
314
- async execute({ accessToken }, { client, service }, emit) {
315
- const ticket = await googleClient.verifyIdToken({
316
- idToken: accessToken,
317
- audience: googleClientId
318
- })
319
- const googleUser = ticket.getPayload()
320
- debug("GOOGLE USER", googleUser)
321
- const account = googleUser.sub
322
- const existingLogin = await Account.get(account)
323
- const { session } = client
324
- if(existingLogin) { /// Sign In
325
- const { user } = existingLogin
326
- await service.trigger({ type: 'signIn' }, {
327
- user, session
328
- })
329
- return {
330
- action: 'signIn',
331
- user: existingLogin.user
332
- }
333
- } else { // Sign up
334
- const user = app.generateUid()
335
- await service.trigger({ type: 'connectGoogle' }, {
336
- user, account, data: googleUser
337
- })
338
- await service.trigger({ type: 'signUpAndSignIn' }, {
339
- user, session
340
- })
341
- return {
342
- action: 'signUp',
343
- user
344
- }
345
- }
346
- }
347
- })
348
-
349
- definition.action({
350
- name: "connectGoogle",
351
- properties: {
352
- accessToken: {
353
- type: String
354
- },
355
- transferOwnership: {
356
- type: Boolean,
357
- default: false
358
- }
359
- },
360
- async execute({ accessToken, transferOwnership }, { client, service }, emit) {
361
- const ticket = await googleClient.verifyIdToken({
362
- idToken: accessToken,
363
- audience: googleClientId
364
- })
365
- const user = client.user
366
- if(!user) throw 'notAuthorized'
367
- const googleUser = ticket.getPayload()
368
- debug("GOOGLE USER", googleUser)
369
- const account = googleUser.sub
370
- await service.trigger({ type: 'connectGoogle' }, {
371
- user, account, data: googleUser,
372
- transferOwnership
373
- })
374
- return {
375
- action: 'connectGoogle',
376
- user
377
- }
378
- }
379
- })
380
-
381
- definition.action({
382
- name: "disconnectGoogle",
383
- properties: {
384
- account: {
385
- type: String,
386
- validation: ['nonEmpty']
387
- }
388
- },
389
- async execute({ account }, { client, service }, emit) {
390
- const accountData = await Account.get(account)
391
- if(!accountData) throw 'notFound'
392
- const { user } = accountData
393
- if(user !== client.user) throw 'notAuthorized'
394
- await service.trigger({ type: 'disconnectGoogle' }, {
395
- user, account
396
- })
397
- }
398
- })
399
11
 
400
12
  export default definition
@@ -0,0 +1,87 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+ import definition from './definition.js'
4
+ import Debug from 'debug'
5
+ const debug = Debug('services:googleAuthentication')
6
+
7
+ export const OfflineAccess = definition.model({
8
+ name: "OfflineAccess",
9
+ userItem: {
10
+ userReadAccess: () => true
11
+ },
12
+ properties: {
13
+ scope: {
14
+ type: String,
15
+ validation: ['nonEmpty']
16
+ },
17
+ refreshToken: {
18
+ type: String,
19
+ validation: ['nonEmpty']
20
+ },
21
+ lastUse: {
22
+ type: Date
23
+ },
24
+ lastRefresh: {
25
+ type: Date
26
+ }
27
+ },
28
+ indexes: {
29
+ byUserAndScope: {
30
+ property: ['user', 'scope']
31
+ }
32
+ }
33
+ })
34
+
35
+ definition.view({
36
+ name: 'myOfflineAccessByScope',
37
+ properties: {
38
+ scope: {
39
+ type: String
40
+ }
41
+ },
42
+ returns: {
43
+ type: OfflineAccess
44
+ },
45
+ access: (params, { client, service }) => {
46
+ return !!client.user
47
+ },
48
+ async daoPath({ scope }, { client }) {
49
+ return OfflineAccess.indexObjectPath('byUserAndScope', [client.user, scope])
50
+ }
51
+ })
52
+
53
+ definition.event({
54
+ name: "offlineAccessSaved",
55
+ async execute({ user, refreshToken, timestamp }) {
56
+ await OfflineAccess.create({ id: user, user, refreshToken, lastUse: timestamp })
57
+ }
58
+ })
59
+
60
+ definition.event({
61
+ name: "offlineAccessDeleted",
62
+ async execute({ user }) {
63
+ await OfflineAccess.delete(user)
64
+ }
65
+ })
66
+
67
+ definition.event({
68
+ name: "offlineAccessUsed",
69
+ async execute({ user, timestamp }) {
70
+ await OfflineAccess.update(user, { lastUse: timestamp })
71
+ }
72
+ })
73
+
74
+ definition.event({
75
+ name: "offlineAccessRefreshed",
76
+ async execute({ user, timestamp }) {
77
+ await OfflineAccess.update(user, { lastRefresh: timestamp })
78
+ }
79
+ })
80
+
81
+ definition.event({
82
+ name: "offlineAccessDeleted",
83
+ async execute({ user }) {
84
+ await ApiAccess.delete(user)
85
+ }
86
+ })
87
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/google-authentication-service",
3
- "version": "0.8.33",
3
+ "version": "0.8.34",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -21,10 +21,10 @@
21
21
  "url": "https://www.viamage.com/"
22
22
  },
23
23
  "dependencies": {
24
- "@live-change/framework": "^0.8.33",
25
- "@live-change/relations-plugin": "^0.8.33",
24
+ "@live-change/framework": "^0.8.34",
25
+ "@live-change/relations-plugin": "^0.8.34",
26
26
  "google-auth-library": "9.0.0"
27
27
  },
28
- "gitHead": "98ff6f9c09e5fc1f408010df6cc8038eff571276",
28
+ "gitHead": "40e61928bf43b35352c76fc135f36a2d8bd76c4a",
29
29
  "type": "module"
30
30
  }
package/sign.js ADDED
@@ -0,0 +1,160 @@
1
+ import App from '@live-change/framework'
2
+ const app = App.app()
3
+ import definition from './definition.js'
4
+
5
+ import Debug from 'debug'
6
+ const debug = Debug('services:googleAuthentication')
7
+
8
+ import { User, googleProperties, Account } from './account.js'
9
+ import { getTokensWithCode, googleClientId, getUserInfo } from './googleClient.js'
10
+
11
+ definition.trigger({
12
+ name: "signInGoogle",
13
+ properties: {
14
+ account: {
15
+ type: String,
16
+ validation: ['nonEmpty']
17
+ }
18
+ },
19
+ async execute({ account, session }, { service }, _emit) {
20
+ const accountData = await Account.get(account)
21
+ if(!accountData) throw { properties: { email: 'notFound' } }
22
+ const { user } = accountData
23
+ return service.trigger({ type: 'signIn' }, {
24
+ user, session
25
+ })
26
+ }
27
+ })
28
+
29
+ definition.action({
30
+ name: "signIn",
31
+ properties: {
32
+ code: {
33
+ type: String,
34
+ validation: ['nonEmpty']
35
+ },
36
+ redirectUri: {
37
+ type: String,
38
+ validation: ['nonEmpty']
39
+ },
40
+ },
41
+ returns: {
42
+ type: User,
43
+ idOnly: true
44
+ },
45
+ waitForEvents: true,
46
+ async execute({ code, redirectUri }, { client, service }, emit) {
47
+ const tokens = await getTokensWithCode(code, redirectUri)
48
+ debug("TOKENS", tokens)
49
+ const googleUser = await getUserInfo(tokens.access_token)
50
+ debug("GOOGLE USER", googleUser)
51
+ const account = googleUser.sub
52
+ const existingLogin = await Account.get(account)
53
+ const { session } = client
54
+ if(existingLogin) { /// Sign In
55
+ const { user } = existingLogin
56
+ await service.trigger({ type: 'signIn' }, {
57
+ user, session
58
+ })
59
+ return {
60
+ action: 'signIn',
61
+ user
62
+ }
63
+ } else { // Sign up
64
+ throw 'notFound'
65
+ }
66
+ }
67
+ })
68
+
69
+ definition.action({
70
+ name: "signUp",
71
+ properties: {
72
+ code: {
73
+ type: String,
74
+ validation: ['nonEmpty']
75
+ },
76
+ redirectUri: {
77
+ type: String,
78
+ validation: ['nonEmpty']
79
+ },
80
+ },
81
+ returns: {
82
+ type: User,
83
+ idOnly: true
84
+ },
85
+ waitForEvents: true,
86
+ async execute({ accessToken }, { client, service }, emit) {
87
+ const tokens = await getTokensWithCode(code, redirectUri)
88
+ debug("TOKENS", tokens)
89
+ const googleUser = await getUserInfo(tokens.access_token)
90
+ debug("GOOGLE USER", googleUser)
91
+ const account = googleUser.sub
92
+ const existingLogin = await Account.get(account)
93
+ const { session } = client
94
+ if(existingLogin) { /// Sign In
95
+ throw 'alreadyConnected'
96
+ } else { // Sign up
97
+ const user = app.generateUid()
98
+ await service.trigger({ type: 'connectGoogle' }, {
99
+ user, account, data: googleUser
100
+ })
101
+ await service.trigger({ type: 'signUpAndSignIn' }, {
102
+ user, session
103
+ })
104
+ return {
105
+ action: 'signUp',
106
+ user
107
+ }
108
+ }
109
+ }
110
+ })
111
+
112
+ definition.action({
113
+ name: "signInOrSignUp",
114
+ properties: {
115
+ code: {
116
+ type: String,
117
+ validation: ['nonEmpty']
118
+ },
119
+ redirectUri: {
120
+ type: String,
121
+ validation: ['nonEmpty']
122
+ },
123
+ },
124
+ returns: {
125
+ type: User,
126
+ idOnly: true
127
+ },
128
+ waitForEvents: true,
129
+ async execute({ code, redirectUri }, { client, service }, emit) {
130
+ const tokens = await getTokensWithCode(code, redirectUri)
131
+ debug("TOKENS", tokens)
132
+ const googleUser = await getUserInfo(tokens.access_token)
133
+ debug("GOOGLE USER", googleUser)
134
+ const account = googleUser.sub
135
+ const existingLogin = await Account.get(account)
136
+ const { session } = client
137
+ if(existingLogin) { /// Sign In
138
+ const { user } = existingLogin
139
+ await service.trigger({ type: 'signIn' }, {
140
+ user, session
141
+ })
142
+ return {
143
+ action: 'signIn',
144
+ user: existingLogin.user
145
+ }
146
+ } else { // Sign up
147
+ const user = app.generateUid()
148
+ await service.trigger({ type: 'connectGoogle' }, {
149
+ user, account, data: googleUser
150
+ })
151
+ await service.trigger({ type: 'signUpAndSignIn' }, {
152
+ user, session
153
+ })
154
+ return {
155
+ action: 'signUp',
156
+ user
157
+ }
158
+ }
159
+ }
160
+ })