Swiftpack.co - Package - nodes-vapor/reset

Reset 🏳

Swift Version Vapor Version Circle CI codebeat badge codecov Readme Score GitHub license

This package makes it easy to handle flows that involves resetting a password. It's up for the consumer to decide how to distribute the token that allows one to reset the password.

📦 Installation

Add Reset to the package dependencies (in your Package.swift file):

dependencies: [
    .package(url: "https://github.com/nodes-vapor/reset.git", from: "1.0.0")

as well as to your target (e.g. "App"):

targets: [
        name: "App",
        dependencies: [... "Reset" ...]

Next, copy/paste the Resources/Views/Reset folder into your project in order to be able to use the provided Leaf files. These files can be changed as explained in the Specifying the responses section, however it's recommended to copy this folder to your project anyway. This makes it easier for you to keep track of updates and your project will work if you decide later on to not use your own customized leaf files.

Getting started 🚀

First make sure that you've imported Reset everywhere it's needed:

import Reset

Adding the Provider

Reset comes with a light-weight provider that we'll need to register in the configure function in our configure.swift file:

try services.register(ResetProvider<User>(config: ResetConfig(
        name: AppConfig.app.name,
        baseURL: AppConfig.app.url,
        signer: ExpireableJWTSigner(
            expirationPeriod: 3600, // 1 hour
            signer: .hs256(
                key: env(EnvironmentKey.Reset.signerKey, "secret-reset"

Please see Making a PasswordResettable model for more information on confirming a type to PasswordResettable.

Adding the Reset routes

Make sure to add the relevant Reset routes, e.g. in your configure.swift or routes.swift:

services.register(Router.self) { container -> EngineRouter in
    let router = EngineRouter.default()
    try router.useResetRoutes(User.self, on: container)
    return router

Adding the Leaf tag

This package comes with a small Leaf tag that is used to pass Reset-related information such as project name and project url to Leaf. To add it to your project, please do the following:

public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
    services.register { _ -> LeafTagConfig in
        var tags = LeafTagConfig.default()
        return tags

Making a PasswordResettable model

There's a couple of things that needs to be in place for conforming your model to PasswordResettable. The following example is based on having a User model which you would like to add support for resetting a password.

Request and reset structs

The first thing to define is the data that is needed in order to request a reset password flow and the data for actually resetting the password. It could look like this:

extension User: PasswordResettable {
    // ...
    public struct RequestReset: RequestCreatable, Decodable, HasReadableUsername {
        static let readableUsernameKey = \RequestReset.username
        public let username: String

    public struct ResetPassword: RequestCreatable, Decodable, HasReadablePassword {
        static let readablePasswordKey = \ResetPassword.password
        public let password: String

    // ..

Basically the username (this could also be the email) is needed to request a reset flow and a new password is needed to submit the password change.

Note how that RequestReset conforms to HasReadableUsername. This enables Reset to implement the find method for looking up the user automatically.

Sending the reset-password url

Once the user has requested to reset their password, the sendPasswordReset function will be called. The implementation could send the url by email or just include the token in a text message. It's up to the implementer to decide how to distribute this.

Here's an example using the Mailgun package to send out an email with the reset password url:

extension User: PasswordResettable {
    // ...

    public func sendPasswordReset(
        url: String,
        token: String,
        expirationPeriod: TimeInterval,
        context: ResetPasswordContext,
        on req: Request
    ) throws -> Future<Void> {
        let mailgun = try req.make(Mailgun.self)
        let expire = Int(expirationPeriod / 60) // convert to minutes

        return try req
            .render(ViewPath.Reset.resetPasswordEmail, ["url": url, "expire": expire])
            .map(to: String.self) { view in
                String(bytes: view.data, encoding: .utf8) ?? ""
            .map(to: Mailgun.Message.self) { html in
                    from: "donotreply@reset.com",
                    to: self.email,
                    subject: "Reset password",
                    text: "Please turn on html to view this email.",
                    html: html
            .flatMap(to: Response.self) { message in
                try mailgun.send(message, on: req)
            .transform(to: ())

    // ..

Handling multiple reset flows

There might be cases where you would want to have multiple signers for multiple different reset password flows. One example could be to handle the regular reset password flow as well as automatically resetting a password when a user gets created. By implementing the signer function, you're able to handle this:

extension User: PasswordResettable {
    // ...

    public enum MyResetPasswordContext: HasRequestResetPasswordContext {
        case userRequestedToResetPassword
        case newUserWithoutPassword

        public static func requestResetPassword() -> MyResetPasswordContext {
            return .userRequestedToResetPassword

    public func signer(
        for context: MyResetPasswordContext,
        on container: Container
    ) throws -> ExpireableJWTSigner {
        let resetConfig: ResetConfig<User> = try container.make() // The default signer
        let myConfig: MyConfig = try container.make() // Some project specific config that holds the extra signer

        switch context {
        case .userRequestedToResetPassword: return resetConfig.signer
        case .newUserWithoutPassword: return myConfig.newUserSetPasswordSigner

    // ..

Please note that you need to implement your own Context if you want to handle multiple signers.

Specifying the responses

All endpoints and responses that Reset uses can be overwritten. Reset provides responses for the following cases:

  • Form for requesting a reset password flow
  • Response for letting the user know that the reset password url has been sent
  • Form for resetting the password
  • Response for letting the user know that the password has been reset

Here's a small example where the request to reset password should only be exposed through the API:

let customResponse = ResetResponses(
    resetPasswordRequestForm: { req in
        return try HTTPResponse(status: .notFound).encode(for: req)
    resetPasswordUserNotified: { req in
        return try HTTPResponse(status: .noContent).encode(for: req)
    resetPasswordForm: { req, user in
        return try req
            .encode(for: req)
    resetPasswordSuccess: { req, user in
        return try req
            .encode(for: req)

This instance can then be used when registering the provider as explained in Adding the Provider.

Alternatively, instead of passing in ResetResponses in the ResetConfig, one could pass in their own implementation of ResetControllerType for full customizability.

🏆 Credits

This package is developed and maintained by the Vapor team at Nodes.

📄 License

This package is open-sourced software licensed under the MIT license


Stars: 12

Used By

Total: 0


Version 1.0.0 RC 5 - 2019-05-07 11:16:35


  • Fixed Swift 5 compiler warnings related to redundant access modifiers
  • Use correct endpoint to link to reset mail form in when creating reset notice (e.g. in email)

Version 1.0.0 RC 4 - 2019-03-06 14:39:40


  • Depends on updated Sugar 4.0.0 RC and Submissions 2.0.0 RC
  • Provider now takes a config factory to allow for async middleware and signers

Version 1.0.0 RC3 - 2018-12-11 12:13:49


  • Correctly specified 4.1 as the minimum required Swift version.


  • This package no longer uses the MutableLeafTagConfig. Please use useResetLeafTags() for registering this package's Leaf tags (see readme for more info).

Version 1.0.0 RC2 - 2018-11-30 11:51:08


  • All contexts now uses the same signer, but expiration periods are defined in the implementing type of PasswordResettable.


  • Related to above: an issue where it wouldn't use the correct signer when using multiple contexts.

Version 1.0.0 RC1 - 2018-11-08 12:15:19


  • Bumped to Swift 4.2
  • Finalized the README
  • Aligned with community guidelines on how to register routes
  • Introduced a controller for handling the reset flow that can be subclassed or replaced

Version 1.0.0 Beta 3 - 2018-09-04 08:23:09


  • rename baseUrl to baseURL (breaking change)

Version 1.0.0 Beta 2 - 2018-08-23 18:11:49


  • Temporary fix for Leaf issue (https://github.com/vapor/template-kit/issues/17) removed as it's not needed anymore.

Version 1.0.0 Beta 1 - 2018-08-23 13:00:41


  • Initial version, ready for Vapor 3.