@strapi/plugin-users-permissions 0.0.0-next.fb3a0b82484ce466b1efb1b28f16fc8ef73aba4a → 0.0.0-next.fc1775f7731f8999840e56e298a216b9a6c5c4ad

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.
Files changed (198) hide show
  1. package/.eslintignore +1 -2
  2. package/.eslintrc +17 -0
  3. package/LICENSE +18 -3
  4. package/admin/src/components/BoundRoute/{index.js → index.jsx} +2 -2
  5. package/admin/src/components/FormModal/Input/{index.js → index.jsx} +32 -31
  6. package/admin/src/components/FormModal/index.jsx +115 -0
  7. package/admin/src/components/Permissions/PermissionRow/{CheckboxWrapper.js → CheckboxWrapper.jsx} +4 -3
  8. package/admin/src/components/Permissions/PermissionRow/{SubCategory.js → SubCategory.jsx} +13 -22
  9. package/admin/src/components/Permissions/index.jsx +47 -0
  10. package/admin/src/components/Permissions/reducer.js +1 -1
  11. package/admin/src/components/Policies/{index.js → index.jsx} +7 -5
  12. package/admin/src/components/UsersPermissions/{index.js → index.jsx} +15 -7
  13. package/admin/src/components/UsersPermissions/reducer.js +1 -1
  14. package/admin/src/index.js +16 -43
  15. package/admin/src/pages/AdvancedSettings/index.jsx +214 -0
  16. package/admin/src/pages/AdvancedSettings/utils/layout.js +20 -35
  17. package/admin/src/pages/AdvancedSettings/utils/schema.js +5 -2
  18. package/admin/src/pages/EmailTemplates/components/EmailForm.jsx +156 -0
  19. package/admin/src/pages/EmailTemplates/components/{EmailTable.js → EmailTable.jsx} +20 -17
  20. package/admin/src/pages/EmailTemplates/{index.js → index.jsx} +36 -62
  21. package/admin/src/pages/EmailTemplates/utils/schema.js +18 -6
  22. package/admin/src/pages/Providers/{index.js → index.jsx} +98 -113
  23. package/admin/src/pages/Providers/utils/forms.js +23 -11
  24. package/admin/src/pages/Roles/constants.js +3 -3
  25. package/admin/src/pages/Roles/hooks/usePlugins.js +4 -4
  26. package/admin/src/pages/Roles/index.jsx +24 -0
  27. package/admin/src/pages/Roles/pages/{CreatePage.js → CreatePage.jsx} +53 -58
  28. package/admin/src/pages/Roles/pages/{EditPage.js → EditPage.jsx} +63 -68
  29. package/admin/src/pages/Roles/pages/ListPage/components/{TableBody.js → TableBody.jsx} +27 -31
  30. package/admin/src/pages/Roles/pages/ListPage/{index.js → index.jsx} +79 -55
  31. package/admin/src/translations/en.json +1 -1
  32. package/admin/src/utils/prefixPluginTranslations.js +13 -0
  33. package/dist/_chunks/ar-BguGUqwK.js +44 -0
  34. package/dist/_chunks/ar-BguGUqwK.js.map +1 -0
  35. package/dist/_chunks/ar-CK8BRRXB.mjs +44 -0
  36. package/dist/_chunks/ar-CK8BRRXB.mjs.map +1 -0
  37. package/dist/_chunks/cs-BVigMk0l.mjs +50 -0
  38. package/dist/_chunks/cs-BVigMk0l.mjs.map +1 -0
  39. package/dist/_chunks/cs-BW8-K_GY.js +50 -0
  40. package/dist/_chunks/cs-BW8-K_GY.js.map +1 -0
  41. package/dist/_chunks/de-BKUdRFI4.mjs +62 -0
  42. package/dist/_chunks/de-BKUdRFI4.mjs.map +1 -0
  43. package/dist/_chunks/de-owXpVluI.js +62 -0
  44. package/dist/_chunks/de-owXpVluI.js.map +1 -0
  45. package/dist/_chunks/dk-BQiTK50l.mjs +86 -0
  46. package/dist/_chunks/dk-BQiTK50l.mjs.map +1 -0
  47. package/dist/_chunks/dk-LXAnbuBk.js +86 -0
  48. package/dist/_chunks/dk-LXAnbuBk.js.map +1 -0
  49. package/dist/_chunks/en-DOHtPf-2.mjs +86 -0
  50. package/dist/_chunks/en-DOHtPf-2.mjs.map +1 -0
  51. package/dist/_chunks/en-MHo5mcsU.js +86 -0
  52. package/dist/_chunks/en-MHo5mcsU.js.map +1 -0
  53. package/dist/_chunks/es-BwLCLXAQ.js +86 -0
  54. package/dist/_chunks/es-BwLCLXAQ.js.map +1 -0
  55. package/dist/_chunks/es-DNgOVMjD.mjs +86 -0
  56. package/dist/_chunks/es-DNgOVMjD.mjs.map +1 -0
  57. package/dist/_chunks/fr-DkgRugiU.mjs +50 -0
  58. package/dist/_chunks/fr-DkgRugiU.mjs.map +1 -0
  59. package/dist/_chunks/fr-DkhpSjjm.js +50 -0
  60. package/dist/_chunks/fr-DkhpSjjm.js.map +1 -0
  61. package/dist/_chunks/id-BTemOeTZ.js +62 -0
  62. package/dist/_chunks/id-BTemOeTZ.js.map +1 -0
  63. package/dist/_chunks/id-BdEsvnaF.mjs +62 -0
  64. package/dist/_chunks/id-BdEsvnaF.mjs.map +1 -0
  65. package/dist/_chunks/index-6IfauKkm.js +365 -0
  66. package/dist/_chunks/index-6IfauKkm.js.map +1 -0
  67. package/dist/_chunks/index-BH2Kbto3.mjs +344 -0
  68. package/dist/_chunks/index-BH2Kbto3.mjs.map +1 -0
  69. package/dist/_chunks/index-BPLSKK9l.mjs +1142 -0
  70. package/dist/_chunks/index-BPLSKK9l.mjs.map +1 -0
  71. package/dist/_chunks/index-BteVBZ5H.js +252 -0
  72. package/dist/_chunks/index-BteVBZ5H.js.map +1 -0
  73. package/dist/_chunks/index-CItjLvCp.mjs +617 -0
  74. package/dist/_chunks/index-CItjLvCp.mjs.map +1 -0
  75. package/dist/_chunks/index-CU5dmnhc.js +280 -0
  76. package/dist/_chunks/index-CU5dmnhc.js.map +1 -0
  77. package/dist/_chunks/index-D4QvcqXm-BWNRataA.mjs +11090 -0
  78. package/dist/_chunks/index-D4QvcqXm-BWNRataA.mjs.map +1 -0
  79. package/dist/_chunks/index-D4QvcqXm-HpyyuCo7.js +11113 -0
  80. package/dist/_chunks/index-D4QvcqXm-HpyyuCo7.js.map +1 -0
  81. package/dist/_chunks/index-DDd0fVR6.js +1171 -0
  82. package/dist/_chunks/index-DDd0fVR6.js.map +1 -0
  83. package/dist/_chunks/index-DMkiPQOl.mjs +262 -0
  84. package/dist/_chunks/index-DMkiPQOl.mjs.map +1 -0
  85. package/dist/_chunks/index-DZnDAnLg.mjs +253 -0
  86. package/dist/_chunks/index-DZnDAnLg.mjs.map +1 -0
  87. package/dist/_chunks/index-Dd0JjBda.js +639 -0
  88. package/dist/_chunks/index-Dd0JjBda.js.map +1 -0
  89. package/dist/_chunks/it-B-rv0E24.mjs +62 -0
  90. package/dist/_chunks/it-B-rv0E24.mjs.map +1 -0
  91. package/dist/_chunks/it-D1rH6V6_.js +62 -0
  92. package/dist/_chunks/it-D1rH6V6_.js.map +1 -0
  93. package/dist/_chunks/ja-C8K-VBPD.mjs +48 -0
  94. package/dist/_chunks/ja-C8K-VBPD.mjs.map +1 -0
  95. package/dist/_chunks/ja-DqShgTMf.js +48 -0
  96. package/dist/_chunks/ja-DqShgTMf.js.map +1 -0
  97. package/dist/_chunks/ko-B9DGEPWH.js +86 -0
  98. package/dist/_chunks/ko-B9DGEPWH.js.map +1 -0
  99. package/dist/_chunks/ko-Busb0wIY.mjs +86 -0
  100. package/dist/_chunks/ko-Busb0wIY.mjs.map +1 -0
  101. package/dist/_chunks/ms-ByvsQjRt.mjs +49 -0
  102. package/dist/_chunks/ms-ByvsQjRt.mjs.map +1 -0
  103. package/dist/_chunks/ms-CPBU3LWf.js +49 -0
  104. package/dist/_chunks/ms-CPBU3LWf.js.map +1 -0
  105. package/dist/_chunks/nl-5qO8Rpcy.mjs +48 -0
  106. package/dist/_chunks/nl-5qO8Rpcy.mjs.map +1 -0
  107. package/dist/_chunks/nl-CwNB6YoO.js +48 -0
  108. package/dist/_chunks/nl-CwNB6YoO.js.map +1 -0
  109. package/dist/_chunks/pl-BdIzifBE.mjs +86 -0
  110. package/dist/_chunks/pl-BdIzifBE.mjs.map +1 -0
  111. package/dist/_chunks/pl-Do9UD69f.js +86 -0
  112. package/dist/_chunks/pl-Do9UD69f.js.map +1 -0
  113. package/dist/_chunks/pt-BIO24ioG.mjs +48 -0
  114. package/dist/_chunks/pt-BIO24ioG.mjs.map +1 -0
  115. package/dist/_chunks/pt-BR-D7dZhxuP.js +44 -0
  116. package/dist/_chunks/pt-BR-D7dZhxuP.js.map +1 -0
  117. package/dist/_chunks/pt-BR-f0p23AQZ.mjs +44 -0
  118. package/dist/_chunks/pt-BR-f0p23AQZ.mjs.map +1 -0
  119. package/dist/_chunks/pt-fdvyOnUp.js +48 -0
  120. package/dist/_chunks/pt-fdvyOnUp.js.map +1 -0
  121. package/dist/_chunks/ru-C94rjPGA.js +86 -0
  122. package/dist/_chunks/ru-C94rjPGA.js.map +1 -0
  123. package/dist/_chunks/ru-VWy-IB7K.mjs +86 -0
  124. package/dist/_chunks/ru-VWy-IB7K.mjs.map +1 -0
  125. package/dist/_chunks/sk-BABEhykl.js +50 -0
  126. package/dist/_chunks/sk-BABEhykl.js.map +1 -0
  127. package/dist/_chunks/sk-B_LIcepm.mjs +50 -0
  128. package/dist/_chunks/sk-B_LIcepm.mjs.map +1 -0
  129. package/dist/_chunks/sv-ABLKOokl.mjs +86 -0
  130. package/dist/_chunks/sv-ABLKOokl.mjs.map +1 -0
  131. package/dist/_chunks/sv-Be43LhA9.js +86 -0
  132. package/dist/_chunks/sv-Be43LhA9.js.map +1 -0
  133. package/dist/_chunks/th-DKyP7ueR.mjs +60 -0
  134. package/dist/_chunks/th-DKyP7ueR.mjs.map +1 -0
  135. package/dist/_chunks/th-DgVhVLhL.js +60 -0
  136. package/dist/_chunks/th-DgVhVLhL.js.map +1 -0
  137. package/dist/_chunks/tr-B_idhkEs.js +85 -0
  138. package/dist/_chunks/tr-B_idhkEs.js.map +1 -0
  139. package/dist/_chunks/tr-qa1Q5UjC.mjs +85 -0
  140. package/dist/_chunks/tr-qa1Q5UjC.mjs.map +1 -0
  141. package/dist/_chunks/uk-BmRqbeQc.mjs +49 -0
  142. package/dist/_chunks/uk-BmRqbeQc.mjs.map +1 -0
  143. package/dist/_chunks/uk-LHOivnhP.js +49 -0
  144. package/dist/_chunks/uk-LHOivnhP.js.map +1 -0
  145. package/dist/_chunks/vi-CdVRdKDw.js +50 -0
  146. package/dist/_chunks/vi-CdVRdKDw.js.map +1 -0
  147. package/dist/_chunks/vi-HW-EdMea.mjs +50 -0
  148. package/dist/_chunks/vi-HW-EdMea.mjs.map +1 -0
  149. package/dist/_chunks/zh-5hKkVPA4.mjs +86 -0
  150. package/dist/_chunks/zh-5hKkVPA4.mjs.map +1 -0
  151. package/dist/_chunks/zh-Cuq8gMnF.js +86 -0
  152. package/dist/_chunks/zh-Cuq8gMnF.js.map +1 -0
  153. package/dist/_chunks/zh-Hans-BHilK-yc.mjs +86 -0
  154. package/dist/_chunks/zh-Hans-BHilK-yc.mjs.map +1 -0
  155. package/dist/_chunks/zh-Hans-GQDMKtY4.js +86 -0
  156. package/dist/_chunks/zh-Hans-GQDMKtY4.js.map +1 -0
  157. package/dist/admin/index.js +4 -0
  158. package/dist/admin/index.js.map +1 -0
  159. package/dist/admin/index.mjs +5 -0
  160. package/dist/admin/index.mjs.map +1 -0
  161. package/package.json +45 -27
  162. package/packup.config.ts +22 -0
  163. package/server/bootstrap/index.js +18 -51
  164. package/server/bootstrap/users-permissions-actions.js +6 -0
  165. package/server/config.js +29 -0
  166. package/server/content-types/user/index.js +0 -1
  167. package/server/controllers/auth.js +62 -63
  168. package/server/controllers/content-manager-user.js +28 -30
  169. package/server/controllers/role.js +17 -4
  170. package/server/controllers/user.js +8 -9
  171. package/server/controllers/validation/auth.js +81 -25
  172. package/server/graphql/types/index.js +1 -0
  173. package/server/graphql/types/me.js +1 -0
  174. package/server/graphql/types/user-input.js +20 -0
  175. package/server/middlewares/rateLimit.js +1 -1
  176. package/server/register.js +1 -1
  177. package/server/services/jwt.js +3 -3
  178. package/server/services/permission.js +3 -7
  179. package/server/services/providers-registry.js +469 -261
  180. package/server/services/providers.js +10 -5
  181. package/server/services/role.js +15 -13
  182. package/server/services/user.js +56 -19
  183. package/server/services/users-permissions.js +15 -13
  184. package/server/utils/index.d.ts +2 -1
  185. package/server/utils/sanitize/sanitizers.js +7 -3
  186. package/server/utils/sanitize/visitors/remove-user-relation-from-role-entities.js +2 -2
  187. package/.eslintrc.js +0 -14
  188. package/admin/src/components/FormModal/index.js +0 -126
  189. package/admin/src/components/Permissions/index.js +0 -55
  190. package/admin/src/pages/AdvancedSettings/index.js +0 -259
  191. package/admin/src/pages/EmailTemplates/components/EmailForm.js +0 -176
  192. package/admin/src/pages/Roles/index.js +0 -33
  193. package/admin/src/pages/Roles/pages/ListPage/utils/api.js +0 -30
  194. package/server/bootstrap/grant-config.js +0 -131
  195. package/strapi-admin.js +0 -3
  196. package/strapi-server.js +0 -3
  197. /package/admin/src/components/Permissions/PermissionRow/{index.js → index.jsx} +0 -0
  198. /package/admin/src/contexts/UsersPermissionsContext/{index.js → index.jsx} +0 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { strict: assert } = require('assert');
