forked from auth0/rules
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrules.json
639 lines (639 loc) · 95.2 KB
/
rules.json
1
2
3
4
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
[
{
"name": "access control",
"templates": [
{
"id": "access-on-weekdays-only-for-an-app",
"title": "Allow Access during weekdays for a specific App",
"categories": [
"access control"
],
"description": "<p>This rule is used to prevent access during weekends for a specific app.</p>\n",
"code": "function (user, context, callback) {\n\n if (context.clientName === 'TheAppToCheckAccessTo') {\n var d = new Date().getDay();\n\n if (d === 0 || d === 6) {\n return callback(new UnauthorizedError('This app is available during the week'));\n }\n }\n\n callback(null, user, context);\n}"
},
{
"id": "active-directory-groups",
"title": "Active Directory group membership",
"categories": [
"access control"
],
"description": "<p>This rule checks if a user belongs to an AD group and if not, it will return Access Denied.</p>\n<blockquote>\n<p>Note: you can mix this with <code>context.clientID</code> or <code>clientName</code> to do it only for specific application</p>\n</blockquote>\n",
"code": "function (user, context, callback) {\n var groupAllowed = 'group1';\n var userHasAccess = user.groups.some(\n function (group) {\n return groupAllowed === group;\n });\n\n if (!userHasAccess) {\n return callback(new UnauthorizedError('Access denied.'));\n }\n\n callback(null, user, context);\n}"
},
{
"id": "check-domains-against-connection-aliases",
"title": "Check user email domain matches domains configured in connection",
"categories": [
"access control"
],
"description": "<p>This rule will check that the email the user has used to login matches any of the domains configured in a connection. If there are no domains configured, it will allow access.</p>\n<blockquote>\n<p>Note: this rule uses the Auth0 Management API v2. You need to get a token from the <a href=\"https://auth0.com/docs/api/management/v2\">API explorer</a>. The required scope is <code>read:connections</code>.</p>\n</blockquote>\n",
"code": "function (user, context, callback) {\n request('https://login.auth0.com/api/v2/connections', {\n headers: {\n Authorization: 'Bearer ' + configuration.AUTH0_API_TOKEN //TODO: replace with your own Auth0 APIv2 token\n } \n },\n function(e,r,b){\n if(e) return callback(e);\n\n var connections = JSON.parse(b);\n var connection = connections[_.findIndex(connections,function(c){\n return (c.name === context.connection);\n })];\n\n //No domains -> access allowed\n if( !connection.options.tenant_domain ) {\n return callback(null, user, context);\n }\n\n //Access allowed if domains is found.\n if( _.findIndex(connection.options.domain_aliases,function(d){\n return user.email.indexOf(d) >= 0; \n }) >= 0 ) return callback(null, user, context);\n\n return callback('Access denied');\n });\n}"
},
{
"id": "check_last_password_reset",
"title": "Check last password reset",
"categories": [
"access control"
],
"description": "<p>This rule will check the last time that a user changed his or her account password.</p>\n",
"code": "function (user, context, callback) {\n\n function daydiff (first, second) {\n return (second-first)/(1000*60*60*24);\n }\n\n var last_password_change = user.last_password_reset || user.created_at;\n\n if (daydiff(new Date(last_password_change), new Date()) > 30) {\n return callback(new UnauthorizedError('please change your password'));\n }\n\n callback(null, user, context);\n}"
},
{
"id": "custom-scopes",
"title": "Custom authorization scopes",
"categories": [
"access control"
],
"description": "<p>This rule maps arbitrary <code>scope</code> values to actual properties in the user profile.</p>\n",
"code": "function (user, context, callback) {\n // The currently requested scopes can be accessed as follows:\n // context.request.query.scope.match(/\\S+/g)\n var scopeMapping = {\n contactInfo: [\"name\", \"email\", \"company\"],\n publicInfo: [\"public_repos\", \"public_gists\"]\n };\n context.jwtConfiguration.scopes = scopeMapping;\n callback(null, user, context);\n}"
},
{
"id": "disable-resource-owner",
"title": "Disable the Resource Owner endpoint",
"categories": [
"access control"
],
"description": "<p>This rule is used to disable the Resource Owner endpoint (to prevent users from bypassing MFA policies).</p>\n",
"code": "function (user, context, callback) {\n if (context.protocol === 'oauth2-resource-owner') {\n return callback(\n new UnauthorizedError('The resource owner endpoint cannot be used.'));\n }\n callback(null, user, context);\n}"
},
{
"id": "disable-social-signup",
"title": "Disable social signups",
"categories": [
"access control"
],
"description": "<p>This rule is used to prevent signups using social connections.</p>\n",
"code": "function (user, context, callback) {\n\n var CLIENTS_ENABLED = ['REPLACE_WITH_YOUR_CLIENT_ID'];\n // run only for the specified clients\n if (CLIENTS_ENABLED.indexOf(context.clientID) === -1) {\n return callback(null, user, context);\n }\n\n // initialize app_metadata\n user.app_metadata = user.app_metadata || {};\n\n // if it is the first login (hence the `signup`) and it is a social login\n if (context.stats.loginsCount === 1 && user.identities[0].isSocial) {\n\n // turn on the flag\n user.app_metadata.is_signup = true; \n\n // store the app_metadata\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then(function(){\n // throw error\n return callback('Signup disabled');\n })\n .catch(function(err){\n callback(err);\n });\n\n return;\n } \n\n // if flag is enabled, throw error\n if (user.app_metadata.is_signup) {\n return callback('Signup disabled');\n }\n\n // else it is a non social login or it is not a signup\n callback(null, user, context);\n}"
},
{
"id": "dropbox-whitelist",
"title": "Whitelist on the cloud",
"categories": [
"access control"
],
"description": "<p>This rule denies/grant access to users based on a list of emails stored in Dropbox.</p>\n",
"code": "function (user, context, callback) {\n request.get({\n url: 'https://dl.dropboxusercontent.com/u/21665105/users.txt'\n }, function (err, response, body) {\n var whitelist = body.split('\\r\\n');\n\n var userHasAccess = whitelist.some(function (email) {\n return email === user.email;\n });\n\n if (!userHasAccess) {\n return callback(new UnauthorizedError('Access denied.'));\n }\n\n callback(null, user, context);\n });\n}"
},
{
"id": "email-verified",
"title": "Force email verification",
"categories": [
"access control"
],
"description": "<p>This rule will only allow access users that have verified their emails.\nNote that it might be a better UX to make this verification from your application.</p>\n<p>If you are using <a href=\"https://auth0.com/docs/lock\">Lock</a>, the default behavior is to log in a user immediately after they have signed up.\nTo prevent this from immediately displaying an error to the user, you can pass the following option to <code>lock.show()</code> or similar: <code>loginAfterSignup: false</code>.\nIf you are using <a href=\"https://auth0.com/docs/libraries/auth0js\">auth0.js</a>, the equivalent option is <code>auto_login: false</code>.</p>\n",
"code": "function (user, context, callback) {\n if (!user.email_verified) {\n return callback(new UnauthorizedError('Please verify your email before logging in.'));\n } else {\n return callback(null, user, context);\n }\n}"
},
{
"id": "ip-address-whitelist",
"title": "IP Address whitelist",
"categories": [
"access control"
],
"description": "<p>This rule will only allow access to an app from a specific set of IP addresses</p>\n",
"code": "function (user, context, callback) {\n var whitelist = ['1.2.3.4', '2.3.4.5']; //authorized IPs\n var userHasAccess = whitelist.some(\n function (ip) {\n return context.request.ip === ip;\n });\n\n if (!userHasAccess) {\n return callback(new UnauthorizedError('Access denied from this IP address.'));\n }\n\n return callback(null, user, context);\n}"
},
{
"id": "link-users-by-email-with-metadata",
"title": "Link Accounts with Same Email Address while Merging Metadata",
"categories": [
"access control"
],
"description": "<p>This rule will link any accounts that have the same email address while merging metadata.</p>\n",
"code": "function (user, context, callback) {\n var request = require('[email protected]');\n var async = require('[email protected]');\n\n // Check if email is verified, we shouldn't automatically\n // merge accounts if this is not the case.\n if (!user.email_verified) {\n return callback(null, user, context);\n }\n var userApiUrl = auth0.baseUrl + '/users';\n\n request({\n url: userApiUrl,\n headers: {\n Authorization: 'Bearer ' + auth0.accessToken\n },\n qs: {\n search_engine: 'v2',\n q: 'email.raw:\"' + user.email + '\" AND email_verified: \"true\" -user_id:\"' + user.user_id + '\"',\n }\n },\n function(err, response, body) {\n if (err) return callback(err);\n if (response.statusCode !== 200) return callback(new Error(body));\n\n var data = JSON.parse(body);\n if (data.length > 1) {\n return callback(new Error('[!] Rule: Multiple user profiles already exist - cannot select base profile to link with'));\n }\n if (data.length === 0) {\n console.log('[-] Skipping link rule');\n return callback(null, user, context);\n }\n\n var originalUser = data[0];\n var provider = user.identities[0].provider;\n var providerUserId = user.identities[0].user_id;\n\n user.app_metadata = user.app_metadata || {};\n user.user_metadata = user.user_metadata || {};\n auth0.users.updateAppMetadata(originalUser.user_id, user.app_metadata)\n .then(auth0.users.updateUserMetadata(originalUser.user_id, user.user_metadata))\n .then(function(){\n request.post({\n url: userApiUrl + '/' + originalUser.user_id + '/identities',\n headers: {\n Authorization: 'Bearer ' + auth0.accessToken\n },\n json: { provider: provider, user_id: providerUserId }\n }, function(err, response, body) {\n if (response && response.statusCode >= 400) {\n return cb(new Error('Error linking account: ' + response.statusMessage));\n }\n context.primaryUser = originalUser.user_id;\n callback(null, user, context);\n });\n })\n .catch(function(err){\n cb(err);\n });\n });\n}"
},
{
"id": "link-users-by-email",
"title": "Link Accounts with Same Email Address",
"categories": [
"access control"
],
"description": "<p>This rule will link any accounts that have the same email address.</p>\n<blockquote>\n<p>Note: When linking accounts, only the metadata of the target user is saved. If you want to merge the metadata of the two accounts you must do that manually. See the document on <a href=\"https://auth0.com/docs/link-accounts\">Linking Accounts</a> for more details.</p>\n</blockquote>\n",
"code": "function (user, context, callback) {\n var request = require('[email protected]');\n // Check if email is verified, we shouldn't automatically\n // merge accounts if this is not the case.\n if (!user.email_verified) {\n return callback(null, user, context);\n }\n var userApiUrl = auth0.baseUrl + '/users';\n\n request({\n url: userApiUrl,\n headers: {\n Authorization: 'Bearer ' + auth0.accessToken\n },\n qs: {\n search_engine: 'v2',\n q: 'email.raw:\"' + user.email + '\" AND email_verified: \"true\" -user_id:\"' + user.user_id + '\"',\n }\n },\n function(err, response, body) {\n if (err) return callback(err);\n if (response.statusCode !== 200) return callback(new Error(body));\n\n var data = JSON.parse(body);\n if (data.length > 1) {\n return callback(new Error('[!] Rule: Multiple user profiles already exist - cannot select base profile to link with'));\n }\n if (data.length === 0) {\n console.log('[-] Skipping link rule');\n return callback(null, user, context);\n }\n\n var originalUser = data[0];\n var provider = user.identities[0].provider;\n var providerUserId = user.identities[0].user_id;\n\n request.post({\n url: userApiUrl + '/' + originalUser.user_id + '/identities',\n headers: {\n Authorization: 'Bearer ' + auth0.accessToken\n },\n json: {\n provider: provider,\n user_id: providerUserId\n }\n }, function(err, response, body) {\n if (response.statusCode >= 400) {\n return callback(new Error('Error linking account: ' + response.statusMessage));\n }\n context.primaryUser = originalUser.user_id;\n callback(null, user, context);\n });\n });\n}"
},
{
"id": "roles-creation",
"title": "Set roles to a user",
"categories": [
"access control"
],
"description": "<p>This rule adds a Roles field to the user based on some pattern</p>\n",
"code": "function (user, context, callback) {\n user.app_metadata = user.app_metadata || {};\n // You can add a Role based on what you want\n // In this case I check domain\n var addRolesToUser = function(user, cb) {\n if (user.email && user.email.indexOf('@example.com') > -1) {\n cb(null, ['admin']);\n } else {\n cb(null, ['user']);\n }\n };\n\n addRolesToUser(user, function(err, roles) {\n if (err) {\n callback(err);\n } else {\n user.app_metadata.roles = roles;\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then(function(){\n context.idToken['https://example.com/roles'] = user.app_metadata.roles;\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n }\n });\n}"
},
{
"id": "simple-domain-whitelist",
"title": "Email domain whitelist",
"categories": [
"access control"
],
"description": "<p>This rule will only allow access to users with specific email domains.</p>\n",
"code": "function (user, context, callback) {\n var whitelist = ['example.com', 'example.org']; //authorized domains\n var userHasAccess = whitelist.some(\n function (domain) {\n var emailSplit = user.email.split('@');\n return emailSplit[emailSplit.length - 1].toLowerCase() === domain;\n });\n\n if (!userHasAccess) {\n return callback(new UnauthorizedError('Access denied.'));\n }\n\n return callback(null, user, context);\n}"
},
{
"id": "simple-user-whitelist-for-app",
"title": "Whitelist for a Specific App",
"categories": [
"access control"
],
"description": "<p>This rule will only allow access to users with specific email addresses on a specific app.</p>\n",
"code": "function (user, context, callback) {\n //we just care about NameOfTheAppWithWhiteList\n //bypass this rule for every other app\n if(context.clientName !== 'NameOfTheAppWithWhiteList'){\n return callback(null, user, context);\n }\n\n var whitelist = [ '[email protected]', '[email protected]' ]; //authorized users\n var userHasAccess = whitelist.some(\n function (email) {\n return email === user.email;\n });\n\n if (!userHasAccess) {\n return callback(new UnauthorizedError('Access denied.'));\n }\n\n callback(null, user, context);\n}"
},
{
"id": "simple-user-whitelist",
"title": "Whitelist",
"categories": [
"access control"
],
"description": "<p>This rule will only allow access to users with specific email addresses.</p>\n",
"code": "function (user, context, callback) {\n var whitelist = [ '[email protected]', '[email protected]' ]; //authorized users\n var userHasAccess = whitelist.some(\n function (email) {\n return email === user.email;\n });\n\n if (!userHasAccess) {\n return callback(new UnauthorizedError('Access denied.'));\n }\n\n callback(null, user, context);\n}"
},
{
"id": "simple-whitelist-on-a-connection",
"title": "Whitelist on Specific Connection",
"categories": [
"access control"
],
"description": "<p>This rule will only allow access to certain users coming from a specific connection (e.g. fitbit).</p>\n",
"code": "function (user, context, callback) {\n\n // We check users only authenticated with 'fitbit'\n if(context.connection === 'fitbit'){\n\n var whitelist = [ 'user1', 'user2' ]; //authorized users\n var userHasAccess = whitelist.some(\n function (name) {\n return name === user.name;\n });\n\n if (!userHasAccess) {\n return callback(new UnauthorizedError('Access denied.'));\n }\n }\n\n callback(null, user, context);\n}"
},
{
"id": "thisdata-deny-anomalies",
"title": "Account Takeover Prevention via ThisData",
"categories": [
"access control"
],
"description": "<p>This rule is designed to detect phished or compromised user accounts and block\nattackers from logging in to hacked accounts. Even if the primary user\nauthentication is approved (e.g. correct username and password) it will deny\naccess if the login appears to be highly suspicious.</p>\n<p>It uses <a href=\"https://thisdata.com\">ThisData's</a> anomaly detection\nalgorithms which take into account many behavioral factors including:</p>\n<ul>\n<li>Location & Velocity</li>\n<li>Devices</li>\n<li>Time of day</li>\n<li>Tor usage</li>\n<li>Risky IP addresses</li>\n<li>And more...</li>\n</ul>\n<p>This rule uses ThisData's API to get a risk score for the login, and then blocks\nthe login by raising an <code>UnauthorizedError</code> error if the risk is very high.</p>\n<p>You will need a ThisData API Key. Sign up for a free ThisData\naccount at <a href=\"https://thisdata.com/sign-up\">https://thisdata.com/sign-up</a></p>\n<p><strong>Important</strong> This rule should be used with the "Account Takeover Detection via ThisData"\nAuth0 rule, which allows you to teach our algorithms about your users. Using\nboth rules allows you to achieve results of higher accuracy.</p>\n<p>Read our guide "How to add login anomaly detection to Auth0"\n <a href=\"https://thisdata.com/blog/how-to-add-login-anomaly-detection-to-auth0/\">https://thisdata.com/blog/how-to-add-login-anomaly-detection-to-auth0/</a></p>\n<p>Contact ThisData: [email protected]</p>\n",
"code": "function (user, context, callback) {\n // Get this from your ThisData account\n var apiKey = configuration.THISDATA_API_KEY;\n\n // 0.85 will generally block irregular Tor usage\n // or sudden changes in location and device\n var riskLimit = 0.85;\n\n var options = {\n method: 'POST',\n headers: {\n 'User-Agent': 'thisdata-auth0'\n },\n uri: 'https://api.thisdata.com/v1/verify?api_key=' + apiKey,\n json: {\n ip: context.request.ip,\n user_agent: context.request.userAgent,\n user: {\n id: user.user_id,\n name: user.name,\n email: user.email\n }\n }\n };\n\n request.post(options, function(e, r, b){\n if(e || r.statusCode !== 200){\n // If anything fails dont block the login\n callback(null, user, context);\n } else {\n\n // If the risk is high then block the login\n if(b.score >= riskLimit){\n return callback(new UnauthorizedError('Login anomaly detected by ThisData. Risk: ' + b.score));\n }\n\n callback(null, user, context);\n }\n });\n}"
}
]
},
{
"name": "enrich profile",
"templates": [
{
"id": "add-attributes",
"title": "Add attributes to a user for specific connection",
"categories": [
"enrich profile"
],
"description": "<p>This rule will add an attribute to the user only for the login transaction (i.e. they won't be persisted to the user). This is useful for cases where you want to enrich the user information for a specific application.</p>\n",
"code": "function (user, context, callback) {\n if (context.connection === 'company.com') {\n context.idToken['https://example.com/vip'] = true;\n }\n\n callback(null, user, context);\n}"
},
{
"id": "add-country",
"title": "Add country to the user profile",
"categories": [
"enrich profile"
],
"description": "<p>This rule will add a <code>country</code> attribute to the user based on their ip address.</p>\n",
"code": "function (user, context, callback) {\n if (context.request.geoip) {\n context.idToken['https://example.com/country'] = context.request.geoip.country_name;\n context.idToken['https://example.com/timezone'] = context.request.geoip.time_zone;\n }\n\n // Example geoip object:\n // \"geoip\": {\n // \"country_code\": \"AR\",\n // \"country_code3\": \"ARG\",\n // \"country_name\": \"Argentina\",\n // \"region\": \"05\",\n // \"city\": \"Cordoba\",\n // \"latitude\": -31.41349983215332,\n // \"longitude\": -64.18109893798828,\n // \"continent_code\": \"SA\",\n // \"time_zone\": \"America/Argentina/Cordoba\"\n // }\n\n callback(null, user, context);\n}"
},
{
"id": "add-income",
"title": "Add zipcode median household income to the user profile",
"categories": [
"enrich profile"
],
"description": "<p>This rule will add an <code>income</code> (median household income) attribute to the user based on the zipcode of their ip address. It is based on the last US Census data (not available for other countries).</p>\n",
"code": "function (user, context, callback) {\n\n user.user_metadata = user.user_metadata || {};\n var geoip = user.user_metadata.geoip || context.request.geoip;\n\n if (!geoip || geoip.country_code !== 'US') return callback(null, user, context);\n\n if(global.incomeData === undefined) {\n retrieveIncomeData(user, geoip, context, callback);\n } else {\n setIncomeData(global.incomeData, user, geoip, context, callback);\n }\n\n function retrieveIncomeData(user, geoip, context, callback) {\n request({\n url: 'http://cdn.auth0.com/zip-income/householdincome.json'\n }, function (e,r,b) {\n if(e) return callback(e);\n if(r.statusCode===200){\n var incomeData = JSON.parse(b);\n global.incomeData = incomeData;\n setIncomeData(incomeData,user,context, callback);\n }\n callback(null, user, context);\n });\n }\n\n function setIncomeData(incomeData, user, geoip, context, callback) {\n if (incomeData[geoip.postal_code]) {\n\n user.user_metadata.zipcode_income = incomeData[geoip.postal_code];\n context.idToken['https://example.com/zipcode_income'] = incomeData[geoip.postal_code];\n\n auth0.users.updateUserMetadata(user.user_id, user.user_metadata)\n .then(function(){\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n }\n }\n}"
},
{
"id": "add-persistent-attributes",
"title": "Add persistent attributes to the user",
"categories": [
"enrich profile"
],
"description": "<p>This rule count set the default color (an example preference) to a user (using user_metadata`).</p>\n",
"code": "function (user, context, callback) {\n user.user_metadata = user.user_metadata || {};\n user.user_metadata.color = user.user_metadata.color || 'blue';\n context.idToken['https://example.com/favorite_color'] = user.user_metadata.color;\n\n auth0.users.updateUserMetadata(user.user_id, user.user_metadata)\n .then(function(){\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n}"
},
{
"id": "add-roles-from-sqlserver",
"title": "Add user roles from a SQL Server database",
"categories": [
"enrich profile"
],
"description": "<p>This rule will query a SQL server database on each login and add a <code>roles</code> array to the user.</p>\n<blockquote>\n<p>Note: you can store the connection string securely on Auth0 encrypted configuration. Also make sure when you call an external endpoint to open your firewall/ports to our IP address which you can find it in the rules editor. This happens when you query SQL Azure for example.</p>\n</blockquote>\n",
"code": "function (user, context, callback) {\n getRoles(user.email, function(err, roles) {\n if (err) return callback(err);\n\n context.idToken['https://example.com/roles'] = roles;\n\n callback(null, user, context);\n });\n\n // Queries a table by e-mail and returns associated 'Roles'\n function getRoles(email, done) {\n var connection = sqlserver.connect({\n userName: '<user_name>',\n password: '<password>',\n server: '<db_server_name>',\n options: {\n database: '<db_name>',\n encrypt: true,\n rowCollectionOnRequestCompletion: true\n }\n }).on('errorMessage', function (error) {\n console.log(error.message);\n });\n\n var query = \"SELECT Email, Role \" +\n \"FROM dbo.Role WHERE Email = @email\";\n\n connection.on('connect', function (err) {\n if (err) return done(new Error(err));\n\n var request = new sqlserver.Request(query, function (err, rowCount, rows) {\n if (err) return done(new Error(err));\n\n var roles = rows.map(function (row) {\n return row[1].value;\n });\n\n done(null, roles);\n });\n\n request.addParameter('email', sqlserver.Types.VarChar, email);\n\n connection.execSql(request);\n });\n }\n}"
},
{
"id": "decrypt-sensitive-data",
"title": "Decrypt sensitive data from the user profile",
"categories": [
"enrich profile"
],
"description": "<p>This rule will get a sensitive value in the app_metadata and decrypt it (see the <a href=\"https://auth0.com/rules/encrypt-sensitive-data\">Encrypt sensitive data in the user profile</a> rule for information on how to encrypt the data).</p>\n<p>Note, for this to work you'll need to set 2 configuration settings:</p>\n<ul>\n<li><code>ENCRYPT_PASSWORD</code>, eg: <strong>S0me,Password!è§</strong></li>\n<li><code>ENCRYPT_IV</code>, eg: <strong>abcjfiekdpaifjfd</strong></li>\n</ul>\n<p>And here's an example of how you would decrypt this in .NET:</p>\n",
"code": "function (user, context, callback) {\n user.app_metadata = user.app_metadata || { };\n\n var private_data = decrypt(user.app_metadata.private_data);\n if (private_data.license_key === '1234567') {\n user.role = 'admin';\n }\n\n return callback(null, user, context);\n\n function decrypt(data) {\n if (!data) {\n return { };\n }\n var iv = new Buffer(configuration.ENCRYPT_IV);\n var encodeKey = crypto.createHash('sha256')\n .update(configuration.ENCRYPT_PASSWORD, 'utf-8').digest();\n var cipher = crypto.createDecipheriv('aes-256-cbc', encodeKey, iv);\n var decrypted = cipher.update(data, 'base64', 'utf8') + cipher.final('utf8');\n return JSON.parse(decrypted);\n }\n}"
},
{
"id": "default-picture-null-avatars",
"title": "Default picture for null avatars",
"categories": [
"enrich profile"
],
"description": "<p>Here is how to set a default picture for null avatars via a rule. \nHere's an example that does this for email-based logins:</p>\n",
"code": "function (user, context, callback) {\n if (user.picture.indexOf('cdn.auth0.com') > -1) {\n var url = require('url');\n var u = url.parse(user.picture, true);\n u.query.d = '<URL TO YOUR DEFAULT PICTURE HERE>';\n delete u.search;\n user.picture = url.format(u);\n }\n\n callback(null, user, context);\n}"
},
{
"id": "encrypt-sensitive-data",
"title": "Encrypt sensitive data in the user profile",
"categories": [
"enrich profile"
],
"description": "<p>This rule will set a sensitive value in the app_metadata and encrypt it (see the <a href=\"https://auth0.com/rules/decrypt-sensitive-data\">Decrypt sensitive data from the user profile</a> rule for information on how to decrypt the data).</p>\n<p>The <code>user</code> will look like this after the encryption:</p>\n<p>Note, for this to work you'll need to set two configuration settings. Both should be random strings:</p>\n<ul>\n<li><code>ENCRYPT_PASSWORD</code></li>\n<li><code>ENCRYPT_IV</code></li>\n</ul>\n",
"code": "function (user, context, callback) {\n context.idToken['https://example.com/private_data'] = encrypt({\n license_key: '1234567',\n social_security_number: '56789'\n });\n\n callback(null, user, context);\n\n function encrypt(data) {\n var iv = new Buffer(configuration.ENCRYPT_IV);\n var decodeKey = crypto.createHash('sha256')\n .update(configuration.ENCRYPT_PASSWORD, 'utf-8').digest();\n var cipher = crypto.createCipheriv('aes-256-cbc', decodeKey, iv);\n return cipher.update(JSON.stringify(data || {}), 'utf8', 'base64') + cipher.final('base64');\n }\n}"
},
{
"id": "facebook-custom-picture",
"title": "Use a custom sized profile picture for Facebook connections",
"categories": [
"enrich profile"
],
"description": "<p>This rule will set the <code>picture</code> to a custom size for users who login with Facebook.</p>\n",
"code": "function (user, context, callback) {\n if (context.connection === 'facebook') {\n var fbIdentity = _.find(user.identities, { connection: 'facebook' });\n // See: https://developers.facebook.com/docs/graph-api/reference/user/picture/ for more \n // sizes and types of images that can be returned\n var pictureType = 'large';\n context.idToken.picture = 'https://graph.facebook.com/v2.5/' + fbIdentity.user_id + '/picture?type=' + pictureType;\n }\n callback(null, user, context);\n}"
},
{
"id": "get-FullContact-profile",
"title": "Enrich profile with FullContact",
"categories": [
"enrich profile"
],
"description": "<p>This rule gets the user profile from FullContact using the e-mail (if available). If the information is immediately available (signaled by a <code>statusCode=200</code>), it adds a new property <code>fullContactInfo</code> to the user_metadata and returns. Any other conditions are ignored. See <a href=\"http://www.fullcontact.com/developer/docs/\">FullContact docs</a> for full details.</p>\n",
"code": "function (user, context, callback) {\n var FULLCONTACT_KEY = 'YOUR FULLCONTACT API KEY';\n var SLACK_HOOK = 'YOUR SLACK HOOK URL';\n\n var slack = require('slack-notify')(SLACK_HOOK);\n\n // skip if no email\n if(!user.email) return callback(null, user, context);\n // skip if fullcontact metadata is already there\n if(user.user_metadata && user.user_metadata.fullcontact) return callback(null, user, context);\n request({\n url: 'https://api.fullcontact.com/v2/person.json',\n qs: {\n email: user.email,\n apiKey: FULLCONTACT_KEY\n }\n }, function (error, response, body) {\n if (error || (response && response.statusCode !== 200)) {\n\n slack.alert({\n channel: '#slack_channel',\n text: 'Fullcontact API Error',\n fields: {\n error: error ? error.toString() : (response ? response.statusCode + ' ' + body : '')\n }\n });\n\n // swallow fullcontact api errors and just continue login\n return callback(null, user, context);\n }\n\n\n // if we reach here, it means fullcontact returned info and we'll add it to the metadata\n user.user_metadata = user.user_metadata || {};\n user.user_metadata.fullcontact = JSON.parse(body);\n\n auth0.users.updateUserMetadata(user.user_id, user.user_metadata);\n context.idToken['https://example.com/fullcontact'] = user.user_metadata.fullcontact;\n return callback(null, user, context);\n });\n}"
},
{
"id": "get-getIP",
"title": "Enrich profile with the locations where the user logs in",
"categories": [
"enrich profile"
],
"description": "<p>This rule gets the user locations based on the IP and is added to the <code>user_metadata</code> in the <code>geoip</code> attribute.</p>\n",
"code": "function (user, context, callback) {\n\n user.user_metadata = user.user_metadata || {};\n\n user.user_metadata.geoip = context.request.geoip;\n\n auth0.users.updateUserMetadata(user.user_id, user.user_metadata)\n .then(function(){\n context.idToken['https://example.com/geoip'] = context.request.geoip;\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n}"
},
{
"id": "get-rapLeaf-profile",
"title": "Enrich profile with Rapleaf",
"categories": [
"enrich profile"
],
"description": "<p>This rule gets user information from <strong>rapleaf</strong> using the e-mail (if available). If the information is immediately available (signaled by a <code>statusCode=200</code>), it adds a new property <code>rapLeafInfo</code> to the user profile and returns. Any other conditions are ignored. See <a href=\"http://www.rapleaf.com/developers/personalization-api/\">RapLeaf docs</a> for full details.</p>\n",
"code": "function (user, context, callback) {\n\n //Filter by app\n //if(context.clientName !== 'AN APP') return callback(null, user, context);\n\n var rapLeafAPIKey = 'YOUR RAPLEAF API KEY';\n\n if(user.email){\n return callback(null, user, context);\n }\n\n request({\n url: 'https://personalize.rapleaf.com/v4/dr',\n qs: {\n email: user.email,\n api_key: rapLeafAPIKey\n }\n }, function(err, response, body){\n if(err) return callback(err);\n\n if(response.statusCode===200){\n context.idToken['https://example.com/rapLeafData'] = JSON.parse(body);\n }\n\n return callback(null, user, context);\n });\n\n}"
},
{
"id": "get-twitter-email",
"title": "Get email address from Twitter",
"categories": [
"enrich profile"
],
"description": "<p>NOTE: Further configuration is needed to enable fetching user emails through your Twitter App. Take a look at <a href=\"https://dev.twitter.com/rest/reference/get/account/verify_credentials\">Twitter's doc</a> for specifics.</p>\n<p>The rule which makes the call to Twitter to retrieve the email is as follows. Do not forget to update\n<code>consumerKey</code> and <code>oauthTokenSecret</code> properly.</p>\n<p>This rule will not persist the returned email to the Auth0 user profile, but will return it to your application. If you want to persist the email, it will need to be <a href=\"https://auth0.com/docs/rules/metadata-in-rules#updating-app_metadata\">done with app_metadata as described here</a>. For example, you can save it under app_metadata.social_email. Then, to access it, you can do the following from a rule or the equivalent in your application:</p>\n",
"code": "function (user, context, callback) {\n // additional request below is specific to Twitter\n if (context.connectionStrategy !== 'twitter') {\n return callback(null, user, context);\n }\n\n var oauth = require('oauth-sign');\n var request = require('request');\n var uuid = require('uuid');\n\n var url = 'https://api.twitter.com/1.1/account/verify_credentials.json';\n var consumerKey = 'UPDATE-WITH-YOUR-CONSUMER-KEY';\n var consumerSecretKey = 'UPDATE-WITH-YOUR-CONSUMER-SECRET-KEY';\n\n var twitterIdentity = _.find(user.identities, { connection: 'twitter' });\n var oauthToken = twitterIdentity.access_token;\n var oauthTokenSecret = twitterIdentity.access_token_secret;\n\n var timestamp = Date.now() / 1000;\n var nonce = uuid.v4().replace(/-/g, '');\n\n var params = {\n include_email: true,\n oauth_consumer_key: consumerKey,\n oauth_nonce: nonce,\n oauth_signature_method: 'HMAC-SHA1',\n oauth_timestamp: timestamp,\n oauth_token: oauthToken,\n oauth_version: '1.0'\n };\n\n params.oauth_signature = oauth.hmacsign('GET', url, params, consumerSecretKey, oauthTokenSecret);\n\n var auth = Object.keys(params).sort().map(function (k) {\n return k + '=\"' + oauth.rfc3986(params[k]) + '\"';\n }).join(', ');\n\n request({\n url: url + '?include_email=true',\n headers: {\n 'Authorization': 'OAuth ' + auth\n }\n }, function (err, resp, body) {\n if (resp.statusCode !== 200) {\n return callback(new Error('Error retrieving email from twitter: ' + body || err));\n }\n user.email = JSON.parse(body).email;\n return callback(err, user, context);\n });\n}"
},
{
"id": "google-refresh-token",
"title": "Store Google Refresh Token",
"categories": [
"enrich profile"
],
"description": "<p>In some scenarios, you might want to access Google APIs from your application. You do that by using the <code>access_token</code> stored on the <code>identities</code> array (<code>user.identities[0].access_token</code>). However <code>access_token</code>s have an expiration and in order to get a new one, you have to ask the user to login again. That's why Google allows asking for a <code>refresh_token</code> that can be used forever (until the user revokes it) to obtain new <code>access_tokens</code> without requiring the user to relogin.</p>\n<p>The way you ask for a <code>refresh_token</code> using Lock is by sending the <code>access_type=offline</code> as an extra parameter as <a href=\"https://auth0.com/docs/libraries/lock/v10/customization#auth-object-\">explained here</a> using the <code>auth.params</code> object in Lock's <code>options</code>.</p>\n<p>The only caveat is that Google will send you the <code>refresh_token</code> only once, and if you haven't stored it, you will have to ask for it again and add <code>approval_prompt=force</code> so the user explicitly consent again. Since this would be annoying from a user experience perspective, you should store the refresh token on Auth0 as a persistent property of the user, only if it there is a new one available.</p>\n",
"code": "function (user, context, callback) {\n user.app_metadata = user.app_metadata || {};\n // IMPORTANT: for greater security, we recommend encrypting this value and decrypt on your application.\n // function encryptAesSha256 (password, textToEncrypt) {\n // var cipher = crypto.createCipher('aes-256-cbc', password);\n // var crypted = cipher.update(textToEncrypt, 'utf8', 'hex');\n // crypted += cipher.final('hex');\n // return crypted;\n // }\n\n // get the google identity\n var googleIdentity = _.find(user.identities, {'provider': 'google-oauth2'});\n\n // if the user that just logged in has a refresh_token, persist it\n if (googleIdentity && googleIdentity.refresh_token) {\n user.app_metadata.refresh_token = googleIdentity.refresh_token;\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then(function(){\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n } else {\n callback(null, user, context);\n }\n}"
},
{
"id": "google-service-account-token",
"title": "Create a Google access_token using a Service Account",
"categories": [
"enrich profile"
],
"description": "<p>In some scenarios, you might want to access Google Admin APIs from your applications. Accessing those APIs require either a consent of the Google Apps administrator or creating a Service Account and obtain a token programmatically without interactive consent. This rule create such token based on a service account and put it under <code>user.admin_access_token</code>. </p>\n<p>To create a service account go to Google API Console, create a new Client ID and choose Service Account</p>\n<p><img src=\"https://cloudup.com/cpvhC6n9xW9+\" width=\"420\"></p>\n<p>You will get the key that you would have to convert to PEM and remove the passphrase using this command</p>\n<p> openssl pkcs12 -in yourkey.p12 -out yourkey.pem -nocerts -nodes</p>\n<p>Login to Google Apps Admin and go to <a href=\"https://admin.google.com/AdminHome?chromeless=1#OGX:ManageOauthClients\">https://admin.google.com/AdminHome?chromeless=1#OGX:ManageOauthClients</a> (Security -> Advanced Settings -> Manage OAuth Client Access)\nEnter</p>\n<p><img src=\"https://cloudup.com/c0Nq5NWRFaQ+\" width=\"620\"></p>\n<p>Enter the Client ID created on the previous step and the scope you want to allow access to.</p>\n<ul>\n<li><code>KEY</code>: the string representation of the key (open the PEM and replace enters with \\n to make it one line).</li>\n<li><code>GOOGLE_CLIENT_ID_EMAIL</code>: this is the email address of the service account created (NOT the Client ID).</li>\n<li><code>SCOPE</code>: the scope you want access to. Full list of scopes <a href=\"https://developers.google.com/admin-sdk/directory/v1/guides/authorizing\">https://developers.google.com/admin-sdk/directory/v1/guides/authorizing</a>.</li>\n<li><code>ADMIN_EMAIL</code>: a user of your Google Apps domain that this rule would impersonate.</li>\n</ul>\n<blockquote>\n<p>NOTE: the Google access_token will last 1 hour, so you will have to either force a re-login or use a refresh token to trigger a token refresh on Auth0 and hence the rule running again.</p>\n<p>NOTE 2: you might want to be careful what scopes you ask for and where the access_token will be used. For instance, if used from a JavaScript application, a low-privilieged user might grab the token and do API calls that you wouldn't allow.</p>\n</blockquote>\n<p>Here's the rule:</p>\n",
"code": "function (user, context, callback) {\n\n // this is the private key you downloaded from your service account.\n // make sure you remove the password from the key and convert it to PEM using the following\n // openssl pkcs12 -in yourkey.p12 -out yourkey.pem -nocerts -nodes\n // finally, you should put this as a configuration encrypted in Auth0\n var KEY = '....RSA private key downloaded from service account...'; \n\n // this is the email address of the service account created (NOT the Client ID)\n var GOOGLE_CLIENT_ID_EMAIL = '[email protected]';\n\n // the scope you want access to. Full list of scopes https://developers.google.com/admin-sdk/directory/v1/guides/authorizing\n var SCOPE = 'https://www.googleapis.com/auth/admin.directory.user.readonly';\n\n // a user of your Google Apps domain that this rule would impersonate\n var ADMIN_EMAIL = '[email protected]';\n\n var token = jwt.sign({ scope: SCOPE, sub: ADMIN_EMAIL }, KEY, { audience: \"https://accounts.google.com/o/oauth2/token\", issuer: GOOGLE_CLIENT_ID_EMAIL, expiresInMinutes: 60, algorithm: 'RS256'});\n\n request.post({ url: 'https://accounts.google.com/o/oauth2/token', form: { grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion: token } }, function(err, resp, body) {\n if (err) return callback(null, user, context);\n var result = JSON.parse(body);\n if (result.error) {\n console.log(body);\n // log and swallow\n return callback(null, user, context);\n }\n\n context.idToken['https://example.com/admin_access_token'] = result.access_token;\n callback(null, user, context);\n });\n\n}"
},
{
"id": "linkedin-original-picture",
"title": "Use the original sized profile picture for LinkedIn connections",
"categories": [
"enrich profile"
],
"description": "<p>This rule will set the <code>picture</code> to the original sized profile picture for users who login with LinkedIn.</p>\n",
"code": "function (user, context, callback) {\n if (context.connection !== 'linkedin') {\n callback(null, user, context);\n }\n\n var request = require('request');\n var options = {\n url: 'https://api.linkedin.com/v1/people/~/picture-urls::(original)?format=json',\n headers: {\n Authorization: 'Bearer ' + user.identities[0].access_token\n }\n };\n\n request(options, function(error, response) {\n if (!error && response.statusCode === 200) {\n var json = JSON.parse(response.body);\n if (json.values && json.values.length >= 1) {\n context.idToken.picture = json.values[0];\n }\n }\n callback(null, user, context);\n });\n}"
},
{
"id": "remove-attributes",
"title": "Remove attributes from a user",
"categories": [
"enrich profile"
],
"description": "<p>Sometimes you don't need every attribute from the user. You can use a rule to delete attributes.</p>\n",
"code": "function (user, context, callback) {\n delete user.some_attribute;\n\n // another option would be to define a whitelist of attributes you want,\n // instead of delete the ones you don't want\n /*\n var whitelist = ['email', 'name', 'identities'];\n Object.keys(user).forEach(function(key) {\n console.log(whitelist.indexOf(key));\n if (whitelist.indexOf(key) === -1) delete user[key];\n });\n */\n\n callback(null, user, context);\n}"
},
{
"id": "saml-attribute-mapping",
"title": "SAML Attributes mapping",
"categories": [
"enrich profile"
],
"description": "<p>If the application the user is logging in to is SAML (like Salesforce for instance), you can customize the mapping between the Auth0 user and the SAML attributes.\nBelow you can see that we are mapping <code>user_id</code> to the NameID, <code>email</code> to <code>http://schemas.../emailaddress</code>, etc.</p>\n<p>For more information about SAML options, look at <a href=\"https://docs.auth0.com/saml-configuration\">https://docs.auth0.com/saml-configuration</a>.</p>\n",
"code": "function (user, context, callback) {\n context.samlConfiguration.mappings = {\n \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier\": \"user_id\",\n \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\": \"email\",\n \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\": \"name\",\n \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/food\": \"user_metadata.favorite_food\",\n \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/address\": \"app_metadata.shipping_address\"\n };\n\n callback(null, user, context);\n}"
},
{
"id": "soap-webservice",
"title": "Roles from a SOAP Service",
"categories": [
"enrich profile"
],
"description": "<p>This rule shows how to query a basic profile http binding SOAP web service for roles and add those to the user.</p>\n",
"code": "function (user, context, callback) {\n function getRoles(callback) {\n request.post({\n url: 'https://somedomain.com/RoleService.svc',\n body: '<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><GetRolesForCurrentUser xmlns=\"http://tempuri.org\"/></s:Body></s:Envelope>',\n headers: { 'Content-Type': 'text/xml; charset=utf-8',\n 'SOAPAction': 'http://tempuri.org/RoleService/GetRolesForCurrentUser' }\n }, function (err, response, body) {\n if (err) return callback(err);\n\n var parser = new xmldom.DOMParser();\n var doc = parser.parseFromString(body);\n var roles = xpath.select(\"//*[local-name(.)='string']\", doc).map(function(node) { return node.textContent; });\n return callback(null, roles);\n });\n }\n\n getRoles(user.email, function(err, roles) {\n if (err) return callback(err);\n\n context.idToken['https://example.com/roles'] = roles;\n\n callback(null, user, context);\n });\n}"
},
{
"id": "socure_fraudscore",
"title": "Detect Fraud Users",
"categories": [
"enrich profile"
],
"description": "<p>This rule gets the fraud score from socure.com and store it on app_metadata.</p>\n",
"code": "function (user, context, callback) {\n // score fraudscore once (if it's already set, skip this)\n user.app_metadata = user.app_metadata || {};\n if (user.app_metadata.socure_fraudscore) return callback(null, user, context);\n\n var SOCURE_KEY = 'YOUR SOCURE API KEY';\n\n if(!user.email) {\n // the profile doesn't have email so we can't query their api.\n return callback(null, user, context);\n }\n\n // socurekey=A678hF8E323172B78E9&[email protected]&ipaddress=1.2.3.4&mobilephone=%2B12015550157\n request({\n url: 'https://service.socure.com/api/1/EmailAuthScore',\n qs: {\n email: user.email,\n socurekey: SOCURE_KEY,\n ipaddress: context.request.ip\n }\n }, function (err, resp, body) {\n if (err) return callback(null, user, context);\n if (resp.statusCode !== 200) return callback(null, user, context);\n var socure_response = JSON.parse(body);\n if (socure_response.status !== 'Ok') return callback(null, user, context);\n\n user.app_metadata = user.app_metadata || {};\n user.app_metadata.socure_fraudscore = socure_response.data.fraudscore;\n user.app_metadata.socure_confidence = socure_response.data.confidence;\n // \"details\":[ \n // \"blacklisted\":{ \n // \"industry\":\"Banking and Finance\",\n // \"reporteddate\":\"2014-07-02\",\n // \"reason\":\"ChargeBack Fraud\"\n // }\n // ] \n user.app_metadata.socure_details = socure_response.data.details;\n\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then(function(){\n context.idToken['https://example.com/socure_fraudscore'] = user.app_metadata.socure_fraudscore;\n context.idToken['https://example.com/socure_confidence'] = user.app_metadata.socure_confidence;\n context.idToken['https://example.com/socure_details'] = user.app_metadata.socure_details;\n callback(null, user, context);\n })\n .catch(function(err){\n callback(null, user, context);\n });\n });\n}"
}
]
},
{
"name": "webhook",
"templates": [
{
"id": "aspnet-webapi",
"title": "Custom webhook with ASPNET WebApi2",
"categories": [
"webhook"
],
"description": "<p>This rule shows how to post the variables sent to your Rule a custom webhook in an ASP.NET WebApi application. This is useful for situations where you want to enrich the User's profile with your internal ID before the JsonWebToken is created, or if you want to seamlessly register new users.</p>\n<p>In this example, we're going to get the internal UserId for your app, then persist it to the Auth0 UserProfile so we only have to make this request the first time a new user signs in.</p>\n<p>Within the snippet, the "secretToken" is a simple way to ensure that the communication is coming from Auth0. Just type in a random string into the Rule, and then check for that string in your WebApi request.</p>\n<p>In your WebApi code, complete whatever operations are necessary, then call <code>return Json(new { customId = USERSCUSTOMID });</code> to return the required JSON to the Rule.</p>\n<blockquote>\n<p>Note: Be sure to change the URL for the request to your website and controller, and make sure the controller is decorated with the <code>[HttpPost]</code> attribute.</p>\n</blockquote>\n<p>Contributed by Robert McLaws, AdvancedREI.com</p>\n",
"code": "function (user, context, callback) {\n user.app_metadata = user.app_metadata || {};\n if (user.app_metadata.customId) {\n console.log('Found ID!');\n return callback(null, user, context);\n }\n\n // You should make your requests over SSL to protect your app secrets.\n request.post({\n url: 'https://yourwebsite.com/auth0',\n json: {\n user: user,\n context: context,\n secretToken: \";ojhsajk;h;Kh:Jh\",\n },\n timeout: 15000\n }, function(err, response, body){\n if (err) return callback(new Error(err));\n\n user.app_metadata.customId = body.customId;\n context.idToken['https://example.com/custom_id'] = body.customId;\n\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then(function(){\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n });\n}"
},
{
"id": "creates-lead-salesforce",
"title": "Creates a new Lead in Salesforce on First Login",
"categories": [
"webhook"
],
"description": "<p>This rule will check if this is the first user login, and in that case will call Salesforce API to record the contact as a new Lead. It is using Salesforce REST APIs and the <code>resource owner</code> flow to obtain an <code>access_token</code>. The username you use to authenticate the API will appear as the <strong>creator</strong> of the lead.</p>\n<blockquote>\n<p>Note: this sample implements very basic error handling.</p>\n</blockquote>\n",
"code": "function (user, context, done) {\n user.app_metadata = user.app_metadata || {};\n if (user.app_metadata.recordedAsLead) {\n return done(null,user,context);\n }\n\n var MY_SLACK_WEBHOOK_URL = 'YOUR SLACK WEBHOOK URL';\n var slack = require('slack-notify')(MY_SLACK_WEBHOOK_URL);\n\n //Populate the variables below with appropriate values\n var SFCOM_CLIENT_ID = configuration.SALESFORCE_CLIENT_ID;\n var SFCOM_CLIENT_SECRET = configuration.SALESFORCE_CLIENT_SECRET;\n var USERNAME = configuration.SALESFORCE_USERNAME;\n var PASSWORD = configuration.SALESFORCE_PASSWORD;\n\n getAccessToken(SFCOM_CLIENT_ID, SFCOM_CLIENT_SECRET, USERNAME, PASSWORD,\n function(r) {\n if (!r.instance_url || !r.access_token) {\n slack.alert({\n channel: '#some_channel',\n text: 'Error Getting SALESFORCE Access Token',\n fields: {\n error: r\n }\n });\n\n return;\n }\n\n createLead(r.instance_url, r.access_token, function (e, result) {\n if (e || !result || !result.id) {\n slack.alert({\n channel: '#some_channel',\n text: 'Error Creating SALESFORCE Lead',\n fields: {\n error: e || result\n }\n });\n\n return;\n }\n\n user.app_metadata.recordedAsLead = true;\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata);\n });\n });\n\n //See http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_objects_lead.htm\n function createLead(url, access_token, callback){\n //Can use many more fields\n var data = {\n LastName: user.name,\n Company: 'Web channel signups'\n };\n\n request.post({\n url: url + \"/services/data/v20.0/sobjects/Lead\",\n headers: {\n \"Authorization\": \"OAuth \" + access_token\n },\n json: data\n }, function(e,r,b) {\n return callback(e, b);\n });\n }\n\n //Obtains a SFCOM access_token with user credentials\n function getAccessToken(client_id, client_secret, username, password, callback) {\n request.post({\n url: 'https://login.salesforce.com/services/oauth2/token',\n form: {\n grant_type: 'password',\n client_id: client_id,\n client_secret: client_secret,\n username: username,\n password: password\n }}, function(e,r,b) {\n return callback(JSON.parse(b));\n });\n }\n\n // don’t wait for the SF API call to finish, return right away (the request will continue on the sandbox)`\n done(null, user, context);\n}"
},
{
"id": "intercom-user",
"title": "Record or update an Intercom User",
"categories": [
"webhook"
],
"description": "<p>This rule will call the Intercom API to create a new contact or update an existing contact's activity, whenever there is a signup or login with Auth0.</p>\n",
"code": "function (user, context, callback) {\n var moment = require('moment-timezone');\n\n var data = {\n \"user_id\": user.user_id,\n \"email\": user.email,\n \"name\": user.name,\n \"signed_up_at\": moment(user.created_at).unix(),\n \"last_seen_ip\" : context.request.ip,\n \"last_seen_user_agent\": context.request.userAgent,\n \"update_last_request_at\": true,\n \"new_session\": true\n };\n var accessToken = 'YOUR INTERCOM ACCESS TOKEN';\n\n request.post({\n url: 'https://api.intercom.io/users',\n headers: {\n 'Authorization': 'Bearer ' + accessToken,\n 'Accept': 'application/json'\n },\n json: data\n });\n // don’t wait for the Intercom API call to finish, return right away.\n callback(null, user, context);\n}"
},
{
"id": "mailgun",
"title": "Send emails through Mailgun",
"categories": [
"webhook"
],
"description": "<p>This rule will send an email to an administrator on the first login of a user using <a href=\"https://mailgun.com\">Mailgun</a>.</p>\n<p>We use a persistent property <code>SignedUp</code> to track whether this is the first login or subsequent ones.</p>\n",
"code": "function(user, context, callback) {\n user.app_metadata = user.app_metadata || {};\n if (user.app_metadata.signedUp) {\n return callback(null, user, context);\n }\n\n request.post( {\n url: 'https://api.mailgun.net/v3/{YOUR MAILGUN ACCOUNT}/messages',\n auth:\n {\n user: 'api',\n pass: '{YOUR MAILGUN KEY}'\n },\n form: {\n 'to': '[email protected]',\n 'subject': 'NEW SIGNUP',\n 'from': '[email protected]',\n 'text': 'We have got a new sign up from: ' + user.email + '.'\n }\n }, function(e,r,b) {\n if (e) return callback(e);\n if (r.statusCode !== 200) return callback(new Error('Invalid operation'));\n\n user.app_metadata.signedUp = true;\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then(function(){\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n });\n}"
},
{
"id": "mandrill",
"title": "Send email with Mandrill",
"categories": [
"webhook"
],
"description": "<p>This rule will send an email to an administrator on a user's first login. We use a persistent <code>signedUp</code> property to track whether this is the case or not. This rule assumes you've stored a secure value named <code>MANDRILL_API_KEY</code>, which contains your secret API key for Mandrill. It is sent in each request.</p>\n<p>In the same way, other services such as <a href=\"http://docs.aws.amazon.com/ses/latest/APIReference/Welcome.html\">Amazon SES</a> and <a href=\"https://auth0.com/rules/sendgrid\">SendGrid</a> can be used.</p>\n<p>Make sure to change the sender and destination emails.</p>\n",
"code": "function (user, context, callback) {\n user.app_metadata = user.app_metadata || {};\n // Only send an email when user signs up\n if (!user.app_metadata.signedUp) {\n // See https://mandrillapp.com/api/docs/messages.JSON.html#method=send\n var body = {\n key: configuration.MANDRILL_API_KEY,\n message: {\n subject: 'User ' + user.name + ' signed up to ' + context.clientName,\n text: 'Sent from an Auth0 rule',\n from_email: '[email protected]',\n from_name: 'Auth0 Rule',\n to: [\n {\n email: '[email protected]',\n type: 'to'\n }\n ],\n }\n };\n var mandrill_send_endpoint = 'https://mandrillapp.com/api/1.0/messages/send.json';\n\n request.post({url: mandrill_send_endpoint, form: body}, function (err, resp, body) {\n if (err) { return callback(err); }\n user.app_metadata.signedUp = true;\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then(function(){\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n });\n } else {\n // User had already logged in before, do nothing\n callback(null, user, context);\n }\n}"
},
{
"id": "mixpanel-track-event",
"title": "Tracks Logins in MixPanel",
"categories": [
"webhook"
],
"description": "<p>This rule will send a <code>Sign In</code> event to MixPanel, and will include the application the user is signing in to as a property. See <a href=\"https://mixpanel.com/help/reference/http\">MixPanel HTTP API</a> for more information.</p>\n",
"code": "function (user, context, callback) {\n\n var mpEvent = {\n \"event\": \"Sign In\",\n \"properties\": {\n \"distinct_id\": user.user_id,\n \"token\": \"{REPLACE_WITH_YOUR_MIXPANEL_TOKEN}\",\n \"application\": context.clientName\n }\n };\n\n var base64Event = new Buffer(JSON.stringify(mpEvent)).toString('base64');\n\n request.get({\n url: 'http://api.mixpanel.com/track/',\n qs: {\n data: base64Event\n }\n }, function (e, r, b){\n // don’t wait for the MixPanel API call to finish, return right away (the request will continue on the sandbox)`\n callback(null, user, context);\n });\n}"
},
{
"id": "pusher",
"title": "Obtains a Pusher token for subscribing/publishing to private channels",
"categories": [
"webhook"
],
"description": "<p>This rule will generate a [pusher.com] token that can be used to send and receive messages from private channels. See <a href=\"https://github.com/auth0/auth0-pusher\">a complete example here</a>.</p>\n",
"code": "function (user, context, callback) {\n\n var pusherKey='YOUR PUSHER KEY';\n var pusherSecret = '{YOUR PUSHER SECRET}';\n\n if( context.request.query.channel && context.request.query.socket_id)\n {\n context.idToken['https://example.com/pusherAuth'] = pusherKey + \":\" + sign(pusherSecret, context.request.query.channel, context.request.query.socket_id);\n }\n\n callback(null, user, context);\n\n function sign(secret, channel, socket_id)\n {\n var string_to_sign = socket_id+\":\"+channel;\n var sha = crypto.createHmac('sha256',secret);\n return sha.update(string_to_sign).digest('hex');\n }\n}"
},
{
"id": "send-events-keenio",
"title": "Send events to Keen",
"categories": [
"webhook"
],
"description": "<p>This rule is used to send a <code>signup</code> event to <a href=\"http://keen.io\">Keen IO</a></p>\n<p>The rule checks whether the user has already signed up before or not. This is tracked by the persistent <code>user.signedUp</code> property. If the property is present, everything else is skipped.\nIf not, then we POST a new event with some information to a <code>signups Collection</code> on Keen IO.</p>\n<p>Once enabled, events will be displayed on Keen IO dashboard:\n<img src=\"http://puu.sh/7k4qN.png\" alt=\"\"></p>\n",
"code": "function(user, context, callback) {\n if (context.stats.loginsCount > 1) {\n return callback(null, user, context);\n }\n\n var MY_SLACK_WEBHOOK_URL = 'YOUR SLACK WEBHOOK URL';\n var slack = require('slack-notify')(MY_SLACK_WEBHOOK_URL);\n\n var writeKey = 'YOUR KEEN IO WRITE KEY';\n var projectId = 'YOUR KEEN IO PROJECT ID';\n var eventCollection = 'signups';\n\n var keenEvent = {\n userId: user.user_id,\n name: user.name,\n ip: context.request.ip //Potentially any other properties in the user profile/context\n };\n\n request.post({\n method: 'POST',\n url: 'https://api.keen.io/3.0/projects/' + projectId + '/events/' + eventCollection + '?api_key=' + writeKey,\n headers: {\n 'Content-type': 'application/json',\n },\n body: JSON.stringify(keenEvent),\n },\n function (error, response, body) {\n\n if( error || (response && response.statusCode !== 200) ) {\n slack.alert({\n channel: '#some_channel',\n text: 'KEEN API ERROR',\n fields: {\n error: error ? error.toString() : (response ? response.statusCode + ' ' + body : '')\n }\n });\n }\n });\n\n callback(null, user, context);\n}"
},
{
"id": "sendgrid",
"title": "Send emails through SendGrid",
"categories": [
"webhook"
],
"description": "<p>This rule will send an email to an administrator on the first login of a user.</p>\n<p>We use a persistent property <code>SignedUp</code> to track whether this is the first login or subsequent ones.</p>\n<p>In the same way you can use other services like <a href=\"http://docs.aws.amazon.com/ses/latest/APIReference/Welcome.html\">Amazon SES</a>, <a href=\"https://auth0.com/mandrill\">Mandrill</a> and few others.</p>\n",
"code": "function(user, context, callback) {\n user.app_metadata = user.app_metadata || {};\n if (!user.app_metadata.signedUp) {\n return callback(null, user, context);\n }\n\n request.post( {\n url: 'https://api.sendgrid.com/api/mail.send.json',\n headers: {\n 'Authorization': 'Bearer ...'\n },\n form: {\n 'to': '[email protected]',\n 'subject': 'NEW SIGNUP',\n 'from': '[email protected]',\n 'text': 'We have got a new sign up from: ' + user.email + '.'\n }\n }, function(e,r,b) {\n if (e) return callback(e);\n if (r.statusCode !== 200) return callback(new Error('Invalid operation'));\n\n user.app_metadata.signedUp = true;\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then(function(){\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n });\n}"
},
{
"id": "slack",
"title": "Slack Notification on User Signup",
"categories": [
"webhook"
],
"description": "<p>This rule sends a message to a slack channel on every user signup.</p>\n",
"code": "function(user, context, callback) {\n // short-circuit if the user signed up already\n if (context.stats.loginsCount > 1) return callback(null, user, context);\n\n // get your slack's hook url from: https://slack.com/services/10525858050\n var SLACK_HOOK = 'YOUR SLACK HOOK URL';\n\n var slack = require('slack-notify')(SLACK_HOOK);\n var message = 'New User: ' + (user.name || user.email) + ' (' + user.email + ')';\n var channel = '#some_channel';\n\n slack.success({\n text: message,\n channel: channel\n });\n\n // don’t wait for the Slack API call to finish, return right away (the request will continue on the sandbox)`\n callback(null, user, context);\n}"
},
{
"id": "splunk-HEC-track-event",
"title": "Tracks Logins/SignUps with Splunk HEC",
"categories": [
"webhook"
],
"description": "<p>This rule will send a <code>SignUp</code> & <code>Login</code> events to Splunk's HTTP Event Collector, including some contextual information of the user: the application the user is signing in, client IP address, username, etc.</p>\n<p>We use a persistent property <code>SignedUp</code> to track whether this is the first login or subsequent ones.</p>\n<p>Events will show up on the Splunk console shortly after user access:</p>\n<p>In order to use this rule, you need to enable HTTP Event Collector (HEC) on your Splunk instance and get an HEC token. You can learn more how to do this <a href=\"http://dev.splunk.com/view/event-collector/SP-CAAAE7F\">here</a> </p>\n<p>Below is a screenshot showing an SingUp event sent to Splunk Cloud.</p>\n<p><img src=\"https://cdn.auth0.com/website/rules/splunk-hec-rule.png\" alt=\"\"></p>\n",
"code": "function(user, context, callback) {\n user.app_metadata = user.app_metadata || {};\n var host = 'YOUR SPLUNK HEC ENDPOINT, like https://localhost:8088';\n var token = 'YOUR SPLUNK HEC TOKEN';\n\n //Add any interesting info to the event\n var hec_event = {\n event: {\n message: user.app_metadata.signedUp ? 'Login' : 'SignUp',\n application: context.clientName,\n clientIP: context.request.ip,\n protocol: context.protocol,\n userName: user.name,\n userId: user.user_id\n },\n source: \"auth0\",\n sourcetype: \"auth0_activity\"\n };\n\n request.post( {\n url: host + '/services/collector',\n headers: {\n 'Authorization': 'Splunk ' + token\n },\n strictSSL: true, // set to false if using a self-signed cert\n json: hec_event\n }, function(e,r,b) {\n if (e) return callback(e);\n if (r.statusCode !== 200) return callback(new Error('Invalid operation'));\n user.app_metadata.signedUp = true; \n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then(function(){\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n });\n\n}"
},
{
"id": "splunk-track-event",
"title": "Tracks Logins/SignUps with Splunk",
"categories": [
"webhook"
],
"description": "<p>This rule will send a <code>SignUp</code> & <code>Login</code> events to Splunk, including some contextual information of the user: the application the user is signing in, client IP address, username, etc.</p>\n<p>We use a persistent property <code>SignedUp</code> to track whether this is the first login or subsequent ones.</p>\n<p>Events will show up on the Splunk console shortly after user access:</p>\n<p><img src=\"http://puu.sh/7R1EW.png\" alt=\"\"></p>\n",
"code": "function(user, context, callback) {\n user.app_metadata = user.app_metadata || {};\n var splunkBaseUrl = 'YOUR SPLUNK SERVER, like: https://your server:8089';\n\n //Add any interesting info to the event\n var event = {\n message: user.app_metadata.signedUp ? 'Login' : 'SignUp',\n application: context.clientName,\n clientIP: context.request.ip,\n protocol: context.protocol,\n userName: user.name,\n userId: user.user_id\n };\n\n request.post( {\n url: splunkBaseUrl + '/services/receivers/simple',\n auth: {\n 'user': 'YOUR SPLUNK USER',\n 'pass': 'YOUR SPLUNK PASSWORD',\n },\n json: event,\n qs: {\n 'source': 'auth0',\n 'sourcetype': 'auth0_activity'\n }\n }, function(e,r,b) {\n if (e) return callback(e);\n if (r.statusCode !== 200) return callback(new Error('Invalid operation'));\n user.app_metadata.signedUp = true;\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then(function(){\n callback(null, user, context);\n })\n .catch(function(err){\n callback(err);\n });\n });\n\n}"
},
{
"id": "thisdata-alert-anomalies",
"title": "Account Takeover Detection via ThisData",
"categories": [
"webhook"
],
"description": "<p>This rule is designed to detect phished or compromised user accounts, and\noptionally send an email or SMS notification to the user asking\n"Was This You?". This is similar to the emails you get signing in to Google or\nFacebook from a new device or location.\nThe users' response can be used to take further action, like shutting\ndown a compromised account.</p>\n<p>It uses <a href=\"https://thisdata.com\">ThisData's</a> anomaly detection\nalgorithms which take into account many behavioral factors including:</p>\n<ul>\n<li>Location & Velocity</li>\n<li>Devices</li>\n<li>Time of day</li>\n<li>Tor usage</li>\n<li>Risky IP addresses</li>\n<li>And more...</li>\n</ul>\n<p>This rule works in the background, and will never stop your users from logging\nin to your application. Use our "Account Takeover Prevention via ThisData" Auth0\nrule to stop suspicious log-ins in their tracks.</p>\n<p>You will need a ThisData API Key. Sign up for a free ThisData\naccount at <a href=\"https://thisdata.com/sign-up\">https://thisdata.com/sign-up</a></p>\n<p>Notifications are disabled by default - you have full control over how we\ncontact your users. Visit ThisData.com to configure:</p>\n<ul>\n<li>Sending "Was This You" notifications via email or SMS (text message)</li>\n<li>Slack notifications</li>\n<li>webhooks to your app</li>\n</ul>\n<p>Read our guide "How to add login anomaly detection to Auth0"\n <a href=\"https://thisdata.com/blog/how-to-add-login-anomaly-detection-to-auth0/\">https://thisdata.com/blog/how-to-add-login-anomaly-detection-to-auth0/</a></p>\n<p>Contact ThisData: [email protected]</p>\n",
"code": "function (user, context, callback) {\n // Get this from your ThisData account\n var apiKey = configuration.THISDATA_API_KEY;\n\n var options = {\n method: 'POST',\n headers: {\n 'User-Agent': 'thisdata-auth0'\n },\n uri: 'https://api.thisdata.com/v1/events?api_key=' + apiKey,\n json: {\n verb: 'log-in',\n ip: context.request.ip,\n user_agent: context.request.userAgent,\n user: {\n id: user.user_id,\n name: user.name,\n email: user.email\n }\n }\n };\n\n request.post(options);\n\n callback(null, user, context);\n}"
},
{
"id": "update-firebase-user",
"title": "Update user profile identity in Firebase",
"categories": [
"webhook"
],
"description": "<p>This rule is used to create or update identity information for a user profile\nstored in Firebase using the Firebase REST API. The unique <code>user.user_id</code> is\nbase64 encoded to provide a unique generated key for the user.</p>\n<p>Each time the user logs into the system, properties of their user\nprofile can updated in Firebase to keep identity properties (like\nname, email, etc) in sync with authentication credentials.</p>\n<p>You can find more information in the Firebase API: <a href=\"https://www.firebase.com/docs/rest-api.html\">REST API</a></p>\n",
"code": "function (user, context, callback) {\n\n var baseURL = configuration.FIREBASE_URL;\n var secret = configuration.FIREBASE_SECRET;\n var fb_id = new Buffer(user.user_id).toString('base64');\n\n var fbIdentity = {\n \"identity\": {\n \"user_id\": user.user_id,\n \"email\": user.email,\n \"name\": user.name,\n \"nickname\": user.nickname,\n \"picture\": user.picture\n }\n };\n\n var putURL = baseURL + \"/users/\" + fb_id + \".json?auth=\" + secret;\n request.put({\n \"url\": putURL,\n \"json\": fbIdentity\n },\n function(err, response, body) {\n if (err) return callback(err);\n return callback(null, user, context);\n });\n}"
},
{
"id": "zapier-new-login",
"title": "Trigger a Zap on Every User Login",
"categories": [
"webhook"
],
"description": "<p><strong>What is Zapier?</strong> <a href=\"http://zapier.com\">Zapier</a> is a tool for primarily non-technical users to connect together web apps. An integration between two apps is called a Zap. A Zap is made up of a Trigger and an Action. Whenever the trigger happens in one app, Zapier will automatically perform the action in another app.</p>\n<p><img src=\"https://cloudup.com/iGyywQuJqIb+\" alt=\"\"></p>\n<p>This rule will call Zapier static hook every time a user logs in.</p>\n",
"code": "function (user, context, callback) {\n var ZAP_HOOK_URL = 'REPLACE_ME';\n\n var small_context = {\n appName: context.clientName,\n userAgent: context.userAgent,\n ip: context.ip,\n connection: context.connection,\n strategy: context.connectionStrategy\n };\n var payload_to_zap = extend({}, user, small_context);\n request.post({\n url: ZAP_HOOK_URL,\n json: payload_to_zap\n },\n function (err, response, body) {\n // swallow error\n callback(null, user, context);\n });\n\n function extend(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i],\n keys = Object.keys(source);\n\n for (var j = 0; j < keys.length; j++) {\n var name = keys[j];\n target[name] = source[name];\n }\n }\n return target;\n }\n}"
},
{
"id": "zapier-new-user",
"title": "Trigger a Zap on New Users",
"categories": [
"webhook"
],
"description": "<p><strong>What is Zapier?</strong> <a href=\"http://zapier.com\">Zapier</a> is a tool for primarily non-technical users to connect together web apps. An integration between two apps is called a Zap. A Zap is made up of a Trigger and an Action. Whenever the trigger happens in one app, Zapier will automatically perform the action in another app.</p>\n<p><img src=\"https://cloudup.com/cgwZds8MjA7+\" alt=\"\"></p>\n<p>This rule will call Zapier static hook every time a new user signs up.</p>\n",
"code": "function (user, context, callback) {\n // short-circuit if the user signed up already\n if (context.stats.loginsCount > 1) {\n return callback(null, user, context);\n }\n\n var _ = require('lodash');\n\n var ZAP_HOOK_URL = 'REPLACE_ME';\n\n var small_context = {\n appName: context.clientName,\n userAgent: context.userAgent,\n ip: context.ip,\n connection: context.connection,\n strategy: context.connectionStrategy\n };\n\n var payload_to_zap = _.extend({}, user, small_context);\n\n request.post({\n url: ZAP_HOOK_URL,\n json: payload_to_zap\n });\n\n // don’t wait for the Zapier WebHook call to finish, return right away (the request will continue on the sandbox)`\n callback(null, user, context);\n}"
}
]
},
{
"name": "multifactor",
"templates": [
{
"id": "duo-multifactor",
"title": "Multifactor with Duo Security",
"categories": [
"multifactor"
],
"description": "<p>This rule is used to trigger multifactor authentication with <a href=\"http://duosecurity.com\">Duo Security</a> when a condition is met.</p>\n<p>Upon first login, the user can enroll the device.</p>\n<p>You need to create two <strong>integrations</strong> in <strong>Duo Security</strong>: one of type <strong>WebSDK</strong> and the other <strong>Admin SDK</strong>.</p>\n",
"code": "function (user, context, callback) {\n\n var CLIENTS_WITH_MFA = ['{REPLACE_WITH_YOUR_CLIENT_ID}'];\n // run only for the specified clients\n if (CLIENTS_WITH_MFA.indexOf(context.clientID) !== -1) {\n // uncomment the following if clause in case you want to request a second factor only from user's that have user_metadata.use_mfa === true\n // if (user.user_metadata && user.user_metadata.use_mfa){\n context.multifactor = {\n //required\n provider: 'duo',\n ikey: 'DIXBMN...LZO8IOS8',\n skey: 'nZLxq8GK7....saKCOLPnh',\n host: 'api-3....049.duosecurity.com',\n\n // optional, defaults to true. Set to false to force DuoSecurity every time. \n // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details\n allowRememberBrowser: false,\n\n // optional. Use some attribute of the profile as the username in DuoSecurity. This is also useful if you already have your users enrolled in Duo.\n // username: user.nickname,\n\n // optional. Admin credentials. If you provide an Admin SDK type of credentials. auth0 will update the realname and email in DuoSecurity.\n // admin: {\n // ikey: 'DIAN...NV6UM',\n // skey: 'YL8OVzvoeeh...I1uiYrKoHvuzHnSRj'\n // },\n };\n // }\n }\n\n callback(null, user, context);\n}"
},
{
"id": "google-multifactor",
"title": "Multifactor with Google Authenticator",
"categories": [
"multifactor"
],
"description": "<p>This rule is used to trigger multifactor authentication with Google Authenticator when a condition is met.</p>\n<p>Upon first login, the user can enroll the device by scanning a QR code. Subsequent logins will ask for the Google Authenticator code.</p>\n<p>To reset Google Authenticator for a user, you can go to Users, search for the specific user and click on Actions -> Multifactor.</p>\n",
"code": "function (user, context, callback) {\n // Uncomment the following to skip MFA when impersonating a user\n // if (user.impersonated) { return callback(null, user, context); }\n\n var CLIENTS_WITH_MFA = ['REPLACE_WITH_YOUR_CLIENT_ID'];\n // run only for the specified clients\n if (CLIENTS_WITH_MFA.indexOf(context.clientID) !== -1) {\n // uncomment the following if clause in case you want to request a second factor only from users that have app_metadata.use_mfa === true\n // if (user.app_metadata && user.app_metadata.use_mfa){\n context.multifactor = {\n provider: 'google-authenticator',\n\n // optional\n // issuer: 'Label on Google Authenticator App', \n\n // optional, the key to use for TOTP. By default one is generated for you\n // key: '{YOUR_KEY_HERE}', \n\n // optional, defaults to true. Set to false to force Google Authenticator every time. \n // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details\n allowRememberBrowser: false \n };\n // }\n }\n\n callback(null, user, context);\n}"
},
{
"id": "guardian-multifactor-authorization-extension",
"title": "Multifactor with Auth0 Guardian + Authorization Extension",
"categories": [
"multifactor",
"guardian"
],
"description": "<p>This rule is used to trigger multifactor authentication with Auth0 for\nor more groups on the authorization extension.</p>\n<p>Upon first login, the user can enroll the device.</p>\n",
"code": "function (user, context, callback) {\n if (!user.app_metadata || !user.app_metadata.authorization ||\n !Array.isArray(user.app_metadata.authorization.groups)) {\n return callback(null, user, context);\n }\n\n var groups = user.app_metadata.authorization.groups;\n var GROUPS_WITH_MFA = {\n // Add groups that need MFA here\n // Example\n // 'admins': true,\n };\n\n var needsMFA = !!groups.find(function(group) {\n return GROUPS_WITH_MFA[group];\n });\n\n if (needsMFA){\n context.multifactor = {\n // required\n provider: 'guardian', //required\n\n // optional, defaults to true. Set to false to force Guardian authentication every time. \n // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details\n allowRememberBrowser: false\n };\n }\n\n callback(null, user, context);\n}"
},
{
"id": "guardian-multifactor-changed-location",
"title": "Multifactor when country changes",
"categories": [
"multifactor",
"guardian"
],
"description": "<p>This rule is used to trigger multifactor authentication when the current country does not match the country of the previous session</p>\n",
"code": "function (user, context, callback) {\n\n user.app_metadata = user.app_metadata || {}; \n\n if (user.app_metadata.last_location !== context.request.geoip.country_code) {\n context.multifactor = { \n provider: 'guardian',\n\n // optional, defaults to true. Set to false to force Guardian authentication every time. \n // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details\n allowRememberBrowser: false\n }; \n }\n\n //Set the location context for next time\n user.app_metadata.last_location = context.request.geoip.country_code;\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then( function() {\n callback(null, user, context);\n })\n .catch( function(err) {\n callback(err);\n }); \n}"
},
{
"id": "guardian-multifactor-ip-range",
"title": "Multifactor when request comes from outside an IP range",
"categories": [
"multifactor",
"guardian"
],
"description": "<p>This rule is used to trigger multifactor authentication when the requesting IP is from outside the corporate IP range.</p>\n",
"code": "function (user, context, callback) {\n var ipaddr = require('ipaddr.js');\n var corp_network = \"192.168.1.134/26\";\n\n var current_ip = ipaddr.parse(context.request.ip); \n if (!current_ip.match(ipaddr.parseCIDR(corp_network))) {\n context.multifactor = {\n provider: 'guardian',\n\n // optional, defaults to true. Set to false to force Guardian authentication every time. \n // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details\n allowRememberBrowser: false\n }; \n }\n\n callback(null, user, context);\n}"
},
{
"id": "guardian-multifactor",
"title": "Multifactor with Auth0 Guardian",
"categories": [
"multifactor",
"guardian"
],
"description": "<p>This rule is used to trigger multifactor authentication with Auth0 when a condition is met.</p>\n<p>Upon first login, the user can enroll the device.</p>\n",
"code": "function (user, context, callback) {\n\n //var CLIENTS_WITH_MFA = ['{REPLACE_WITH_YOUR_CLIENT_ID}'];\n // run only for the specified clients\n // if (CLIENTS_WITH_MFA.indexOf(context.clientID) !== -1) {\n // uncomment the following if clause in case you want to request a second factor only from user's that have user_metadata.use_mfa === true\n // if (user.user_metadata && user.user_metadata.use_mfa){\n context.multifactor = {\n // required\n provider: 'guardian', \n\n // optional, defaults to true. Set to false to force Guardian authentication every time. \n // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details\n allowRememberBrowser: false\n };\n // }\n //}\n\n callback(null, user, context);\n}"
}
]
},
{
"name": "guardian",
"templates": [
{
"id": "guardian-multifactor-authorization-extension",
"title": "Multifactor with Auth0 Guardian + Authorization Extension",
"categories": [
"multifactor",
"guardian"
],
"description": "<p>This rule is used to trigger multifactor authentication with Auth0 for\nor more groups on the authorization extension.</p>\n<p>Upon first login, the user can enroll the device.</p>\n",
"code": "function (user, context, callback) {\n if (!user.app_metadata || !user.app_metadata.authorization ||\n !Array.isArray(user.app_metadata.authorization.groups)) {\n return callback(null, user, context);\n }\n\n var groups = user.app_metadata.authorization.groups;\n var GROUPS_WITH_MFA = {\n // Add groups that need MFA here\n // Example\n // 'admins': true,\n };\n\n var needsMFA = !!groups.find(function(group) {\n return GROUPS_WITH_MFA[group];\n });\n\n if (needsMFA){\n context.multifactor = {\n // required\n provider: 'guardian', //required\n\n // optional, defaults to true. Set to false to force Guardian authentication every time. \n // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details\n allowRememberBrowser: false\n };\n }\n\n callback(null, user, context);\n}"
},
{
"id": "guardian-multifactor-changed-location",
"title": "Multifactor when country changes",
"categories": [
"multifactor",
"guardian"
],
"description": "<p>This rule is used to trigger multifactor authentication when the current country does not match the country of the previous session</p>\n",
"code": "function (user, context, callback) {\n\n user.app_metadata = user.app_metadata || {}; \n\n if (user.app_metadata.last_location !== context.request.geoip.country_code) {\n context.multifactor = { \n provider: 'guardian',\n\n // optional, defaults to true. Set to false to force Guardian authentication every time. \n // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details\n allowRememberBrowser: false\n }; \n }\n\n //Set the location context for next time\n user.app_metadata.last_location = context.request.geoip.country_code;\n auth0.users.updateAppMetadata(user.user_id, user.app_metadata)\n .then( function() {\n callback(null, user, context);\n })\n .catch( function(err) {\n callback(err);\n }); \n}"
},
{
"id": "guardian-multifactor-ip-range",
"title": "Multifactor when request comes from outside an IP range",
"categories": [
"multifactor",
"guardian"
],
"description": "<p>This rule is used to trigger multifactor authentication when the requesting IP is from outside the corporate IP range.</p>\n",
"code": "function (user, context, callback) {\n var ipaddr = require('ipaddr.js');\n var corp_network = \"192.168.1.134/26\";\n\n var current_ip = ipaddr.parse(context.request.ip); \n if (!current_ip.match(ipaddr.parseCIDR(corp_network))) {\n context.multifactor = {\n provider: 'guardian',\n\n // optional, defaults to true. Set to false to force Guardian authentication every time. \n // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details\n allowRememberBrowser: false\n }; \n }\n\n callback(null, user, context);\n}"
},
{
"id": "guardian-multifactor",
"title": "Multifactor with Auth0 Guardian",
"categories": [
"multifactor",
"guardian"
],
"description": "<p>This rule is used to trigger multifactor authentication with Auth0 when a condition is met.</p>\n<p>Upon first login, the user can enroll the device.</p>\n",
"code": "function (user, context, callback) {\n\n //var CLIENTS_WITH_MFA = ['{REPLACE_WITH_YOUR_CLIENT_ID}'];\n // run only for the specified clients\n // if (CLIENTS_WITH_MFA.indexOf(context.clientID) !== -1) {\n // uncomment the following if clause in case you want to request a second factor only from user's that have user_metadata.use_mfa === true\n // if (user.user_metadata && user.user_metadata.use_mfa){\n context.multifactor = {\n // required\n provider: 'guardian', \n\n // optional, defaults to true. Set to false to force Guardian authentication every time. \n // See https://auth0.com/docs/multifactor-authentication/custom#change-the-frequency-of-authentication-requests for details\n allowRememberBrowser: false\n };\n // }\n //}\n\n callback(null, user, context);\n}"
}
]
},
{
"name": "debugging",
"templates": [
{
"id": "requestbin",
"title": "Dump rule variables to RequestBin",
"categories": [
"debugging"
],
"description": "<p>This rule shows how to post the variables sent to your Rule to <a href=\"http://RequestB.in\">http://RequestB.in</a> to help troubleshoot issues with your Rules.</p>\n<p>You can run this rule by itself, or paste it into an existing rule. Once the rule has posted data to RequestB.in, you can use a site like <a href=\"http://bodurov.com/JsonFormatter/\">http://bodurov.com/JsonFormatter/</a> to more easily visualize the data.</p>\n<blockquote>\n<p>Note: You should deactivate this rule or comment out the code once you are finished troubleshooting.</p>\n</blockquote>\n<p>Contributed by Robert McLaws, AdvancedREI.com</p>\n",
"code": "function (user, context, callback) {\n request.post({\n url: 'http://requestb.in/YourBinUrl',\n json: {\n user: user,\n context: context,\n },\n timeout: 15000\n }, function(err, response, body){\n if (err) return callback(new Error(err));\n return callback(null, user, context);\n });\n}"
}
]
},
{
"name": "saml",
"templates": [
{
"id": "saml-configuration",
"title": "Change your SAML configuration",
"categories": [
"saml"
],
"description": "<p>At some point you may want to add fields to your SAML Configuration. The way to do this is to add specific fields as done in the example code snippet below. <code>samlConfiguration</code> is an object that controls the behavior of the SAML and WS-Fed endpoints. Useful for advanced claims mapping and token enrichment (only available for SAMLP and WS-Fed protocol).</p>\n<p>To know more about SAML configuration options check <a href=\"https://auth0.com/docs/saml-configuration#configuration-options\">this documentation page</a>.</p>\n",
"code": "function (user, context, callback) {\n if (context.clientID !== '{YOUR_SAMLP_OR_WSFED_CLIENT_ID}') return callback(null, user, context);\n\n context.samlConfiguration = context.samlConfiguration || {};\n //context.samlConfiguration.audience = \"urn:foo\";\n //context.samlConfiguration.recipient = \"http://foo\";\n //context.samlConfiguration.destination = \"http://foo\";\n //context.samlConfiguration.lifetimeInSeconds = 3600;\n //context.samlConfiguration.mappings = {\n // \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier\": \"user_id\",\n // \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\": \"email\",\n // \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\": \"name\",\n // \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname\": \"given_name\",\n // \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname\": \"family_name\",\n // \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn\": \"upn\",\n // \"http://schemas.xmlsoap.org/claims/Group\": \"groups\"\n // };\n //context.samlConfiguration.nameIdentifierFormat = \"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\";\n //context.samlConfiguration.nameIdentifierProbes = [\n // \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier\",\n // \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\",\n // \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\",\n // ];\n //context.samlConfiguration.signatureAlgorithm = \"rsa-sha1\";\n //context.samlConfiguration.digestAlgorithm = \"sha1\";\n //context.samlConfiguration.signResponse = false;\n //context.samlConfiguration.authnContextClassRef = \"urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified\";\n //context.samlConfiguration.mapIdentities = false;\n //context.samlConfiguration.mapUnknownClaimsAsIs = false;\n //context.samlConfiguration.passthroughClaimsWithNoMapping = true;\n //context.samlConfiguration.createUpnClaim = true;\n //context.samlConfiguration.logout = {\n // \"callback\": \"http://foo/logout\"\n // }\n\n //context.samlConfiguration.RelayState = \"foo=bar\"; // SAMLP protocol only\n //context.samlConfiguration.wctx = \"foo=bar\"; // WS-Fed protocol only\n\n callback(null, user, context);\n}"
}
]
}
]