2017-11-14 11:49:19 +01:00
'use strict' ;
/ * *
* Auth . js controller
*
* @ description : A set of functions called "actions" for managing ` Auth ` .
* /
2018-04-30 18:26:56 +02:00
/* eslint-disable no-useless-escape */
2017-11-16 18:00:15 +01:00
const crypto = require ( 'crypto' ) ;
2018-04-30 18:26:56 +02:00
const _ = require ( 'lodash' ) ;
2021-11-05 10:40:11 +01:00
const utils = require ( '@strapi/utils' ) ;
2021-07-08 18:15:32 +02:00
const { getService } = require ( '../utils' ) ;
2021-10-20 17:30:05 +02:00
const {
validateCallbackBody ,
validateRegisterBody ,
validateSendEmailConfirmationBody ,
2022-05-31 19:38:08 +02:00
validateForgotPasswordBody ,
2021-10-20 17:30:05 +02:00
} = require ( './validation/auth' ) ;
2019-01-18 16:08:15 +01:00
2022-03-21 11:04:54 +01:00
const { getAbsoluteAdminUrl , getAbsoluteServerUrl , sanitize } = utils ;
2021-11-05 10:40:11 +01:00
const { ApplicationError , ValidationError } = utils . errors ;
2019-01-18 16:08:15 +01:00
2018-01-23 13:35:51 +01:00
const emailRegExp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ ;
2017-11-16 14:12:03 +01:00
2021-11-04 15:47:53 +01:00
const sanitizeUser = ( user , ctx ) => {
const { auth } = ctx . state ;
const userSchema = strapi . getModel ( 'plugin::users-permissions.user' ) ;
return sanitize . contentAPI . output ( user , userSchema , { auth } ) ;
} ;
2017-11-14 11:49:19 +01:00
module . exports = {
2019-07-15 23:16:50 +02:00
async callback ( ctx ) {
2017-11-14 11:49:19 +01:00
const provider = ctx . params . provider || 'local' ;
const params = ctx . request . body ;
2022-02-23 22:51:59 +08:00
const store = strapi . store ( { type : 'plugin' , name : 'users-permissions' } ) ;
2022-05-31 14:06:58 +02:00
const grantSettings = await store . get ( { key : 'grant' } ) ;
2018-02-06 13:10:43 +01:00
2022-05-31 14:06:58 +02:00
const grantProvider = provider === 'local' ? 'email' : provider ;
2017-11-14 11:49:19 +01:00
2022-05-31 14:06:58 +02:00
if ( ! _ . get ( grantSettings , [ grantProvider , 'enabled' ] ) ) {
throw new ApplicationError ( 'This provider is disabled' ) ;
}
2017-11-14 11:49:19 +01:00
2022-05-31 14:06:58 +02:00
if ( provider === 'local' ) {
await validateCallbackBody ( params ) ;
2017-11-14 11:49:19 +01:00
2022-05-31 14:06:58 +02:00
const { identifier } = params ;
2017-11-14 11:49:19 +01:00
2019-08-06 00:51:27 +02:00
// Check if the user exists.
2022-05-31 14:06:58 +02:00
const user = await strapi . query ( 'plugin::users-permissions.user' ) . findOne ( {
where : {
provider ,
$or : [ { email : identifier . toLowerCase ( ) } , { username : identifier } ] ,
} ,
} ) ;
2017-11-20 16:28:50 +01:00
if ( ! user ) {
2021-10-20 17:30:05 +02:00
throw new ValidationError ( 'Invalid identifier or password' ) ;
2018-10-23 18:44:49 +02:00
}
2017-11-20 16:28:50 +01:00
2022-05-31 14:06:58 +02:00
const advancedSettings = await store . get ( { key : 'advanced' } ) ;
const requiresConfirmation = _ . get ( advancedSettings , 'email_confirmation' ) ;
if ( requiresConfirmation && user . confirmed !== true ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError ( 'Your account email is not confirmed' ) ;
2018-09-05 11:14:03 +02:00
}
2019-04-09 12:09:03 +02:00
if ( user . blocked === true ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError ( 'Your account has been blocked by an administrator' ) ;
2018-01-24 11:38:42 +01:00
}
2018-03-14 16:56:12 +01:00
// The user never authenticated with the `local` provider.
2017-11-20 16:28:50 +01:00
if ( ! user . password ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError (
'This user never set a local password, please login with the provider used during account creation'
2019-04-09 12:09:03 +02:00
) ;
2017-11-20 16:28:50 +01:00
}
2021-08-19 22:27:00 +02:00
const validPassword = await getService ( 'user' ) . validatePassword (
params . password ,
user . password
) ;
2017-11-20 16:28:50 +01:00
if ( ! validPassword ) {
2021-10-20 17:30:05 +02:00
throw new ValidationError ( 'Invalid identifier or password' ) ;
2018-01-29 13:12:49 +01:00
}
2022-05-31 14:06:58 +02:00
return ctx . send ( {
jwt : getService ( 'jwt' ) . issue ( { id : user . id } ) ,
user : await sanitizeUser ( user , ctx ) ,
} ) ;
}
// Connect the user with the third-party provider.
try {
const user = await getService ( 'providers' ) . connect ( provider , ctx . query ) ;
return ctx . send ( {
jwt : getService ( 'jwt' ) . issue ( { id : user . id } ) ,
user : await sanitizeUser ( user , ctx ) ,
} ) ;
} catch ( error ) {
throw new ApplicationError ( error . message ) ;
2017-11-14 11:49:19 +01:00
}
2017-11-16 14:12:03 +01:00
} ,
2020-03-30 15:13:27 -04:00
async resetPassword ( ctx ) {
2017-12-07 15:21:54 +01:00
const params = _ . assign ( { } , ctx . request . body , ctx . params ) ;
2017-11-16 14:12:03 +01:00
2019-04-09 12:09:03 +02:00
if (
params . password &&
params . passwordConfirmation &&
params . password === params . passwordConfirmation &&
params . code
) {
2019-07-15 23:16:50 +02:00
const user = await strapi
2021-08-06 18:09:49 +02:00
. query ( 'plugin::users-permissions.user' )
2021-07-08 18:15:32 +02:00
. findOne ( { where : { resetPasswordToken : ` ${ params . code } ` } } ) ;
2017-11-16 14:12:03 +01:00
2017-12-07 15:21:54 +01:00
if ( ! user ) {
2021-10-20 17:30:05 +02:00
throw new ValidationError ( 'Incorrect code provided' ) ;
2017-12-07 15:21:54 +01:00
}
2017-11-16 14:12:03 +01:00
2022-03-11 11:08:17 +01:00
await getService ( 'user' ) . edit ( user . id , {
resetPasswordToken : null ,
password : params . password ,
} ) ;
2017-12-07 15:21:54 +01:00
// Update the user.
2017-12-06 11:47:39 +01:00
ctx . send ( {
2021-08-19 22:27:00 +02:00
jwt : getService ( 'jwt' ) . issue ( { id : user . id } ) ,
2021-11-04 15:47:53 +01:00
user : await sanitizeUser ( user , ctx ) ,
2017-12-06 11:47:39 +01:00
} ) ;
2019-04-09 12:09:03 +02:00
} else if (
params . password &&
params . passwordConfirmation &&
params . password !== params . passwordConfirmation
) {
2021-10-20 17:30:05 +02:00
throw new ValidationError ( 'Passwords do not match' ) ;
2017-12-07 15:21:54 +01:00
} else {
2021-10-20 17:30:05 +02:00
throw new ValidationError ( 'Incorrect params provided' ) ;
2017-12-06 11:47:39 +01:00
}
2017-11-16 18:00:15 +01:00
} ,
2019-07-15 23:16:50 +02:00
async connect ( ctx , next ) {
2021-09-03 11:11:37 +02:00
const grant = require ( 'grant-koa' ) ;
2021-11-29 16:05:45 +01:00
const providers = await strapi
2021-09-13 12:03:12 +02:00
. store ( { type : 'plugin' , name : 'users-permissions' , key : 'grant' } )
2019-04-09 12:09:03 +02:00
. get ( ) ;
2021-11-29 16:05:45 +01:00
const apiPrefix = strapi . config . get ( 'api.rest.prefix' ) ;
const grantConfig = {
defaults : {
prefix : ` ${ apiPrefix } /connect ` ,
} ,
... providers ,
} ;
2020-01-15 11:18:12 +01:00
const [ requestPath ] = ctx . request . url . split ( '?' ) ;
2021-09-29 12:04:42 +02:00
const provider = requestPath . split ( '/connect/' ) [ 1 ] . split ( '/' ) [ 0 ] ;
2018-01-25 09:59:24 +01:00
2020-05-08 13:50:00 +02:00
if ( ! _ . get ( grantConfig [ provider ] , 'enabled' ) ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError ( 'This provider is disabled' ) ;
2018-01-25 09:59:24 +01:00
}
2020-07-22 15:30:17 +02:00
if ( ! strapi . config . server . url . startsWith ( 'http' ) ) {
strapi . log . warn (
2021-11-29 07:01:25 -08:00
'You are using a third party provider for login. Make sure to set an absolute url in config/server.js. More info here: https://docs.strapi.io/developer-docs/latest/plugins/users-permissions.html#setting-up-the-server-url'
2020-07-22 15:30:17 +02:00
) ;
}
2019-08-26 10:41:50 +04:30
// Ability to pass OAuth callback dynamically
2021-12-21 12:12:55 +01:00
grantConfig [ provider ] . callback =
_ . get ( ctx , 'query.callback' ) ||
_ . get ( ctx , 'session.grant.dynamic.callback' ) ||
grantConfig [ provider ] . callback ;
2021-08-19 22:27:00 +02:00
grantConfig [ provider ] . redirect _uri = getService ( 'providers' ) . buildRedirectUri ( provider ) ;
2020-05-08 13:50:00 +02:00
2019-06-27 18:24:04 +02:00
return grant ( grantConfig ) ( ctx , next ) ;
2018-01-25 09:59:24 +01:00
} ,
2019-07-15 23:16:50 +02:00
async forgotPassword ( ctx ) {
2022-05-31 19:38:08 +02:00
const { email } = await validateForgotPasswordBody ( ctx . request . body ) ;
2019-10-09 17:37:16 +02:00
2021-09-13 12:03:12 +02:00
const pluginStore = await strapi . store ( { type : 'plugin' , name : 'users-permissions' } ) ;
2017-11-16 18:00:15 +01:00
2022-05-31 19:38:08 +02:00
const emailSettings = await pluginStore . get ( { key : 'email' } ) ;
const advancedSettings = await pluginStore . get ( { key : 'advanced' } ) ;
2020-01-10 07:40:17 -05:00
// Find the user by email.
2020-09-23 14:25:55 +02:00
const user = await strapi
2021-08-06 18:09:49 +02:00
. query ( 'plugin::users-permissions.user' )
2021-07-08 18:15:32 +02:00
. findOne ( { where : { email : email . toLowerCase ( ) } } ) ;
2017-11-16 18:00:15 +01:00
2022-05-31 19:38:08 +02:00
// NOTE: we do not want to leak the existing emails
if ( ! user || user . blocked ) {
return ctx . send ( { ok : true } ) ;
2021-08-23 15:11:15 -07:00
}
2017-11-16 18:00:15 +01:00
// Generate random token.
2022-05-31 19:38:08 +02:00
const userInfo = await sanitizeUser ( user , ctx ) ;
2017-11-16 18:00:15 +01:00
const resetPasswordToken = crypto . randomBytes ( 64 ) . toString ( 'hex' ) ;
2022-05-31 19:38:08 +02:00
const resetPasswordSettings = _ . get ( emailSettings , 'reset_password.options' , { } ) ;
const emailBody = await getService ( 'users-permissions' ) . template (
resetPasswordSettings . message ,
{
URL : advancedSettings . email _reset _password ,
SERVER _URL : getAbsoluteServerUrl ( strapi . config ) ,
ADMIN _URL : getAbsoluteAdminUrl ( strapi . config ) ,
USER : userInfo ,
TOKEN : resetPasswordToken ,
2020-03-06 19:16:23 +01:00
}
2022-05-31 19:38:08 +02:00
) ;
2020-06-15 10:34:59 +02:00
2022-05-31 19:38:08 +02:00
const emailObject = await getService ( 'users-permissions' ) . template (
resetPasswordSettings . object ,
{
USER : userInfo ,
}
) ;
const emailToSend = {
to : user . email ,
from :
resetPasswordSettings . from . email || resetPasswordSettings . from . name
? ` ${ resetPasswordSettings . from . name } < ${ resetPasswordSettings . from . email } > `
: undefined ,
replyTo : resetPasswordSettings . response _email ,
subject : emailObject ,
text : emailBody ,
html : emailBody ,
} ;
2017-12-04 14:11:00 +01:00
2022-05-31 19:38:08 +02:00
// NOTE: Update the user before sending the email so an Admin can generate the link if the email fails
await getService ( 'user' ) . edit ( user . id , { resetPasswordToken } ) ;
2019-03-01 16:28:44 +01:00
2017-12-04 14:00:09 +01:00
try {
2018-01-15 14:50:53 +01:00
// Send an email to the user.
2021-08-19 22:27:00 +02:00
await strapi
. plugin ( 'email' )
. service ( 'email' )
2022-05-31 19:38:08 +02:00
. send ( emailToSend ) ;
2017-12-04 14:00:09 +01:00
} catch ( err ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError ( err . message ) ;
2017-12-04 14:00:09 +01:00
}
2017-12-04 13:40:07 +01:00
ctx . send ( { ok : true } ) ;
2017-11-17 11:41:23 +01:00
} ,
2019-07-15 23:16:50 +02:00
async register ( ctx ) {
2021-09-13 12:03:12 +02:00
const pluginStore = await strapi . store ( { type : 'plugin' , name : 'users-permissions' } ) ;
2018-08-23 18:28:13 +02:00
2022-05-31 19:38:08 +02:00
const settings = await pluginStore . get ( { key : 'advanced' } ) ;
2018-03-12 16:06:54 +01:00
if ( ! settings . allow _register ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError ( 'Register action is currently disabled' ) ;
2018-01-15 15:19:59 +01:00
}
2020-05-05 14:41:30 +02:00
const params = {
2022-05-31 19:38:08 +02:00
... _ . omit ( ctx . request . body , [
'confirmed' ,
'blocked' ,
'confirmationToken' ,
'resetPasswordToken' ,
'provider' ,
] ) ,
2019-04-09 12:09:03 +02:00
provider : 'local' ,
2020-05-05 14:41:30 +02:00
} ;
2017-11-17 11:41:23 +01:00
2021-10-20 17:30:05 +02:00
await validateRegisterBody ( params ) ;
2019-07-15 23:16:50 +02:00
const role = await strapi
2021-08-06 18:09:49 +02:00
. query ( 'plugin::users-permissions.role' )
2021-07-08 18:15:32 +02:00
. findOne ( { where : { type : settings . default _role } } ) ;
2018-01-17 18:50:12 +01:00
if ( ! role ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError ( 'Impossible to find the default role' ) ;
2017-12-07 15:21:54 +01:00
}
2017-11-17 11:41:23 +01:00
2022-05-31 19:38:08 +02:00
const { email , username , provider } = params ;
const identifierFilter = {
$or : [
{ email : email . toLowerCase ( ) } ,
{ username : email . toLowerCase ( ) } ,
{ username } ,
{ email : username } ,
] ,
} ;
2017-12-07 15:21:54 +01:00
2022-05-31 19:38:08 +02:00
const conflictingUserCount = await strapi . query ( 'plugin::users-permissions.user' ) . count ( {
where : { ... identifierFilter , provider } ,
2019-07-15 23:16:50 +02:00
} ) ;
2018-01-15 17:58:11 +01:00
2022-05-31 19:38:08 +02:00
if ( conflictingUserCount > 0 ) {
throw new ApplicationError ( 'Email or Username are already taken' ) ;
2018-01-15 17:58:11 +01:00
}
2022-05-31 19:38:08 +02:00
if ( settings . unique _email ) {
const conflictingUserCount = await strapi . query ( 'plugin::users-permissions.user' ) . count ( {
where : { ... identifierFilter } ,
} ) ;
2018-01-15 17:58:11 +01:00
2022-05-31 19:38:08 +02:00
if ( conflictingUserCount > 0 ) {
throw new ApplicationError ( 'Email or Username are already taken' ) ;
2018-08-23 18:28:13 +02:00
}
2022-05-31 19:38:08 +02:00
}
2018-08-23 18:28:13 +02:00
2022-05-31 19:38:08 +02:00
let newUser = {
... params ,
role : role . id ,
email : email . toLowerCase ( ) ,
username ,
confirmed : ! settings . email _confirmation ,
} ;
try {
const user = await getService ( 'user' ) . add ( newUser ) ;
2017-11-17 11:41:23 +01:00
2021-11-04 15:47:53 +01:00
const sanitizedUser = await sanitizeUser ( user , ctx ) ;
2018-08-08 17:57:02 +02:00
if ( settings . email _confirmation ) {
try {
2021-11-04 15:47:53 +01:00
await getService ( 'user' ) . sendConfirmationEmail ( sanitizedUser ) ;
2018-08-08 17:57:02 +02:00
} catch ( err ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError ( err . message ) ;
2018-08-08 17:57:02 +02:00
}
2020-10-16 16:53:40 +02:00
return ctx . send ( { user : sanitizedUser } ) ;
2018-08-08 17:57:02 +02:00
}
2021-08-19 22:27:00 +02:00
const jwt = getService ( 'jwt' ) . issue ( _ . pick ( user , [ 'id' ] ) ) ;
2020-10-16 16:53:40 +02:00
return ctx . send ( {
jwt ,
user : sanitizedUser ,
2017-11-20 16:28:50 +01:00
} ) ;
2019-04-09 12:09:03 +02:00
} catch ( err ) {
2022-05-31 19:38:08 +02:00
if ( err instanceof ApplicationError ) {
2022-05-31 14:06:58 +02:00
throw err ;
2022-01-29 22:16:18 +01:00
} else {
strapi . log . error ( err ) ;
2022-05-31 19:38:08 +02:00
throw new Error ( 'An unknown error occurred during account creation' ) ;
2021-10-20 17:30:05 +02:00
}
2017-11-17 11:41:23 +01:00
}
2018-08-08 17:57:02 +02:00
} ,
2020-04-15 21:23:14 +02:00
async emailConfirmation ( ctx , next , returnUser ) {
2020-10-16 16:53:40 +02:00
const { confirmation : confirmationToken } = ctx . query ;
2018-08-08 17:57:02 +02:00
2021-08-19 22:27:00 +02:00
const userService = getService ( 'user' ) ;
const jwtService = getService ( 'jwt' ) ;
2018-08-08 17:57:02 +02:00
2020-10-16 16:53:40 +02:00
if ( _ . isEmpty ( confirmationToken ) ) {
2021-10-20 17:30:05 +02:00
throw new ValidationError ( 'token.invalid' ) ;
2020-10-16 16:53:40 +02:00
}
2022-05-14 09:00:44 +02:00
const [ user ] = await userService . fetchAll ( { filters : { confirmationToken } } ) ;
2020-10-16 16:53:40 +02:00
if ( ! user ) {
2021-10-20 17:30:05 +02:00
throw new ValidationError ( 'token.invalid' ) ;
2020-10-16 16:53:40 +02:00
}
2022-01-05 23:54:58 +09:00
await userService . edit ( user . id , { confirmed : true , confirmationToken : null } ) ;
2018-08-08 17:57:02 +02:00
2020-05-05 14:41:30 +02:00
if ( returnUser ) {
2020-03-23 17:57:54 +01:00
ctx . send ( {
2020-10-16 16:53:40 +02:00
jwt : jwtService . issue ( { id : user . id } ) ,
2021-11-04 15:47:53 +01:00
user : await sanitizeUser ( user , ctx ) ,
2020-03-23 17:57:54 +01:00
} ) ;
} else {
const settings = await strapi
2021-09-13 12:03:12 +02:00
. store ( { type : 'plugin' , name : 'users-permissions' , key : 'advanced' } )
2020-03-23 17:57:54 +01:00
. get ( ) ;
2018-08-08 17:57:02 +02:00
2020-03-23 17:57:54 +01:00
ctx . redirect ( settings . email _confirmation _redirection || '/' ) ;
}
2019-04-09 12:09:03 +02:00
} ,
2019-11-13 18:45:23 +01:00
async sendEmailConfirmation ( ctx ) {
const params = _ . assign ( ctx . request . body ) ;
2021-10-20 17:30:05 +02:00
await validateSendEmailConfirmationBody ( params ) ;
2019-11-13 18:45:23 +01:00
const isEmail = emailRegExp . test ( params . email ) ;
if ( isEmail ) {
params . email = params . email . toLowerCase ( ) ;
} else {
2021-10-20 17:30:05 +02:00
throw new ValidationError ( 'wrong.email' ) ;
2019-11-13 18:45:23 +01:00
}
2021-08-06 18:09:49 +02:00
const user = await strapi . query ( 'plugin::users-permissions.user' ) . findOne ( {
2021-07-08 18:15:32 +02:00
where : { email : params . email } ,
2019-11-13 18:45:23 +01:00
} ) ;
2022-01-28 22:03:23 +01:00
if ( ! user ) {
2022-03-10 17:51:47 +01:00
return ctx . send ( {
email : params . email ,
2022-03-11 11:08:17 +01:00
sent : true ,
2022-03-10 17:51:47 +01:00
} ) ;
2022-01-28 22:03:23 +01:00
}
2019-11-13 18:45:23 +01:00
if ( user . confirmed ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError ( 'already.confirmed' ) ;
2019-11-13 18:45:23 +01:00
}
if ( user . blocked ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError ( 'blocked.user' ) ;
2019-11-13 18:45:23 +01:00
}
try {
2021-07-08 18:15:32 +02:00
await getService ( 'user' ) . sendConfirmationEmail ( user ) ;
2019-11-13 18:45:23 +01:00
ctx . send ( {
2020-10-16 16:53:40 +02:00
email : user . email ,
2019-11-26 16:03:06 +01:00
sent : true ,
2019-11-13 18:45:23 +01:00
} ) ;
} catch ( err ) {
2021-10-20 17:30:05 +02:00
throw new ApplicationError ( err . message ) ;
2019-11-13 18:45:23 +01:00
}
} ,
2017-11-14 11:49:19 +01:00
} ;