4
4
  const jwt = require('jsonwebtoken');
5
+ const urljoin = require('url-join');
5
6
  const jwkToPem = require('jwk-to-pem');
6
7
 
7
8
  const getCognitoPayload = async ({ idToken, jwksUrl, purest }) => {
@@ -45,315 +46,522 @@ const getCognitoPayload = async ({ idToken, jwksUrl, purest }) => {
45
46
  }
46
47
  };
47
48
 
48
- const getInitialProviders = ({ purest }) => ({
49
- async discord({ accessToken }) {
50
- const discord = purest({ provider: 'discord' });
51
-
52
- return discord
53
- .get('users/@me')
54
- .auth(accessToken)
55
- .request()
56
- .then(({ body }) => {
57
- // Combine username and discriminator because discord username is not unique
58
- const username = `${body.username}#${body.discriminator}`;
59
- return {
60
- username,
61
- email: body.email,
62
- };
63
- });
49
+ const initProviders = ({ baseURL, purest }) => ({
50
+ email: {
51
+ enabled: true,
52
+ icon: 'envelope',
53
+ grantConfig: {},
64
54
  },
65
- async cognito({ query, providers }) {
66
- const jwksUrl = new URL(providers.cognito.jwksurl);
67
- const idToken = query.id_token;
68
- const tokenPayload = await getCognitoPayload({ idToken, jwksUrl, purest });
69
- return {
70
- username: tokenPayload['cognito:username'],
71
- email: tokenPayload.email,
72
- };
55
+ discord: {
56
+ enabled: false,
57
+ icon: 'discord',
58
+ grantConfig: {
59
+ key: '',
60
+ secret: '',
61
+ callbackUrl: `${baseURL}/discord/callback`,
62
+ scope: ['identify', 'email'],
63
+ },
64
+ async authCallback({ accessToken }) {
65
+ const discord = purest({ provider: 'discord' });
66
+
67
+ return discord
68
+ .get('users/@me')
69
+ .auth(accessToken)
70
+ .request()
71
+ .then(({ body }) => {
72
+ // Combine username and discriminator (if discriminator exists and not equal to 0)
73
+ const username =
74
+ body.discriminator && body.discriminator !== '0'
75
+ ? `${body.username}#${body.discriminator}`
76
+ : body.username;
77
+ return {
78
+ username,
79
+ email: body.email,
80
+ };
81
+ });
82
+ },
73
83
  },
74
- async facebook({ accessToken }) {
75
- const facebook = purest({ provider: 'facebook' });
76
-
77
- return facebook
78
- .get('me')
79
- .auth(accessToken)
80
- .qs({ fields: 'name,email' })
81
- .request()
82
- .then(({ body }) => ({
83
- username: body.name,
84
- email: body.email,
85
- }));
84
+ facebook: {
85
+ enabled: false,
86
+ icon: 'facebook-square',
87
+ grantConfig: {
88
+ key: '',
89
+ secret: '',
90
+ callbackUrl: `${baseURL}/facebook/callback`,
91
+ scope: ['email'],
92
+ },
93
+ async authCallback({ accessToken }) {
94
+ const facebook = purest({ provider: 'facebook' });
95
+
96
+ return facebook
97
+ .get('me')
98
+ .auth(accessToken)
99
+ .qs({ fields: 'name,email' })
100
+ .request()
101
+ .then(({ body }) => ({
102
+ username: body.name,
103
+ email: body.email,
104
+ }));
105
+ },
86
106
  },
87
- async google({ accessToken }) {
88
- const google = purest({ provider: 'google' });
89
-
90
- return google
91
- .query('oauth')
92
- .get('tokeninfo')
93
- .qs({ accessToken })
94
- .request()
95
- .then(({ body }) => ({
96
- username: body.email.split('@')[0],
97
- email: body.email,
98
- }));
107
+ google: {
108
+ enabled: false,
109
+ icon: 'google',
110
+ grantConfig: {
111
+ key: '',
112
+ secret: '',
113
+ callbackUrl: `${baseURL}/google/callback`,
114
+ scope: ['email'],
115
+ },
116
+ async authCallback({ accessToken }) {
117
+ const google = purest({ provider: 'google' });
118
+
119
+ return google
120
+ .query('oauth')
121
+ .get('tokeninfo')
122
+ .qs({ accessToken })
123
+ .request()
124
+ .then(({ body }) => ({
125
+ username: body.email.split('@')[0],
126
+ email: body.email,
127
+ }));
128
+ },
99
129
  },
100
- async github({ accessToken }) {
101
- const github = purest({
102
- provider: 'github',
103
- defaults: {
104
- headers: {
105
- 'user-agent': 'strapi',
130
+ github: {
131
+ enabled: false,
132
+ icon: 'github',
133
+ grantConfig: {
134
+ key: '',
135
+ secret: '',
136
+ callbackUrl: `${baseURL}/github/callback`,
137
+ scope: ['user', 'user:email'],
138
+ },
139
+ async authCallback({ accessToken }) {
140
+ const github = purest({
141
+ provider: 'github',
142
+ defaults: {
143
+ headers: {
144
+ 'user-agent': 'strapi',
145
+ },
106
146
  },
107
- },
108
- });
147
+ });
109
148
 
110
- const { body: userBody } = await github.get('user').auth(accessToken).request();
149
+ const { body: userBody } = await github.get('user').auth(accessToken).request();
150
+
151
+ // This is the public email on the github profile
152
+ if (userBody.email) {
153
+ return {
154
+ username: userBody.login,
155
+ email: userBody.email,
156
+ };
157
+ }
158
+ // Get the email with Github's user/emails API
159
+ const { body: emailBody } = await github.get('user/emails').auth(accessToken).request();
111
160
 
112
- // This is the public email on the github profile
113
- if (userBody.email) {
114
161
  return {
115
162
  username: userBody.login,
116
- email: userBody.email,
163
+ email: Array.isArray(emailBody)
164
+ ? emailBody.find((email) => email.primary === true).email
165
+ : null,
117
166
  };
118
- }
119
- // Get the email with Github's user/emails API
120
- const { body: emailBody } = await github.get('user/emails').auth(accessToken).request();
121
-
122
- return {
123
- username: userBody.login,
124
- email: Array.isArray(emailBody)
125
- ? emailBody.find((email) => email.primary === true).email
126
- : null,
127
- };
167
+ },
128
168
  },
129
- async microsoft({ accessToken }) {
130
- const microsoft = purest({ provider: 'microsoft' });
131
-
132
- return microsoft
133
- .get('me')
134
- .auth(accessToken)
135
- .request()
136
- .then(({ body }) => ({
137
- username: body.userPrincipalName,
138
- email: body.userPrincipalName,
139
- }));
169
+ microsoft: {
170
+ enabled: false,
171
+ icon: 'windows',
172
+ grantConfig: {
173
+ key: '',
174
+ secret: '',
175
+ callbackUrl: `${baseURL}/microsoft/callback`,
176
+ scope: ['user.read'],
177
+ },
178
+ async authCallback({ accessToken }) {
179
+ const microsoft = purest({ provider: 'microsoft' });
180
+
181
+ return microsoft
182
+ .get('me')
183
+ .auth(accessToken)
184
+ .request()
185
+ .then(({ body }) => ({
186
+ username: body.userPrincipalName,
187
+ email: body.userPrincipalName,
188
+ }));
189
+ },
140
190
  },
141
- async twitter({ accessToken, query, providers }) {
142
- const twitter = purest({
143
- provider: 'twitter',
144
- defaults: {
145
- oauth: {
146
- consumer_key: providers.twitter.key,
147
- consumer_secret: providers.twitter.secret,
191
+
192
+ twitter: {
193
+ enabled: false,
194
+ icon: 'twitter',
195
+ grantConfig: {
196
+ key: '',
197
+ secret: '',
198
+ callbackUrl: `${baseURL}/twitter/callback`,
199
+ },
200
+ async authCallback({ accessToken, query, providers }) {
201
+ const twitter = purest({
202
+ provider: 'twitter',
203
+ defaults: {
204
+ oauth: {
205
+ consumer_key: providers.twitter.key,
206
+ consumer_secret: providers.twitter.secret,
207
+ },
148
208
  },
149
- },
150
- });
209
+ });
151
210
 
152
- return twitter
153
- .get('account/verify_credentials')
154
- .auth(accessToken, query.access_secret)
155
- .qs({ screen_name: query['raw[screen_name]'], include_email: 'true' })
156
- .request()
157
- .then(({ body }) => ({
158
- username: body.screen_name,
159
- email: body.email,
160
- }));
211
+ return twitter
212
+ .get('account/verify_credentials')
213
+ .auth(accessToken, query.access_secret)
214
+ .qs({ screen_name: query['raw[screen_name]'], include_email: 'true' })
215
+ .request()
216
+ .then(({ body }) => ({
217
+ username: body.screen_name,
218
+ email: body.email,
219
+ }));
220
+ },
161
221
  },
162
- async instagram({ accessToken }) {
163
- const instagram = purest({ provider: 'instagram' });
164
-
165
- return instagram
166
- .get('me')
167
- .auth(accessToken)
168
- .qs({ fields: 'id,username' })
169
- .request()
170
- .then(({ body }) => ({
171
- username: body.username,
172
- email: `${body.username}@strapi.io`, // dummy email as Instagram does not provide user email
173
- }));
222
+ instagram: {
223
+ enabled: false,
224
+ icon: 'instagram',
225
+ grantConfig: {
226
+ key: '',
227
+ secret: '',
228
+ callbackUrl: `${baseURL}/instagram/callback`,
229
+ scope: ['user_profile'],
230
+ },
231
+ async authCallback({ accessToken }) {
232
+ const instagram = purest({ provider: 'instagram' });
233
+
234
+ return instagram
235
+ .get('me')
236
+ .auth(accessToken)
237
+ .qs({ fields: 'id,username' })
238
+ .request()
239
+ .then(({ body }) => ({
240
+ username: body.username,
241
+ email: `${body.username}@strapi.io`, // dummy email as Instagram does not provide user email
242
+ }));
243
+ },
174
244
  },
175
- async vk({ accessToken, query }) {
176
- const vk = purest({ provider: 'vk' });
177
-
178
- return vk
179
- .get('users')
180
- .auth(accessToken)
181
- .qs({ id: query.raw.user_id, v: '5.122' })
182
- .request()
183
- .then(({ body }) => ({
184
- username: `${body.response[0].last_name} ${body.response[0].first_name}`,
185
- email: query.raw.email,
186
- }));
245
+ vk: {
246
+ enabled: false,
247
+ icon: 'vk',
248
+ grantConfig: {
249
+ key: '',
250
+ secret: '',
251
+ callbackUrl: `${baseURL}/vk/callback`,
252
+ scope: ['email'],
253
+ },
254
+ async authCallback({ accessToken, query }) {
255
+ const vk = purest({ provider: 'vk' });
256
+
257
+ return vk
258
+ .get('users')
259
+ .auth(accessToken)
260
+ .qs({ id: query.raw.user_id, v: '5.122' })
261
+ .request()
262
+ .then(({ body }) => ({
263
+ username: `${body.response[0].last_name} ${body.response[0].first_name}`,
264
+ email: query.raw.email,
265
+ }));
266
+ },
187
267
  },
188
- async twitch({ accessToken, providers }) {
189
- const twitch = purest({
190
- provider: 'twitch',
191
- config: {
192
- twitch: {
193
- default: {
194
- origin: 'https://api.twitch.tv',
195
- path: 'helix/{path}',
196
- headers: {
197
- Authorization: 'Bearer {auth}',
198
- 'Client-Id': '{auth}',
268
+
269
+ twitch: {
270
+ enabled: false,
271
+ icon: 'twitch',
272
+ grantConfig: {
273
+ key: '',
274
+ secret: '',
275
+ callbackUrl: `${baseURL}/twitch/callback`,
276
+ scope: ['user:read:email'],
277
+ },
278
+ async authCallback({ accessToken, providers }) {
279
+ const twitch = purest({
280
+ provider: 'twitch',
281
+ config: {
282
+ twitch: {
283
+ default: {
284
+ origin: 'https://api.twitch.tv',
285
+ path: 'helix/{path}',
286
+ headers: {
287
+ Authorization: 'Bearer {auth}',
288
+ 'Client-Id': '{auth}',
289
+ },
199
290
  },
200
291
  },
201
292
  },
202
- },
203
- });
293
+ });
204
294
 
205
- return twitch
206
- .get('users')
207
- .auth(accessToken, providers.twitch.key)
208
- .request()
209
- .then(({ body }) => ({
210
- username: body.data[0].login,
211
- email: body.data[0].email,
212
- }));
295
+ return twitch
296
+ .get('users')
297
+ .auth(accessToken, providers.twitch.key)
298
+ .request()
299
+ .then(({ body }) => ({
300
+ username: body.data[0].login,
301
+ email: body.data[0].email,
302
+ }));
303
+ },
213
304
  },
214
- async linkedin({ accessToken }) {
215
- const linkedIn = purest({ provider: 'linkedin' });
216
- const {
217
- body: { localizedFirstName },
218
- } = await linkedIn.get('me').auth(accessToken).request();
219
- const {
220
- body: { elements },
221
- } = await linkedIn
222
- .get('emailAddress?q=members&projection=(elements*(handle~))')
223
- .auth(accessToken)
224
- .request();
225
-
226
- const email = elements[0]['handle~'];
227
-
228
- return {
229
- username: localizedFirstName,
230
- email: email.emailAddress,
231
- };
305
+
306
+ linkedin: {
307
+ enabled: false,
308
+ icon: 'linkedin',
309
+ grantConfig: {
310
+ key: '',
311
+ secret: '',
312
+ callbackUrl: `${baseURL}/linkedin/callback`,
313
+ scope: ['r_liteprofile', 'r_emailaddress'],
314
+ },
315
+ async authCallback({ accessToken }) {
316
+ const linkedIn = purest({ provider: 'linkedin' });
317
+ const {
318
+ body: { localizedFirstName },
319
+ } = await linkedIn.get('me').auth(accessToken).request();
320
+ const {
321
+ body: { elements },
322
+ } = await linkedIn
323
+ .get('emailAddress?q=members&projection=(elements*(handle~))')
324
+ .auth(accessToken)
325
+ .request();
326
+
327
+ const email = elements[0]['handle~'];
328
+
329
+ return {
330
+ username: localizedFirstName,
331
+ email: email.emailAddress,
332
+ };
333
+ },
232
334
  },
233
- async reddit({ accessToken }) {
234
- const reddit = purest({
235
- provider: 'reddit',
236
- config: {
237
- reddit: {
238
- default: {
239
- origin: 'https://oauth.reddit.com',
240
- path: 'api/{version}/{path}',
241
- version: 'v1',
242
- headers: {
243
- Authorization: 'Bearer {auth}',
244
- 'user-agent': 'strapi',
335
+
336
+ cognito: {
337
+ enabled: false,
338
+ icon: 'aws',
339
+ grantConfig: {
340
+ key: '',
341
+ secret: '',
342
+ subdomain: 'my.subdomain.com',
343
+ callback: `${baseURL}/cognito/callback`,
344
+ scope: ['email', 'openid', 'profile'],
345
+ },
346
+ async authCallback({ query, providers }) {
347
+ const jwksUrl = new URL(providers.cognito.jwksurl);
348
+ const idToken = query.id_token;
349
+ const tokenPayload = await getCognitoPayload({ idToken, jwksUrl, purest });
350
+ return {
351
+ username: tokenPayload['cognito:username'],
352
+ email: tokenPayload.email,
353
+ };
354
+ },
355
+ },
356
+
357
+ reddit: {
358
+ enabled: false,
359
+ icon: 'reddit',
360
+ grantConfig: {
361
+ key: '',
362
+ secret: '',
363
+ callback: `${baseURL}/reddit/callback`,
364
+ scope: ['identity'],
365
+ },
366
+ async authCallback({ accessToken }) {
367
+ const reddit = purest({
368
+ provider: 'reddit',
369
+ config: {
370
+ reddit: {
371
+ default: {
372
+ origin: 'https://oauth.reddit.com',
373
+ path: 'api/{version}/{path}',
374
+ version: 'v1',
375
+ headers: {
376
+ Authorization: 'Bearer {auth}',
377
+ 'user-agent': 'strapi',
378
+ },
245
379
  },
246
380
  },
247
381
  },
248
- },
249
- });
382
+ });
250
383
 
251
- return reddit
252
- .get('me')
253
- .auth(accessToken)
254
- .request()
255
- .then(({ body }) => ({
256
- username: body.name,
257
- email: `${body.name}@strapi.io`, // dummy email as Reddit does not provide user email
258
- }));
384
+ return reddit
385
+ .get('me')
386
+ .auth(accessToken)
387
+ .request()
388
+ .then(({ body }) => ({
389
+ username: body.name,
390
+ email: `${body.name}@strapi.io`, // dummy email as Reddit does not provide user email
391
+ }));
392
+ },
259
393
  },
260
- async auth0({ accessToken, providers }) {
261
- const auth0 = purest({ provider: 'auth0' });
262
-
263
- return auth0
264
- .get('userinfo')
265
- .subdomain(providers.auth0.subdomain)
266
- .auth(accessToken)
267
- .request()
268
- .then(({ body }) => {
269
- const username = body.username || body.nickname || body.name || body.email.split('@')[0];
270
- const email = body.email || `${username.replace(/\s+/g, '.')}@strapi.io`;
271
394
 
272
- return {
273
- username,
274
- email,
275
- };
276
- });
395
+ auth0: {
396
+ enabled: false,
397
+ icon: '',
398
+ grantConfig: {
399
+ key: '',
400
+ secret: '',
401
+ subdomain: 'my-tenant.eu',
402
+ callback: `${baseURL}/auth0/callback`,
403
+ scope: ['openid', 'email', 'profile'],
404
+ },
405
+ async authCallback({ accessToken, providers }) {
406
+ const auth0 = purest({ provider: 'auth0' });
407
+
408
+ return auth0
409
+ .get('userinfo')
410
+ .subdomain(providers.auth0.subdomain)
411
+ .auth(accessToken)
412
+ .request()
413
+ .then(({ body }) => {
414
+ const username = body.username || body.nickname || body.name || body.email.split('@')[0];
415
+ const email = body.email || `${username.replace(/\s+/g, '.')}@strapi.io`;
416
+
417
+ return {
418
+ username,
419
+ email,
420
+ };
421
+ });
422
+ },
277
423
  },
278
- async cas({ accessToken, providers }) {
279
- const cas = purest({ provider: 'cas' });
280
-
281
- return cas
282
- .get('oidc/profile')
283
- .subdomain(providers.cas.subdomain)
284
- .auth(accessToken)
285
- .request()
286
- .then(({ body }) => {
287
- // CAS attribute may be in body.attributes or "FLAT", depending on CAS config
288
- const username = body.attributes
289
- ? body.attributes.strapiusername || body.id || body.sub
290
- : body.strapiusername || body.id || body.sub;
291
- const email = body.attributes
292
- ? body.attributes.strapiemail || body.attributes.email
293
- : body.strapiemail || body.email;
294
- if (!username || !email) {
295
- strapi.log.warn(
296
- `CAS Response Body did not contain required attributes: ${JSON.stringify(body)}`
297
- );
298
- }
299
- return {
300
- username,
301
- email,
302
- };
303
- });
424
+
425
+ cas: {
426
+ enabled: false,
427
+ icon: 'book',
428
+ grantConfig: {
429
+ key: '',
430
+ secret: '',
431
+ callback: `${baseURL}/cas/callback`,
432
+ scope: ['openid email'], // scopes should be space delimited
433
+ subdomain: 'my.subdomain.com/cas',
434
+ },
435
+ async authCallback({ accessToken, providers }) {
436
+ const cas = purest({ provider: 'cas' });
437
+
438
+ return cas
439
+ .get('oidc/profile')
440
+ .subdomain(providers.cas.subdomain)
441
+ .auth(accessToken)
442
+ .request()
443
+ .then(({ body }) => {
444
+ // CAS attribute may be in body.attributes or "FLAT", depending on CAS config
445
+ const username = body.attributes
446
+ ? body.attributes.strapiusername || body.id || body.sub
447
+ : body.strapiusername || body.id || body.sub;
448
+ const email = body.attributes
449
+ ? body.attributes.strapiemail || body.attributes.email
450
+ : body.strapiemail || body.email;
451
+ if (!username || !email) {
452
+ strapi.log.warn(
453
+ `CAS Response Body did not contain required attributes: ${JSON.stringify(body)}`
454
+ );
455
+ }
456
+ return {
457
+ username,
458
+ email,
459
+ };
460
+ });
461
+ },
304
462
  },
305
- async patreon({ accessToken }) {
306
- const patreon = purest({
307
- provider: 'patreon',
308
- config: {
309
- patreon: {
310
- default: {
311
- origin: 'https://www.patreon.com',
312
- path: 'api/oauth2/{path}',
313
- headers: {
314
- authorization: 'Bearer {auth}',
463
+
464
+ patreon: {
465
+ enabled: false,
466
+ icon: '',
467
+ grantConfig: {
468
+ key: '',
469
+ secret: '',
470
+ callback: `${baseURL}/patreon/callback`,
471
+ scope: ['identity', 'identity[email]'],
472
+ },
473
+ async authCallback({ accessToken }) {
474
+ const patreon = purest({
475
+ provider: 'patreon',
476
+ config: {
477
+ patreon: {
478
+ default: {
479
+ origin: 'https://www.patreon.com',
480
+ path: 'api/oauth2/{path}',
481
+ headers: {
482
+ authorization: 'Bearer {auth}',
483
+ },
315
484
  },
316
485
  },
317
486
  },
318
- },
319
- });
320
-
321
- return patreon
322
- .get('v2/identity')
323
- .auth(accessToken)
324
- .qs(new URLSearchParams({ 'fields[user]': 'full_name,email' }).toString())
325
- .request()
326
- .then(({ body }) => {
327
- const patreonData = body.data.attributes;
328
- return {
329
- username: patreonData.full_name,
330
- email: patreonData.email,
331
- };
332
487
  });
488
+
489
+ return patreon
490
+ .get('v2/identity')
491
+ .auth(accessToken)
492
+ .qs(new URLSearchParams({ 'fields[user]': 'full_name,email' }).toString())
493
+ .request()
494
+ .then(({ body }) => {
495
+ const patreonData = body.data.attributes;
496
+ return {
497
+ username: patreonData.full_name,
498
+ email: patreonData.email,
499
+ };
500
+ });
501
+ },
502
+ },
503
+ keycloak: {
504
+ enabled: false,
505
+ icon: '',
506
+ grantConfig: {
507
+ key: '',
508
+ secret: '',
509
+ subdomain: 'myKeycloakProvider.com/realms/myrealm',
510
+ callback: `${baseURL}/keycloak/callback`,
511
+ scope: ['openid', 'email', 'profile'],
512
+ },
513
+ async authCallback({ accessToken, providers }) {
514
+ const keycloak = purest({ provider: 'keycloak' });
515
+
516
+ return keycloak
517
+ .subdomain(providers.keycloak.subdomain)
518
+ .get('protocol/openid-connect/userinfo')
519
+ .auth(accessToken)
520
+ .request()
521
+ .then(({ body }) => {
522
+ return {
523
+ username: body.preferred_username,
524
+ email: body.email,
525
+ };
526
+ });
527
+ },
333
528
  },
334
529
  });
335
530
 
336
531
  module.exports = () => {
337
532
  const purest = require('purest');
338
533
 
339
- const providersCallbacks = getInitialProviders({ purest });
534
+ const apiPrefix = strapi.config.get('api.rest.prefix');
535
+ const baseURL = urljoin(strapi.config.server.url, apiPrefix, 'auth');
340
536
 
341
- return {
342
- register(providerName, provider) {
343
- assert(typeof providerName === 'string', 'Provider name must be a string');
344
- assert(typeof provider === 'function', 'Provider callback must be a function');
537
+ const authProviders = initProviders({ baseURL, purest });
345
538
 
346
- providersCallbacks[providerName] = provider({ purest });
539
+ /**
540
+ * @public
541
+ */
542
+ return {
543
+ getAll() {
544
+ return authProviders;
545
+ },
546
+ get(name) {
547
+ return authProviders[name];
548
+ },
549
+ add(name, config) {
550
+ authProviders[name] = config;
551
+ },
552
+ remove(name) {
553
+ delete authProviders[name];
347
554
  },
348
555
 
556
+ /**
557
+ * @internal
558
+ */
349
559
  async run({ provider, accessToken, query, providers }) {
350
- if (!providersCallbacks[provider]) {
351
- throw new Error('Unknown provider.');
352
- }
560
+ const authProvider = authProviders[provider];
353
561
 
354
- const providerCb = providersCallbacks[provider];
562
+ assert(authProvider, 'Unknown auth provider');
355
563
 
356
- return providerCb({ accessToken, query, providers });
564
+ return authProvider.authCallback({ accessToken, query, providers, purest });
357
565
  },
358
566
  };
359
567
  };