Giter VIP home page Giter VIP logo

reset-password-bundle's Introduction

ResetPasswordBundle: Mind-Blowing (and Secure) Password Resetting for Symfony

Worrying about how to deal with users that can't remember their password? We've got you covered! This bundle provides a secure out of the box solution to allow users to reset their forgotten passwords.

Installation

The bundle can be installed using Composer or the Symfony binary:

composer require symfonycasts/reset-password-bundle

Usage

There are two ways to get started, the easiest and preferred way is to use Symfony's MakerBundle. The Maker will take care of everything from creating configuration, to generating your templates, controllers, and entities.

Using Symfony's Maker Bundle (Recommended)

  • Run bin/console make:reset-password, answer a couple questions, and enjoy our bundle!

Setting things up manually

If you prefer to take care of the leg work yourself, checkout the manual setup guide. We still recommend using the Maker command to get a feel for how we intended the bundle to be used.


If you used our Symfony Maker command bin/console make:reset-password after installation, your app is ready to go. Go to https://your-apps-domain/reset-password, fill out the form, click on the link sent to your email, and change your password. That's it! The ResetPasswordBundle takes care of the rest.

The above assumes you have already setup authentication with a registered user account & configured Symfony's mailer in your app.

Configuration

You can change the default configuration parameters for the bundle in the config/packages/reset_password.yaml config file created by Maker.

symfonycasts_reset_password:
    request_password_repository: App\Repository\ResetPasswordRequestRepository
    lifetime: 3600
    throttle_limit: 3600
    enable_garbage_collection: true

The production environment may require the default_uri to be defined in the config/packages/routing.yaml to prevent the URI in emails to point to localhost.

# config/packages/routing.yaml
when@prod:
    framework:
        router:
            # ...
            default_uri: '<your project's root URI>'

Parameters:

request_password_repository

Required

The complete namespace of the repository for the ResetPasswordRequest entity. If you used make:reset-password, this will be App\Repository\ResetPasswordRequestRepository.

lifetime

Optional - Defaults to 3600 seconds

This is the length of time a reset password request is valid for in seconds after it has been created.

throttle_limit

Optional - Defaults to 3600 seconds

This is the length of time in seconds that must pass before a user can request a subsequent reset request.

Setting this value equal to or higher than lifetime will prevent a user from requesting a password reset before a previous reset attempt has either 1) Been successfully completed. 2) The previous request has expired.

Setting this value lower than lifetime will allow a user to make several reset password requests, even if any previous requests have not been successfully completed or have not expired. This would allow for cases such as a user never received the reset password request email.

enable_garbage_collection

Optional - Defaults to true

Enable or disable the Reset Password Cleaner which handles expired reset password requests that may have been left in persistence.

Advanced Usage

Purging ResetPasswordRequest objects from persistence

The ResetPasswordRequestRepositoryInterface::removeRequests() method, which is implemented in the ResetPasswordRequestRepositoryTrait, can be used to remove all request objects from persistence for a single user. This differs from the garbage collection mechanism which only removes expired request objects for all users automatically.

Typically, you'd call this method when you need to remove request object(s) for a user who changed their email address due to suspicious activity and potentially has valid request objects in persistence with their "old" compromised email address.

// ProfileController

#[Route(path: '/profile/{id}', name: 'app_update_profile', methods: ['GET', 'POST'])]
public function profile(Request $request, User $user, ResetPasswordRequestRepositoryInterface $repository): Response
{
    $originalEmail = $user->getEmail();

    $form = $this->createFormBuilder($user)
        ->add('email', EmailType::class)
        ->add('save', SubmitType::class, ['label' => 'Save Profile'])
        ->getForm()
    ;
    
    $form->handleRequest($request);
    
    if ($form->isSubmitted() && $form->isValid()) {
        if ($originalEmail !== $user->getEmail()) {
            // The user changed their email address.
            // Remove any old reset requests for the user.
            $repository->removeRequests($user);
        }
        
        // Persist the user object and redirect...
    }
    
    return $this->render('profile.html.twig', ['form' => $form]);
}

