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' ) ;
2023-08-25 13:44:44 +02:00
const { concat , compact , isArray } = require ( 'lodash/fp' ) ;
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 ,
2022-06-01 16:22:19 +02:00
validateResetPasswordBody ,
2022-06-01 17:59:30 +02:00
validateEmailConfirmationBody ,
2022-08-02 20:58:37 +02:00
validateChangePasswordBody ,
2021-10-20 17:30:05 +02:00
} = require ( './validation/auth' ) ;
2019-01-18 16:08:15 +01:00
2023-09-12 20:24:43 +05:30
const { ApplicationError , ValidationError , ForbiddenError } = utils . errors ;
2019-01-18 16:08:15 +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' ) ;
2024-03-21 20:07:54 +01:00
return strapi . contentAPI . sanitize . output ( user , userSchema , { auth } ) ;
2021-11-04 15:47:53 +01:00
} ;
2024-09-27 10:54:37 +03:00
module . exports = ( { strapi } ) => ( {
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-01-18 16:01:52 +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.
2024-03-13 15:40:30 +01:00
const user = await strapi . db . query ( 'plugin::users-permissions.user' ) . findOne ( {
2022-05-31 14:06:58 +02:00
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
if ( ! user . password ) {
2022-06-01 20:35:43 +02:00
throw new ValidationError ( 'Invalid identifier or password' ) ;
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-06-01 20:35:43 +02:00
const advancedSettings = await store . get ( { key : 'advanced' } ) ;
const requiresConfirmation = _ . get ( advancedSettings , 'email_confirmation' ) ;
if ( requiresConfirmation && user . confirmed !== true ) {
throw new ApplicationError ( 'Your account email is not confirmed' ) ;
2018-01-15 15:19:59 +01:00
}
2022-06-01 20:35:43 +02:00
if ( user . blocked === true ) {
throw new ApplicationError ( 'Your account has been blocked by an administrator' ) ;
2018-01-25 15:04:42 +01:00
}
2022-05-31 14:06:58 +02:00
return 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 ) ,
2018-01-12 15:20:13 +01:00
} ) ;
2017-11-14 11:49:19 +01:00
}
2022-08-11 10:04:46 +02:00
// Connect the user with the third-party provider.
try {
const user = await getService ( 'providers' ) . connect ( provider , ctx . query ) ;
2023-09-12 20:24:43 +05:30
if ( user . blocked ) {
throw new ForbiddenError ( 'Your account has been blocked by an administrator' ) ;
2023-09-06 11:05:39 +05:30
}
2022-08-11 10:04:46 +02:00
return ctx . send ( {
jwt : getService ( 'jwt' ) . issue ( { id : user . id } ) ,
user : await sanitizeUser ( user , ctx ) ,
} ) ;
} catch ( error ) {
throw new ApplicationError ( error . message ) ;
}
2017-11-16 14:12:03 +01:00
} ,
2022-03-20 19:50:08 +01:00
async changePassword ( ctx ) {
2022-08-02 20:58:37 +02:00
if ( ! ctx . state . user ) {
2022-03-20 19:50:08 +01:00
throw new ApplicationError ( 'You must be authenticated to reset your password' ) ;
}
2024-09-27 10:54:37 +03:00
const validations = strapi . config . get ( 'plugin::users-permissions.validationRules' ) ;
2024-09-27 11:07:29 +03:00
2024-10-02 12:03:28 +03:00
const { currentPassword , password } = await validateChangePasswordBody (
ctx . request . body ,
validations
2024-09-27 11:07:29 +03:00
) ;
2022-03-20 19:50:08 +01:00
2024-03-11 11:35:58 +01:00
const user = await strapi . db
. query ( 'plugin::users-permissions.user' )
. findOne ( { where : { id : ctx . state . user . id } } ) ;
2022-03-20 19:50:08 +01:00
2022-08-02 20:58:37 +02:00
const validPassword = await getService ( 'user' ) . validatePassword ( currentPassword , user . password ) ;
2022-03-20 19:50:08 +01:00
2022-08-02 20:58:37 +02:00
if ( ! validPassword ) {
2022-08-03 16:46:41 +02:00
throw new ValidationError ( 'The provided current password is invalid' ) ;
2022-08-02 20:58:37 +02:00
}
2022-03-20 19:50:08 +01:00
2022-08-02 20:58:37 +02:00
if ( currentPassword === password ) {
throw new ValidationError ( 'Your new password must be different than your current password' ) ;
2022-03-20 19:50:08 +01:00
}
2022-08-02 20:58:37 +02:00
await getService ( 'user' ) . edit ( user . id , { password } ) ;
ctx . send ( {
jwt : getService ( 'jwt' ) . issue ( { id : user . id } ) ,
user : await sanitizeUser ( user , ctx ) ,
} ) ;
2022-03-20 19:50:08 +01:00
} ,
2020-03-30 15:13:27 -04:00
async resetPassword ( ctx ) {
2024-09-27 10:54:37 +03:00
const validations = strapi . config . get ( 'plugin::users-permissions.validationRules' ) ;
2024-10-02 12:03:28 +03:00
const { password , passwordConfirmation , code } = await validateResetPasswordBody (
ctx . request . body ,
validations
2022-06-01 16:22:19 +02:00
) ;
2017-11-16 14:12:03 +01:00
2022-06-01 16:22:19 +02:00
if ( password !== passwordConfirmation ) {
2021-10-20 17:30:05 +02:00
throw new ValidationError ( 'Passwords do not match' ) ;
2017-12-06 11:47:39 +01:00
}
2017-11-16 14:12:03 +01:00
2024-03-13 15:40:30 +01:00
const user = await strapi . db
2022-06-01 16:22:19 +02:00
. query ( 'plugin::users-permissions.user' )
. findOne ( { where : { resetPasswordToken : code } } ) ;
2017-11-16 14:12:03 +01:00
2022-06-01 16:22:19 +02:00
if ( ! user ) {
throw new ValidationError ( 'Incorrect code provided' ) ;
2017-12-06 11:47:39 +01:00
}
2022-06-01 16:22:19 +02:00
await getService ( 'user' ) . edit ( user . id , {
resetPasswordToken : null ,
password ,
} ) ;
// Update the user.
ctx . send ( {
jwt : getService ( 'jwt' ) . issue ( { id : user . id } ) ,
user : await sanitizeUser ( user , ctx ) ,
} ) ;
2017-11-16 18:00:15 +01:00
} ,
2019-07-15 23:16:50 +02:00
async connect ( ctx , next ) {
2025-01-27 13:58:37 +02:00
const grant = require ( 'grant' ) . koa ( ) ;
2021-09-03 11:11:37 +02:00
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
2024-04-05 09:12:04 +02:00
const queryCustomCallback = _ . get ( ctx , 'query.callback' ) ;
const dynamicSessionCallback = _ . get ( ctx , 'session.grant.dynamic.callback' ) ;
const customCallback = queryCustomCallback ? ? dynamicSessionCallback ;
// The custom callback is validated to make sure it's not redirecting to an unwanted actor.
if ( customCallback !== undefined ) {
try {
// We're extracting the callback validator from the plugin config since it can be user-customized
const { validate : validateCallback } = strapi
. plugin ( 'users-permissions' )
. config ( 'callback' ) ;
await validateCallback ( customCallback , grantConfig [ provider ] ) ;
grantConfig [ provider ] . callback = customCallback ;
} catch ( e ) {
2024-04-17 16:24:18 +02:00
throw new ValidationError ( 'Invalid callback URL provided' , { callback : customCallback } ) ;
2024-04-05 09:12:04 +02:00
}
}
// Build a valid redirect URI for the current provider
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.
2024-03-13 15:40:30 +01:00
const user = await strapi . db
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
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 ,
2024-01-19 10:09:53 +01:00
SERVER _URL : strapi . config . get ( 'server.absoluteUrl' ) ,
ADMIN _URL : strapi . config . get ( 'admin.absoluteUrl' ) ,
2022-05-31 19:38:08 +02:00
USER : userInfo ,
TOKEN : resetPasswordToken ,
2020-03-06 19:16:23 +01:00
}
2022-05-31 19:38:08 +02:00
) ;
2019-04-09 12:09:03 +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 ,
} ;
2019-03-01 16:28:44 +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 } ) ;
2017-12-04 14:00:09 +01:00
2022-06-01 16:22:19 +02:00
// Send an email to the user.
2022-08-08 23:33:39 +02:00
await strapi . plugin ( 'email' ) . service ( 'email' ) . send ( emailToSend ) ;
2017-11-16 18:00:15 +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
}
2024-03-11 12:28:46 +01:00
const { register } = strapi . config . get ( 'plugin::users-permissions' ) ;
2023-08-25 13:44:44 +02:00
const alwaysAllowedKeys = [ 'username' , 'password' , 'email' ] ;
2024-01-16 18:14:32 +01:00
// Note that we intentionally do not filter allowedFields to allow a project to explicitly accept private or other Strapi field on registration
2023-08-25 13:44:44 +02:00
const allowedKeys = compact (
2024-01-16 18:14:32 +01:00
concat ( alwaysAllowedKeys , isArray ( register ? . allowedFields ) ? register . allowedFields : [ ] )
2023-08-25 13:44:44 +02:00
) ;
2024-01-16 18:14:32 +01:00
// Check if there are any keys in requestBody that are not in allowedKeys
const invalidKeys = Object . keys ( ctx . request . body ) . filter ( ( key ) => ! allowedKeys . includes ( key ) ) ;
if ( invalidKeys . length > 0 ) {
// If there are invalid keys, throw an error
throw new ValidationError ( ` Invalid parameters: ${ invalidKeys . join ( ', ' ) } ` ) ;
}
2020-05-05 14:41:30 +02:00
const params = {
2023-08-25 13:44:44 +02:00
... _ . pick ( ctx . request . body , allowedKeys ) ,
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
2024-09-27 10:54:37 +03:00
const validations = strapi . config . get ( 'plugin::users-permissions.validationRules' ) ;
2024-10-02 12:03:28 +03:00
await validateRegisterBody ( params , validations ) ;
2019-07-15 23:16:50 +02:00
2024-03-13 15:40:30 +01:00
const role = await strapi . db
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 ;
2018-01-24 11:52:09 +01:00
2022-05-31 19:38:08 +02:00
const identifierFilter = {
$or : [
{ email : email . toLowerCase ( ) } ,
{ username : email . toLowerCase ( ) } ,
{ username } ,
{ email : username } ,
] ,
} ;
2017-12-07 15:21:54 +01:00
2024-03-13 15:40:30 +01:00
const conflictingUserCount = await strapi . db . query ( 'plugin::users-permissions.user' ) . count ( {
2022-05-31 19:38:08 +02:00
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 ) {
2024-03-13 15:40:30 +01:00
const conflictingUserCount = await strapi . db . query ( 'plugin::users-permissions.user' ) . count ( {
2022-05-31 19:38:08 +02:00
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-08-08 15:50:34 +02:00
const newUser = {
2022-05-31 19:38:08 +02:00
... params ,
role : role . id ,
email : email . toLowerCase ( ) ,
username ,
confirmed : ! settings . email _confirmation ,
} ;
2017-11-17 11:41:23 +01:00
2022-06-01 16:22:19 +02:00
const user = await getService ( 'user' ) . add ( newUser ) ;
2018-08-08 17:57:02 +02:00
2022-06-01 16:22:19 +02:00
const sanitizedUser = await sanitizeUser ( user , ctx ) ;
2020-10-16 16:53:40 +02:00
2022-06-01 16:22:19 +02:00
if ( settings . email _confirmation ) {
try {
await getService ( 'user' ) . sendConfirmationEmail ( sanitizedUser ) ;
} catch ( err ) {
2024-09-16 10:26:16 +02:00
strapi . log . error ( err ) ;
throw new ApplicationError ( 'Error sending confirmation email' ) ;
2018-08-08 17:57:02 +02:00
}
2022-06-01 16:22:19 +02:00
return ctx . send ( { user : sanitizedUser } ) ;
2017-11-17 11:41:23 +01:00
}
2022-06-01 16:22:19 +02:00
const jwt = getService ( 'jwt' ) . issue ( _ . pick ( user , [ 'id' ] ) ) ;
return ctx . send ( {
jwt ,
user : sanitizedUser ,
} ) ;
2018-08-08 17:57:02 +02:00
} ,
2020-04-15 21:23:14 +02:00
async emailConfirmation ( ctx , next , returnUser ) {
2022-06-01 17:59:30 +02:00
const { confirmation : confirmationToken } = await validateEmailConfirmationBody ( 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
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 ) {
2022-06-01 17:59:30 +02:00
throw new ValidationError ( 'Invalid token' ) ;
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 ) {
2022-06-01 16:22:19 +02:00
const { email } = await validateSendEmailConfirmationBody ( ctx . request . body ) ;
2019-11-13 18:45:23 +01:00
2024-03-13 15:40:30 +01:00
const user = await strapi . db . query ( 'plugin::users-permissions.user' ) . findOne ( {
2022-06-01 16:22:19 +02:00
where : { email : email . toLowerCase ( ) } ,
2019-11-13 18:45:23 +01:00
} ) ;
2022-01-28 22:03:23 +01:00
if ( ! user ) {
2022-06-01 16:22:19 +02:00
return ctx . send ( { email , sent : true } ) ;
2022-01-28 22:03:23 +01:00
}
2019-11-13 18:45:23 +01:00
if ( user . confirmed ) {
2022-06-01 16:22:19 +02:00
throw new ApplicationError ( 'Already confirmed' ) ;
2019-11-13 18:45:23 +01:00
}
if ( user . blocked ) {
2022-06-01 16:22:19 +02:00
throw new ApplicationError ( 'User blocked' ) ;
2019-11-13 18:45:23 +01:00
}
2022-06-01 16:22:19 +02:00
await getService ( 'user' ) . sendConfirmationEmail ( user ) ;
ctx . send ( {
email : user . email ,
sent : true ,
} ) ;
2019-11-13 18:45:23 +01:00
} ,
2024-09-27 10:54:37 +03:00
} ) ;