@@ -24,6 +24,7 @@ class AccountManager {
2424 * @param [options={ }] {Object}
2525 * @param [options.authMethod] {string} Primary authentication method (e.g. 'tls')
2626 * @param [options.emailService] {EmailService}
27+ * @param [options.tokenService] {TokenService}
2728 * @param [options.host] {SolidHost}
2829 * @param [options.multiUser=false] {boolean} (argv.idp) Is the server running
2930 * in multiUser mode (users can sign up for accounts) or single user
@@ -41,6 +42,7 @@ class AccountManager {
4142 }
4243 this . host = options . host
4344 this . emailService = options . emailService
45+ this . tokenService = options . tokenService
4446 this . authMethod = options . authMethod || defaults . auth
4547 this . multiUser = options . multiUser || false
4648 this . store = options . store
@@ -200,6 +202,22 @@ class AccountManager {
200202 return webIdUri . format ( )
201203 }
202204
205+ /**
206+ * Returns the root .acl URI for a given user account (the account recovery
207+ * email is stored there).
208+ *
209+ * @param userAccount {UserAccount}
210+ *
211+ * @throws {Error } via accountUriFor()
212+ *
213+ * @return {string } Root .acl URI
214+ */
215+ rootAclFor ( userAccount ) {
216+ let accountUri = this . accountUriFor ( userAccount . username )
217+
218+ return url . resolve ( accountUri , this . store . suffixAcl )
219+ }
220+
203221 /**
204222 * Adds a newly generated WebID-TLS certificate to the user's profile graph.
205223 *
@@ -352,6 +370,91 @@ class AccountManager {
352370 } )
353371 }
354372
373+ /**
374+ * Generates an expiring one-time-use token for password reset purposes
375+ * (the user's Web ID is saved in the token service).
376+ *
377+ * @param userAccount {UserAccount}
378+ *
379+ * @return {string } Generated token
380+ */
381+ generateResetToken ( userAccount ) {
382+ return this . tokenService . generate ( { webId : userAccount . webId } )
383+ }
384+
385+ /**
386+ * Returns a password reset URL (to be emailed to the user upon request)
387+ *
388+ * @param token {string} One-time-use expiring token, via the TokenService
389+ * @param returnToUrl {string}
390+ *
391+ * @return {string }
392+ */
393+ passwordResetUrl ( token , returnToUrl ) {
394+ let encodedReturnTo = encodeURIComponent ( returnToUrl )
395+
396+ let resetUrl = url . resolve ( this . host . serverUri ,
397+ `/api/password/validateReset?token=${ token } ` +
398+ `&returnToUrl=${ encodedReturnTo } ` )
399+
400+ return resetUrl
401+ }
402+
403+ /**
404+ * Parses and returns an account recovery email stored in a user's root .acl
405+ *
406+ * @param userAccount {UserAccount}
407+ *
408+ * @return {Promise<string|undefined> }
409+ */
410+ loadAccountRecoveryEmail ( userAccount ) {
411+ return Promise . resolve ( )
412+ . then ( ( ) => {
413+ let rootAclUri = this . rootAclFor ( userAccount )
414+
415+ return this . store . getGraph ( rootAclUri )
416+ } )
417+ . then ( rootAclGraph => {
418+ let matches = rootAclGraph . match ( null , ns . acl ( 'agent' ) )
419+
420+ let recoveryMailto = matches . find ( agent => {
421+ return agent . object . value . startsWith ( 'mailto:' )
422+ } )
423+
424+ if ( recoveryMailto ) {
425+ recoveryMailto = recoveryMailto . object . value . replace ( 'mailto:' , '' )
426+ }
427+
428+ return recoveryMailto
429+ } )
430+ }
431+
432+ sendPasswordResetEmail ( userAccount , returnToUrl ) {
433+ return Promise . resolve ( )
434+ . then ( ( ) => {
435+ if ( ! this . emailService ) {
436+ throw new Error ( 'Email service is not set up' )
437+ }
438+
439+ if ( ! userAccount . email ) {
440+ throw new Error ( 'Account recovery email has not been provided' )
441+ }
442+
443+ return this . generateResetToken ( userAccount )
444+ } )
445+ . then ( resetToken => {
446+ let resetUrl = this . passwordResetUrl ( resetToken , returnToUrl )
447+
448+ let emailData = {
449+ to : userAccount . email ,
450+ webId : userAccount . webId ,
451+ resetUrl
452+ }
453+
454+ return this . emailService . sendWithTemplate ( 'reset-password' , emailData )
455+ } )
456+ }
457+
355458 /**
356459 * Sends a Welcome email (on new user signup).
357460 *
0 commit comments