Support

Feel free to open an issue for questions, problems, or suggestions with our bundle. Issues pertaining to Symfony's Maker Bundle, specifically make:reset-password, should be addressed in the Symfony Maker repository.

Security Issues

For security related vulnerabilities, we ask that you send an email to ryan [at] symfonycasts.com instead of creating an issue.

This will give us the opportunity to address the issue without exposing the vulnerability before a fix can be published.

reset-password-bundle's People

Contributors

1ed avatar akincer avatar bocharsky-bw avatar chindit avatar codedmonkey avatar crovitche-1623 avatar davidbilodeau1 avatar dennis-g avatar dennisdebest avatar dfridrich avatar dragosholban avatar erkhembayar-gantulga avatar glaubinix avatar idmarinas avatar jrushlow avatar kbond avatar ker0x avatar maxhelias avatar mollokhan avatar mssoylu avatar nabbisen avatar oskarstark avatar routmoute avatar thomas2411 avatar tobion avatar victormhg avatar weaverryan avatar zairigimad avatar zalesak avatar zmakrevski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reset-password-bundle's Issues

Can I show my controller improvement

Hello, I modified the controller. I deleted an unnecessary method and deleted a view. Can I offer my controller? I am new. I do not know how to do.

User is never notified if request has expired and auto garbage collection is enabled

Automatic garbage collection occurs before fetching the request in ResetPasswordHelper::validateTokenAndFetchUser(). Expired requests are removed before being fetched. They never get checked to see if they are expired so a ExpiredResetPasswordTokenException will never be thrown.

I thought about doing the garbage collection after (like in try...finally) but it still doesn't fully fix the issue as garbage collection happens on generating and fetching. On a busy site, expired tokens would be cleaned up quickly.

One solution is to make the garbage collection time configurable.

WDYT?

fix configuration descriptions

// src/DependencyInjection/Configuration.php 
29  ->info('How long a reset password token should be valid before expiring.')
33  ->info('Length of time between non-expired reset password requests.')

Both of these descriptions need a little bit of work - this one could be clearer and both should mention that they are seconds.

Text version link

Just installed this bundle for first time - thanks for saving me a job creating this logic btw - but just noticed that the link in the text version of the email is stripped out - not sure if this is an issue with this bundle or Symfony mailer as a text part doesn't seem to be supplied but thought I should highlight it either way

Hi!

To reset your password, please visit
here
This link will expire in 1 hour(s)..



Cheers!

readme related documention needed

  • Install
  • Usage
  • copyright notice
  • author / contributor notice

Random thoughts to touch on:
semver, bugfix / feature request / security related guidelines,

[CI] composer cache not using latest depencies

Without a lock file, composer cache in CI doesn't always use latest builds of dependencies.

  • temp. disable composer cache to use latest dev dependencies
  • solution to invalidate cache without a composer.lock file

require symfony mailer in composer

Bundle uses Symfony's Mailer component to send emails

  • require symfony/mailer in composer.json
  • reference in docs
  • if ! exists mailer_dsn in .env - maker should create it
  • add maker text reminder to set correct MAILER_DSN

copyright headers needed

cs-fixer is being implemented in PR #35. Project is under an MIT license but appropriate header for src/ files is still needed.

composer flex requirement

Bundle uses flex recipes to initiate it's config.

Either flex requirement, suggestion, or conflict should be added to composer if not covered by the framework-bundle conflict

refactor token generator

  • if (empty($verifier)) { should be null === $verifier
  • $verifier does not create new random string on subsequent getToken calls
  • rename getToken() to createToken() to better describe the methods intent

[dev] bump doctrine/doctrine-bundle minimum version

Only affects bundle development

A few of our tests utilize the deprecated Doctrine\Common\Persistence namespace. These direct deprecation's do not appear in PHP Unit because of the current dev version constraints. Specifically doctrine/doctrine-bundle.

Update doctrine/doctrine-bundle constraint to at least 2.0.3 to allow the use of Doctrine\Persistence namespace in the tests.

Note to self:
#75 depends on this
fix ns from #93 after merge

Idea to make a more generic bundle

Hey!

I really like this bundle but I also need secure token generation for a) confirming a registration and b) changing a email. Both require a similar structure as used in this bundle. Do you think it would be cool to have some "user-token" bundle as dependency on this bundle? So we can easily scaffold other systems that require confirmation tokens?

