{"id":25040661,"url":"https://github.com/devise-passwordless/devise-passwordless","last_synced_at":"2025-04-04T09:07:08.136Z","repository":{"id":47468298,"uuid":"311433187","full_name":"devise-passwordless/devise-passwordless","owner":"devise-passwordless","description":"Devise passwordless logins using emailed magic links","archived":false,"fork":false,"pushed_at":"2025-02-19T16:19:56.000Z","size":204,"stargazers_count":218,"open_issues_count":11,"forks_count":41,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-28T08:05:24.140Z","etag":null,"topics":["devise","devise-gem-extension","magic-link","maintainer-wanted","passwordless","rails","rails-engine","ruby-on-rails"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devise-passwordless.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-09T18:44:00.000Z","updated_at":"2025-03-24T22:27:20.000Z","dependencies_parsed_at":"2024-05-30T15:22:10.029Z","dependency_job_id":"8f7baa4f-e6c7-4249-b48a-3b7cdef4876c","html_url":"https://github.com/devise-passwordless/devise-passwordless","commit_stats":{"total_commits":132,"total_committers":15,"mean_commits":8.8,"dds":"0.18181818181818177","last_synced_commit":"19b0c849e8a6a7bf5b26bdb4f9f4f5ff4dbb0a6c"},"previous_names":["devise-passwordless/devise-passwordless","abevoelker/devise-passwordless"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devise-passwordless%2Fdevise-passwordless","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devise-passwordless%2Fdevise-passwordless/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devise-passwordless%2Fdevise-passwordless/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devise-passwordless%2Fdevise-passwordless/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devise-passwordless","download_url":"https://codeload.github.com/devise-passwordless/devise-passwordless/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247149500,"owners_count":20891954,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["devise","devise-gem-extension","magic-link","maintainer-wanted","passwordless","rails","rails-engine","ruby-on-rails"],"created_at":"2025-02-06T03:03:54.789Z","updated_at":"2025-04-04T09:07:08.118Z","avatar_url":"https://github.com/devise-passwordless.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Devise::Passwordless\n\nA passwordless login strategy for [Devise] using emailed magic links\n\n## Features\n\n* No passwords - users receive magic link emails to register / sign-in\n* No database changes needed - magic links are stateless tokens\n* [Choose your token encoding algorithm or easily write your own](#tokenizers)\n* [Can be combined with traditional password authentication in the same model](#combining-password-and-passwordless-auth-in-the-same-model)\n* [Supports multiple user (resource) types](#multiple-user-resource-types)\n* All the goodness of Devise!\n\n## Call for maintainers 🗣️📣\n\nWe're currently looking for volunteers to help maintain this library! [See here for details][call-for-maintainers].\n\n[call-for-maintainers]: https://github.com/abevoelker/devise-passwordless/discussions/64\n\n## 0.x to 1.0 Upgrade\n\n⭐ The 1.0 release includes significant breaking changes! ⭐\n\nIf you're upgrading from 0.x to 1.0, read [the upgrade guide][] for\na list of changes you'll need to make.\n\n[the upgrade guide]: https://github.com/abevoelker/devise-passwordless/blob/master/UPGRADING.md\n\n## Installation\n\nFirst, install and set up [Devise][].\n\nThen add this gem to your application's Gemfile:\n\n```ruby\ngem \"devise-passwordless\"\n```\n\nAnd then execute:\n\n```\n$ bundle install\n```\n\nFinally, run the install generator:\n\n```\n$ rails g devise:passwordless:install\n```\n\nSee the [customization section](#customization) for details on what gets installed and how to configure and customize.\n\n## Usage\n\nThis gem adds a `:magic_link_authenticatable` strategy that can be used in your Devise models for passwordless authentication. This strategy plays well with most other Devise strategies (see [*compatibility with other Devise strategies*](#compatibility-with-other-devise-strategies)).\n\nFor example, if your Devise model is User, enable the strategy like this:\n\n```ruby\n# app/models/user.rb\nclass User \u003c ApplicationRecord\n  devise :magic_link_authenticatable #, :registerable, :rememberable, ...\nend\n```\n\nThen, change your route to process sessions using the passwordless sessions controller:\n\n```ruby\n# config/routes.rb\nRails.application.routes.draw do\n  devise_for :users,\n    controllers: { sessions: \"devise/passwordless/sessions\" }\nend\n```\n\nFinally, we need to update Devise's views to remove references to passwords. We will assume you're using the standard Devise views for all your registrations and logins; if you need to support multiple Devise models, some with passwordless login and some with password login, then jump down to the [multiple users section below](#multiple-user-resource-types).\n\nFirst, ensure you have Devise views generated for your project under `app/views/devise`. If not, you can generate them with:\n\n```\nrails generate devise:views\n```\n\nThen, delete these files and directories:\n\n```\nrm -rf app/views/devise/passwords\nrm -f app/views/devise/mailer/password_change.html.erb\nrm -f app/views/devise/mailer/reset_password_instructions.html.erb\n```\n\nThen, edit these files to remove password references:\n\n* app/views/devise/registrations/new.html.erb\n  * Delete fields `:password` and `:password_confirmation`\n* app/views/devise/registrations/edit.html.erb\n  * Delete fields `:password`, `:password_confirmation`, `:current_password`\n* app/views/devise/sessions/new.html.erb\n  * Delete field `:password`\n\nThat's it! 🎉 Now check out the customization section so that you\nmay change the default configuration to better match your needs.\n\n## Customization\n\nConfiguration options are stored in Devise's initializer at `config/initializers/devise.rb`:\n\n```ruby\n# ==\u003e Configuration for :magic_link_authenticatable\n\n# Need to use a custom Devise mailer in order to send magic links.\n# If you're already using a custom mailer just have it inherit from\n# Devise::Passwordless::Mailer instead of Devise::Mailer\nconfig.mailer = \"Devise::Passwordless::Mailer\"\n\n# Which algorithm to use for tokenizing magic links. See README for descriptions\nconfig.passwordless_tokenizer = \"SignedGlobalIDTokenizer\"\n\n# Time period after a magic login link is sent out that it will be valid for.\n# config.passwordless_login_within = 20.minutes\n\n# The secret key used to generate passwordless login tokens. The default value\n# is nil, which means defer to Devise's `secret_key` config value. Changing this\n# key will render invalid all existing passwordless login tokens. You can\n# generate your own secret value with e.g. `rake secret`\n# config.passwordless_secret_key = nil\n\n# When using the :trackable module and MessageEncryptorTokenizer, set to true to \n# consider magic link tokens generated before the user's current sign in time to \n# be expired. In other words, each time you sign in, all existing magic links \n# will be considered invalid.\n# config.passwordless_expire_old_tokens_on_sign_in = false\n```\n\nMost config options can be set on a per-model basis. For instance,\nyou can use different tokenizers across different models like so:\n\n```ruby\n# app/models/user.rb\nclass User \u003c ApplicationRecord\n  devise :magic_link_authenticatable\n\n  def self.passwordless_tokenizer\n    \"SignedGlobalIDTokenizer\"\n  end\nend\n\n# app/models/another_user.rb\nclass AnotherUser \u003c ApplicationRecord\n  devise :magic_link_authenticatable\n\n  def self.passwordless_tokenizer\n    \"MessageEncryptorTokenizer\"\n  end\n  def self.passwordless_login_within\n    1.hour\n  end\nend\n```\n\nTo customize the magic link email subject line and other status and error messages, modify these values in `config/locales/devise.en.yml`:\n\n```yaml\nen:\n  devise:\n    passwordless:\n      not_found_in_database: \"Could not find a user for that email address\"\n      magic_link_sent: \"A login link has been sent to your email address. Please follow the link to log in to your account.\"\n      magic_link_sent_paranoid: \"If your account exists, you will receive an email with a login link. Please follow the link to log in to your account.\"\n    failure:\n      magic_link_invalid: \"Invalid or expired login link.\"\n    mailer:\n      magic_link:\n        subject: \"Here's your magic login link ✨\"\n```\n\n**Note**: If [Devise's paranoid mode][] is enabled in your Devise initializer, the\n`:magic_link_sent_paranoid` message will be used both when a user account exists\nand when it does not exist to prevent account enumeration vulnerabilities. If\nparanoid mode is disabled, then `:magic_link_sent` will be used for existing\naccounts, and `:not_found_in_database` when no account was found.\n\n[Devise's paranoid mode]: https://github.com/heartcombo/devise/wiki/How-To:-Using-paranoid-mode,-avoid-user-enumeration-on-registerable\n\nTo customize the magic link email body, edit `app/views/devise/mailer/magic_link.html.erb`\n\n## Manually creating and sending magic links\n\nMagic links are created and sent normally using Devise's views for sign-in and registration, but you can create them manually as well.\n\nTo send a magic link email, do this:\n\n```ruby\nuser = User.last\nuser.send_magic_link\n# additional options are passed through to Devise's mailer logic\nuser.send_magic_link(remember_me: true, subject: \"Custom email subject\", \"X-Entity-Ref-ID\": SecureRandom.uuid)\n```\n\nIf you only need to generate the token portion of a magic link, you can do this:\n\n```ruby\n# see the tokenizer's #encode method for all supported keyword options\ntoken = user.encode_passwordless_token(expires_at: 2.hours.from_now)\n```\n\nOr, to generate the full magic link URL, use this URL view helper:\n\n```ruby\nuser_magic_link_url(\n  user: {\n    email: user.email,\n    token: token,\n    remember_me: true\n  }\n)\n```\n\n## Redirecting after magic link is sent\n\nAfter a user enters their email on the sign-in page, and a magic link is sent, the user\nwill be redirected back to the `:root` path of the application.\n\nTo provide a custom redirect location, you can write a custom\n`after_magic_link_sent_path_for` helper, similar to\n[how Devise's `after_sign_in_path_for` helper works][after_sign_in_path_for]:\n\n```ruby\nclass ApplicationController \u003c ActionController::Base\n  def after_magic_link_sent_path_for(resource_or_scope)\n    \"/foo\"\n  end\nend\n```\n\n[after_sign_in_path_for]: https://github.com/heartcombo/devise/wiki/How-To:-Redirect-back-to-current-page-after-sign-in,-sign-out,-sign-up,-update\n\nIf you need to have different paths for multiple different types of resources,\nyou can write something like this:\n\n```ruby\nclass ApplicationController \u003c ActionController::Base\n  def after_magic_link_sent_path_for(resource_or_scope)\n    case Devise::Mapping.find_scope!(resource_or_scope)\n    when :user\n      some_path\n    when :admin\n      some_other_path\n    end\n  end\nend\n```\n\nAnd, if you need more complex behavior, you can always write a custom sessions\ncontroller for each resource:\n\n```ruby\n# app/controllers/custom_sessions_controller.rb\nclass CustomSessionsController \u003c Devise::Passwordless::SessionsController\n  def create\n    # your custom logic\n  end\nend\n\n# config/routes.rb\nRails.application.routes.draw do\n  devise_for :users,\n    controllers: { sessions: \"custom_sessions\" }\nend\n```\n\n## Tokenizers\n\nTokenizers handle encoding and decoding of magic link tokens. There are multiple\npre-built ones to choose from, or [you can write your own](#your-own-custom-tokenizer).\n\nSet the default tokenizer in your Devise initializer (`config.passwordless_tokenizer`),\nwhich will be the global default. If you want a model to have a different tokenizer\nthan the default, you can define a class method `::passwordless_tokenizer` on your\nmodel and that will be used instead. Models can have different tokenizers from\neach other in this way.\n\n### SignedGlobalIDTokenizer\n\n```ruby\nconfig.passwordless_tokenizer = \"SignedGlobalIDTokenizer\"\n```\n\nTokens are [Rails signed Global IDs][globalid]. This is the default for new installs.\n\nReasons to use or not use:\n\n* The implementation is short and simple, so less likely to be buggy\n* Should work with all ORMs that implement GlobalID support\n* Cannot add arbitrary metadata to generated tokens\n* Tokens are signed, not encrypted, so some data will be visible when base64-decoded\n* Tokens tend to be a little longer (~30 chars IME) than MessageEncryptors'\n\n[globalid]: https://github.com/rails/globalid\n\n### MessageEncryptorTokenizer\n\n```ruby\nconfig.passwordless_tokenizer = \"MessageEncryptorTokenizer\"\n```\n\nTokens are encrypted using Rails's [MessageEncryptor][].\n\n[MessageEncryptor]: https://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html\n\nReasons to use or not use:\n\n* This was the only tokenizer in previous library versions\n* The implementation is longer and more involved than SignedGlobalID\n* Written with ActiveRecord in mind but may work with other ORMs\n* Can add arbitrary extra metadata to tokens\n* Tokens are opaque, due to being encrypted - no data visible when base64-decoded\n* Tokens tend to be a little shorter than SignedGlobalID IME\n\n### Your own custom tokenizer\n\nIt's straightforward to write your own tokenizer class; it just needs to respond to\n`::encode` and `::decode`:\n\n```ruby\nclass LuckyUserTokenizer\n  def self.encode(resource, *args)\n    \"8\" * 88 # our token is always lucky!\n  end\n\n  def self.decode(token, resource_class, *args)\n    # ignore token and retrieve a random user\n    [resource_class.order(\"RANDOM()\").limit(1).first, extra_data={}]\n  end\nend\n\n# config/initializers/devise.rb\nconfig.passwordless_tokenizer = \"::LuckyUserTokenizer\"\n```\n\n#### Single-use tokenizer\n\nWith Rails 7.1 and [generates_token_for](https://api.rubyonrails.org/classes/ActiveRecord/TokenFor/ClassMethods.html#method-i-generates_token_for) you can create a single-use tokenizer. For example:\n\n```ruby\nclass SingleUseTokenizer\n  def self.decode(token, resource_class, *args)\n    resource = resource_class.find_by_token_for(:passwordless_login, token)\n    raise Devise::Passwordless::ExpiredTokenError unless resource\n    raise Devise::Passwordless::InvalidTokenError unless resource.is_a?(resource_class)\n    [resource, {}]\n  end\n\n  def self.encode(resource, *args)\n    resource.generate_token_for(:passwordless_login)\n  end\nend\n```\n\nThen in your `User` model:\n\n```ruby\n  generates_token_for :passwordless_login, expires_in: passwordless_login_within do\n    current_sign_in_at\n  end\n```\n\nIt relies on the `current_sign_in_at` attribute changing on a user after a successful login.\nOnce it changes, the same token will always be invalid and cannot be reused.\n\nSince the same link cannot be visited twice, you may need to ignore `HEAD` requests\nto avoid some email clients (ex: Outlook) visiting links with a `HEAD` request before\nthe `GET` request with something like:\n\n```ruby\nmodule Users\n  class PasswordlessMagicLinksController \u003c Devise::MagicLinksController\n    def show\n      return render(plain: '') if request.method == 'HEAD'\n\n      super\n    end\n  end\nend\n```\n\n## Multiple user (resource) types\n\nDevise supports multiple resource types, so we do too.\n\nFor example, if you have a User and Admin model, enable the `:magic_link_authenticatable` strategy for each:\n\n```ruby\n# app/models/user.rb\nclass User \u003c ApplicationRecord\n  devise :magic_link_authenticatable # , :registerable, :rememberable, ...\nend\n\n# app/models/admin.rb\nclass Admin \u003c ApplicationRecord\n  devise :magic_link_authenticatable # , :registerable, :rememberable, ...\nend\n```\n\nThen just set up your routes like this:\n\n```ruby\n# config/routes.rb\nRails.application.routes.draw do\n  devise_for :users,\n    controllers: { sessions: \"devise/passwordless/sessions\" }\n  devise_for :admins,\n    controllers: { sessions: \"devise/passwordless/sessions\" }\nend\n```\n\nAnd that's it!\n\nMessaging can be customized per-resource using [Devise's usual I18n support][devise-i18n]:\n\n```yaml\nen:\n  devise:\n    passwordless:\n      user:\n        not_found_in_database: \"Could not find a USER for that email address\"\n        magic_link_sent: \"A USER login link has been sent to your email address. Please follow the link to log in to your account.\"\n        magic_link_sent_paranoid: \"If your USER account exists, you will receive an email with a login link. Please follow the link to log in to your account.\"\n      admin:\n        not_found_in_database: \"Could not find an ADMIN for that email address\"\n        magic_link_sent: \"An ADMIN login link has been sent to your email address. Please follow the link to log in to your account.\"\n        magic_link_sent_paranoid: \"If your ADMIN account exists, you will receive an email with a login link. Please follow the link to log in to your account.\"\n    failure:\n      user:\n        magic_link_invalid: \"Invalid or expired USER login link.\"\n      admin:\n        magic_link_invalid: \"Invalid or expired ADMIN login link.\"\n    mailer:\n      magic_link:\n        user_subject: \"Here's your USER magic login link ✨\"\n        admin_subject: \"Here's your ADMIN magic login link ✨\"\n```\n\n### Scoped views\n\nIf you have multiple Devise models, some that are passwordless and some that aren't, you will probably want to enable [Devise's `scoped_views` setting](https://henrytabima.github.io/rails-setup/docs/devise/configuring-views) so that the models have different signup and login pages (since some models will need password fields and others won't).\n\nIf you need to generate fresh Devise views for your models, you can do so like so:\n\n```\n$ rails generate devise:views users\n$ rails generate devise:views admins\n```\n\nWhich will generate the whole set of Devise views under these paths:\n\n```\napp/views/users/\napp/views/admins/\n```\n\n## Combining password and passwordless auth in the same model\n\nIt is possible to use both traditional password authentication (i.e. the\n`:database_authenticatable` strategy) alongside magic link authentication in\nthe same model:\n\n```ruby\n# app/models/user.rb\nclass User \u003c ApplicationRecord\n  devise :database_authenticatable, :magic_link_authenticatable, :registerable,\n         :recoverable, :rememberable, :validatable\nend\n```\n\nHow you end up implementing it will be highly dependent on your use case. By\ndefault, all password validations will still run - so on registration, users\nwill have to provide passwords - but they'll be able to log in via either\npassword OR magic link (you'll have to customize your routes and views to\nmake the separate paths accessible).\n\nHere's an example routes file of that scenario (a separate namespace is\nneeded because the password vs. passwordless paths use different sessions\ncontrollers):\n\n```ruby\ndevise_for :users\nnamespace \"passwordless\" do\n  devise_for :users,\n    controllers: { sessions: \"devise/passwordless/sessions\" }\nend\n```\n\nVisiting `/users/sign_in` will lead to a password sign in, while\n`/passwordless/users/sign_in` will lead to the magic link sign in flow\n(you'll need to [generate the necessary Devise views](#scoped-views)\nto support the different sign-in forms).\n\n### Disabling password authentication or magic link authentication\n\nRather than *all* your users having access to *both* authentication methods,\nit may be the case that you want *some* users to use magic links, *some*\nto use passwords, or some combination between the two.\n\nThis can be managed by defining some methods that disable the relevant\nauthentication strategy and determine the failure message. Here are\nexamples for both:\n\n### Disabling password authentication\n\nLet's say you want to disable password authentication for everyone except\npeople named Bob:\n\n```ruby\nclass User \u003c ApplicationRecord\n  # devise :database_authenticatable, :magic_link_authenticatable, ...\n\n  def first_name_bob?\n    self.first_name.downcase == \"bob\"\n  end\n\n  # The `super` is important in the following two methods as other\n  # auth strategies chain onto these methods:\n\n  def active_for_authentication?\n    super \u0026\u0026 first_name_bob?\n  end\n\n  def inactive_message\n    first_name_bob? ? super : :first_name_not_bob\n  end\nend\n```\n\nThen, you add this to your `devise.yml` to customize the error message:\n\n```yaml\ndevise:\n  failure:\n    first_name_not_bob: \"Sorry, only Bobs may log in using their password. Try magic link login instead.\"\n```\n\nNow, when users not named Bob try to log in with their password, it'll fail with\nyour custom failure message.\n\n### Disabling passwordless / magic link authentication\n\nDisabling magic link authentication is a similar process, just with different\nmethod names:\n\n```ruby\nclass User \u003c ApplicationRecord\n  # devise :database_authenticatable, :magic_link_authenticatable, ...\n\n  def first_name_alice?\n    self.first_name.downcase == \"alice\"\n  end\n\n  # The `super` is actually not important at the moment for these, but if\n  # any future Devise strategies were to extend this one, they will be.\n\n  def active_for_magic_link_authentication?\n    super \u0026\u0026 first_name_alice?\n  end\n\n  def magic_link_inactive_message\n    first_name_alice? ? super : :first_name_not_alice_magic_link\n  end\nend\n```\n\n```yaml\ndevise:\n  failure:\n    first_name_not_alice_magic_link: \"Sorry, only Alices may log in using magic links. Try password login instead.\"\n```\n\n## Compatibility with other Devise strategies\n\nIf using the `:rememberable` strategy for \"remember me\" functionality, you'll need to add a `remember_token` column to your resource, as by default that strategy assumes you're using a password auth strategy and relies on comparing the password's salt to validate cookies:\n\n```ruby\nchange_table :users do |t|\n  t.string :remember_token, limit: 20\nend\n```\n\nIf using the `:confirmable` strategy, you may want to override the default Devise behavior of requiring a fresh login after email confirmation (e.g. [this](https://stackoverflow.com/a/39010334/215168) or [this](https://stackoverflow.com/a/25865526/215168) approach). Otherwise, users will have to get a fresh login link after confirming their email, which makes little sense if they just confirmed they own the email address.\n\n## Hotwire/Turbo support\n\nIf you're using Hotwire/Turbo, be sure that you're on Devise \u003e= 4.9 and that you're\nsetting the `config.responder` config value in your Devise initializer to appropriate\nvalues.\n\nSee the [Devise 4.9 Turbo upgrade guide][] for more info.\n\n[Devise 4.9 Turbo upgrade guide]: https://github.com/heartcombo/devise/wiki/How-To:-Upgrade-to-Devise-4.9.0-%5BHotwire-Turbo-integration%5D\n\n## ActiveJob support\n\nIf you want to use ActiveJob to send magic link emails asynchronously through\na queuing backend, you can accomplish it the same way you\n[enable this functionality in any Devise install][devise-activejob]:\n\n```ruby\nclass User\n  def send_devise_notification(notification, *args)\n    devise_mailer.send(notification, self, *args).deliver_later\n  end\nend\n```\n\n[devise-activejob]: https://github.com/heartcombo/devise/blob/main/README.md#activejob-integration\n\n## Rails logs security\n\nRails's default configuration filters `:token` parameters out of request logs (and\n`Devise::Passwordless` will issue a warning if it detects the configuration doesn't). So request\nlogs shouldn't link magic link tokens.\n\nHowever, there are some other default Rails logging behaviors that may cause plaintext magic\nlink tokens to leak into log files:\n\n1. Action Mailer logs the entire contents of all outgoing emails to the DEBUG level. Magic link tokens delivered to users in email will be leaked.\n2. Active Job logs all arguments to every enqueued job at the INFO level. If you configure Devise to use `deliver_later` to send passwordless emails, magic link tokens will be leaked.\n\nRails sets the production logger level to INFO by default. Consider changing your production logger level to WARN if you wish to prevent tokens from being leaked into your logs. In `config/environments/production.rb`:\n\n```ruby\nconfig.log_level = :warn\n```\n\n(Partially adapted from the [Devise guide on password reset tokens][], which this section also applies to)\n\n[Devise guide on password reset tokens]: https://github.com/heartcombo/devise/blob/main/README.md#password-reset-tokens-and-rails-logs\n\n## Alternatives\n\nOther Ruby libraries that offer passwordless authentication:\n\n* [passwordless](https://github.com/mikker/passwordless)\n* [magic-link](https://github.com/dvanderbeek/magic-link)\n\n## Gem development\n\n### Running tests\n\nTo run the set of basic gem tests, do:\n\n```\n$ bundle\n$ bundle exec rake\n```\n\nThe more important and more thorough tests utilize a \"dummy\" Rails application.\n\nTo run this full suite of dummy app tests across all supported versions of Ruby and Rails,\nyou can use [nektos/act][] to run the same tests that run in our GitHub Workflow CI:\n\n```\n$ act -W .github/workflows/test.yml -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest --no-cache-server\n```\n\nTo run only against specific versions of Ruby or Rails, you can use the `--matrix` flag of `act`:\n\n```\n$ act -W .github/workflows/test.yml -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:act-latest --no-cache-server --matrix ruby-version:3.2 --matrix rails-version:7 --matrix rails-version:6.1\n```\n\nThe above example will only run the tests for Rails 7 and Rails 6.1 using Ruby 3.2.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n[Devise]: https://github.com/heartcombo/devise\n[devise-i18n]: https://github.com/heartcombo/devise#i18n\n[nektos/act]: https://github.com/nektos/act\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevise-passwordless%2Fdevise-passwordless","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevise-passwordless%2Fdevise-passwordless","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevise-passwordless%2Fdevise-passwordless/lists"}