{"id":16927486,"url":"https://github.com/indirect/devise-unpwn","last_synced_at":"2025-04-11T17:42:59.268Z","repository":{"id":63112998,"uuid":"565315483","full_name":"indirect/devise-unpwn","owner":"indirect","description":"ensure user passwords haven't been pwned","archived":false,"fork":false,"pushed_at":"2022-11-13T11:50:16.000Z","size":97,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-23T17:18:24.998Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/indirect.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-11-13T01:52:58.000Z","updated_at":"2022-11-16T19:57:31.000Z","dependencies_parsed_at":"2023-01-23T00:00:47.463Z","dependency_job_id":null,"html_url":"https://github.com/indirect/devise-unpwn","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indirect%2Fdevise-unpwn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indirect%2Fdevise-unpwn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indirect%2Fdevise-unpwn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/indirect%2Fdevise-unpwn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/indirect","download_url":"https://codeload.github.com/indirect/devise-unpwn/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248451666,"owners_count":21105912,"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":[],"created_at":"2024-10-13T20:34:21.411Z","updated_at":"2025-04-11T17:42:59.250Z","avatar_url":"https://github.com/indirect.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Devise::Unpwn\n\nA Devise extension that validates user passwords against the [haveibeenpwned.com dataset of breached passwords](https://haveibeenpwned.com/Passwords).\n\nThe validation leverages the [unpwn](https://github.com/indirect/unpwn) and [pwned](https://github.com/philnash/pwned) gems, to first do an offline check against a bloom filter containing the top one million most common breached passwords, and then check the online API to verify that the password has never been seen in any known breach.\n\nThe check runs at 3 different times:\n1. When signing up for a new account, to block any publicly known passwords.\n2. When changing a password, to block any publicly known passwords.\n3. When signing in, to show a warning if the password has become compromised (optional).\n\n## Installation\n\n```bash\nbundle add devise-unpwn\n```\n\n## Usage\n\nAdd the `:unpwned` module to your existing Devise model.\n\n```ruby\nclass User \u003c ApplicationRecord\n  devise :database_authenticatable, :validatable, :unpwned\nend\n```\n\nUsers will receive the following error message if they use a password from the\nPwnedPasswords dataset:\n\n\u003e Password has previously appeared in a data breach and should never be used. Please choose something harder to guess.\n\n## Configuration\n\nYou can customize this error message by modifying the `devise` YAML file.\n\n```yml\n# config/locales/devise.en.yml\nen:\n  errors:\n    messages:\n      pwned_password: \"has previously appeared in a data breach and should never be used. If you've ever used it anywhere before, change it immediately!\"\n```\n\nYou can set options for the HTTP request that the `pwned` gem will make to the API in the Devise initializer at `config/initializers/devise.rb`, like this:\n\n```ruby\nconfig.unpwn_request_options = {\n  read_timeout: 1,\n  open_timeout: 2,\n  user_agent: \"my_cool_application\"\n}\n```\n\n### How to warn existing users when they sign in\n\nYou can optionally warn existing users when they sign in if they are using a password from the PwnedPasswords dataset.\n\nTo enable this, you _must_ override `after_sign_in_path_for`, like this:\n\n```ruby\n# app/controllers/application_controller.rb\n  def after_sign_in_path_for(resource)\n    set_flash_message! :alert, :warn_pwned if resource.respond_to?(:pwned?) \u0026\u0026 resource.pwned?\n    super\n  end\n```\n\nFor an [Active Admin](https://github.com/activeadmin/activeadmin) application the following monkey patch is needed:\n\n```ruby\n# config/initializers/active_admin_devise_sessions_controller.rb\nclass ActiveAdmin::Devise::SessionsController\n  def after_sign_in_path_for(resource)\n    set_flash_message! :alert, :warn_pwned if resource.respond_to?(:pwned?) \u0026\u0026 resource.pwned?\n    super\n  end\nend\n```\n\nTo prevent the default call to the HaveIBeenPwned API on user sign-in (only\nreally useful if you're going to check `pwned?` after sign-in as used above),\nadd the following to `config/initializers/devise.rb`:\n\n```ruby\nconfig.unpwn_check_on_sign_in = false\n```\n\n### Disabling in test environments\n\nTo disable API calls, set `Unpwn.offline = true` in your test helper. Passwords will still be checked against the top one million breached passwords, but no network calls will be made.\n\n## Considerations\n\nA few things to consider/understand when using this gem:\n\n* User passwords are hashed using SHA-1 and then truncated to 5 characters, implementing the k-Anonymity model described in https://haveibeenpwned.com/API/v2#SearchingPwnedPasswordsByRange Neither the clear-text password nor the full password hash is ever transmitted to a third party. More implementation details and important caveats can be found in https://blog.cloudflare.com/validating-leaked-passwords-with-k-anonymity/\n\n* This puts an external API in the request path of users signing up to your application. This could potentially add some latency to this operation. While we will always check the one million most common breached passwords, if the PwnedPasswords service is offline this gem will skip the API response and allow any password not in the top one million most common.\n\n## Attribution\n\nOriginally based on [devise-pwned_password](https://github.com/michaelbanfield/devise-pwned_password).\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/test` to run the tests. To release a new version, push a new version tag to GitHub by running `bin/rake bump [major|minor|patch]` and then `bin/rake tag`.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/indirect/devise-unpwn. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/indirect/devise-unpwn/blob/main/CODE_OF_CONDUCT.md).\n\n## Code of Conduct\n\nEveryone interacting in the Devise::Unpwn project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/indirect/devise-unpwn/blob/main/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Findirect%2Fdevise-unpwn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Findirect%2Fdevise-unpwn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Findirect%2Fdevise-unpwn/lists"}