POST Request for password reset

Hello,

Is there any way to make a POST call with some parameters like email address for password reset request or you must use the generated web template?
For example to make a request call from mobile app, instead of opening browser.

Thanks for the replies in advance.

[BETA] composer installs non-essential files

when installing the bundle using composer require symfonycasts/reset-password-bundle, composer also installs dev related files/folders i.e. tests/.

This is not a big deal, but those files are not needed in a production env. Or a dev env outside of the actual development on the bundle. A .gitattributes file may be the solution to ignore said files. Thoughts?

make: reset-password ne ma pas crée ChangePasswordFormType.php

Bonjour,

J'ai le soucie comme dit dans le titre mais je ne trouve vraiment 0 réponse sur les docs ou bien même sur google .

Voila se que j'aurai du avoir :
...
php bin/console make:reset-password

created: src/Controller/ResetPasswordController.php
created: src/Entity/ResetPasswordRequest.php
created: src/Repository/ResetPasswordRequestRepository.php
updated: config/packages/reset_password.yaml
created: src/Form/ResetPasswordRequestFormType.php
created: src/Form/ChangePasswordFormType.php
created: templates/reset_password/check_email.html.twig
created: templates/reset_password/email.html.twig
created: templates/reset_password/request.html.twig
created: templates/reset_password/reset.html.twig
...

et Voila se que j'ai :
....
created: src/Controller/ResetPasswordController.php
created: src/Entity/ResetPasswordRequest.php
created: src/Repository/ResetPasswordRequestRepository.php
updated: config/packages/reset_password.yaml
created: src/Form/ResetPasswordRequestFormType.php
...
Voila voila .. merci d'avance pour vos future réponce !

Multiple entities

Hi,
Is it possible to use this bundle with multiple entities? A User entity and an Admin entity for example.
If not, it would be a good idea of feature :)
Thank you :)

flex recipe needed to create bundle config

Bundle relies on config/packages/reset_password.yaml file to get it's configuration param's. Primarily the request_password_repositoryparam which is required for the bundle to manage requests in persistence.

  • create a flex recipe to create reset_password.yaml
  • submit PR for flex recipe to Flex Repo

[BETA] config using wrong NS for fake repo

Attempted to load class "FakeInternalRepository" from namespace "SymfonyCasts\Bundle\ResetPassword\Persistence".  
  Did you forget a "use" statement for another namespace?

Multiple password reset tokens

One thing I noticed in the make-bundle implementation, is it wasn't possible to re-request a reset if there was a valid token available. This is important if sending the email fails. I believe this bundle allows for this with the request throttle time, but I just wanted to confirm.

Email showing incorrect hour(s)

There is a code in .html.twig to show the expire hour(s):

This link will expire in {{ tokenLifetime|date('g') }} hour(s)..

The tokenLifetime is 3600 and i expect it show as 1 hour(s) but it display as

This link will expire in 9 hour(s)..

in my email.

My requested_at and expires_at in database are

requested_at expires_at
2020-06-13 15:23:00 2020-06-13 16:23:00

refactor ResetPasswordHelper::class

  • @var int Another password reset cannot be made faster than this throttle time. use in configuration and add "seconds" reference
  • rearrange constructor args
  • improve generateResetToken() description..

Generates a new password reset token, persists it & returns the token that can be emailed to the user.

  • refactor generateResetToken() internal variable naming

from: $tokenData = $this->tokenGenerator->getToken
to: $tokenComponents = $this->tokenGenerator->getToken

  • findToken() return type does not reference possible null return value

invalid return type for controller trait method

ResetPasswordControllerTrait::getTokenFromSession() returns string, but SessionInterface::get() returns the value of the key in the session, or a configurable value if the key is not found.

As we are not modifying the default return value of SessionInterface::get(), the correct return type for getTokenFromSession() should be ?string.

return $this->getSessionService()->get('ResetPasswordPublicToken');

run CI periodically using cron jobs

Run CI daily

Too bad github actions doesn't have an "allow failure" option for a workflow. At least we are still able to see the deprecation warnings in the job output.

I think as long as we are testing dev-master, we would start to see tests fail once the deprecated code is removed. Using a cron build trigger could help if there is a large amount of time between contributions.

Originally posted by @kbond in #87 (comment)

[CI] implement static analysis

  • require psalm as a dev dependency
  • fix errors triggered by psalm
  • add psalm step to actions CI

do not add psalm step in CI until bulk of errors have been fixed.. ci test failures due to static analysis may hide problems with phpunit based tests..

garbage collection with console command throws error

console:

developer@2f4c8df8a609:/var/htdocs$ bin/console reset-password:remove-expired
Removing expired reset password requests...
Garbage collection successful. Removed 0 reset password request objects.

In Command.php line 258:
                                                                                                                                                      
  Return value of "SymfonyCasts\Bundle\ResetPassword\Command\ResetPasswordRemoveExpiredCommand::execute()" must be of the type int, "NULL" returned.

mariadb:

MariaDB [reset-password-app-test-2]> select * from reset_password_request;
+----+---------+----------------------+----------------------------------------------+---------------------+---------------------+
| id | user_id | selector             | hashed_token                                 | requested_at        | expires_at          |
+----+---------+----------------------+----------------------------------------------+---------------------+---------------------+
|  1 |       1 | k71rLHlzaCMLklIX26Tm | g65btmYbs3jrf2S7zbyvFds4CPTo8UAs+MGSy9P+xyk= | 2020-04-04 22:08:52 | 2020-04-04 23:08:52 |

config:

symfonycasts_reset_password:
    request_password_repository: App\Repository\ResetPasswordRequestRepository
    throttle_limit: 0

symfony/config throws deprecation in functional / integration tests in CI

Since symfony/config 5.1: The signature of method "Symfony\Component\Config\Definition\Builder\NodeDefinition::setDeprecated()" requires 3 arguments: "string $package, string $version, string $message", not defining them is deprecated.

Functional Test: 16x in ResetPasswordRequestRepositoryTest::setUp from SymfonyCasts\Bundle\ResetPassword\Tests\FunctionalTests\Persistence

Integration Test: 2x in ResetPasswordInterfaceAutowireTest::testResetPasswordInterfaceIsAutowiredByContainer from SymfonyCasts\Bundle\ResetPassword\Tests\IntegrationTests

functional tests needed

Functional test suite will be somewhat limited as the Unit & Integration tests will cover the bulk of the bundle. However, the following deserve special attention:

  • ResetPasswordRequestRepositoryTrait

Incorrect typehints?

Is there an issue with the typehints in this?

In the generated ResetPasswordController.php there's this:

try {
    $user = $this->resetPasswordHelper->validateTokenAndFetchUser($token);
} catch (ResetPasswordExceptionInterface $e) {
    $this->addFlash('reset_password_error', sprintf(
        'There was a problem validating your reset request - %s',
        $e->getReason()
    ));

    return $this->redirectToRoute('app_forgot_password_request');
}

then a little further down:

// Encode the plain password, and set it.
$encodedPassword = $passwordEncoder->encodePassword(
    $user,
    $form->get('plainPassword')->getData()
);

PHPStorm complains "Expected UserInterface, got object", and something like PHPStan says:

 ------ -----------------------------------------------------------------------------------------------------------------------------
  Line   Controller/ResetPasswordController.php
 ------ -----------------------------------------------------------------------------------------------------------------------------
  119    Parameter #1 $user of method Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface::encodePassword() expects
         Symfony\Component\Security\Core\User\UserInterface, object given.

Things still work fine but should these be corrected?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.