{"id":13828760,"url":"https://github.com/github/darrrr","last_synced_at":"2025-10-04T07:31:07.923Z","repository":{"id":59152723,"uuid":"70278869","full_name":"github/darrrr","owner":"github","description":"An SDK for the delegated recovery specfication","archived":true,"fork":false,"pushed_at":"2020-03-04T21:14:42.000Z","size":990,"stargazers_count":46,"open_issues_count":4,"forks_count":9,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-09-30T16:49:45.736Z","etag":null,"topics":["authentication","cryptography","facebook","federation","rails","ruby","ruby-on-rails","sinatra"],"latest_commit_sha":null,"homepage":"https://www.facebook.com/notes/protect-the-graph/improving-account-security-with-delegated-recovery/1833022090271267/","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/github.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-07T20:02:25.000Z","updated_at":"2024-12-24T19:44:43.000Z","dependencies_parsed_at":"2022-09-13T10:50:30.288Z","dependency_job_id":null,"html_url":"https://github.com/github/darrrr","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/github/darrrr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/github%2Fdarrrr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/github%2Fdarrrr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/github%2Fdarrrr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/github%2Fdarrrr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/github","download_url":"https://codeload.github.com/github/darrrr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/github%2Fdarrrr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278283485,"owners_count":25961309,"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","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["authentication","cryptography","facebook","federation","rails","ruby","ruby-on-rails","sinatra"],"created_at":"2024-08-04T09:03:07.076Z","updated_at":"2025-10-04T07:31:07.565Z","avatar_url":"https://github.com/github.png","language":"Ruby","readme":"[![Code Climate](https://codeclimate.com/github/github/darrrr/badges/gpa.svg)](https://codeclimate.com/github/github/darrrr)\n![Build + Test](https://github.com/github/darrrr/workflows/Build%20+%20Test/badge.svg?branch=master)\n\nThe Delegated Account Recovery Rigid Reusable Ruby (aka D.a.r.r.r.r. or \"Darrrr\") library is meant to be used as the fully-complete plumbing in your Rack application when implementing the [Delegated Account Recovery specification](https://github.com/facebook/DelegatedRecoverySpecification). This library is currently used for the implementation at [GitHub](https://githubengineering.com/recover-accounts-elsewhere/).\n\nAlong with a fully featured library, a proof of concept application is provided in this repo.\n\n![](/logos/dar-logo-transparent-small.png)\n\n## Configuration\n\nAn account provider (e.g. GitHub) is someone who stores a token with someone else (a recovery provider e.g. Facebook) in order to grant access to an account.\n\nIn `config/initializers` or any location that is run during application setup, add a file. **NOTE:** `proc`s are valid values for `countersign_pubkeys_secp256r1` and `tokensign_pubkeys_secp256r1`\n\n```ruby\nDarrrr.authority = \"http://localhost:9292\"\nDarrrr.privacy_policy = \"#{Darrrr.authority}/articles/github-privacy-statement/\"\nDarrrr.icon_152px = \"#{Darrrr.authority}/icon.png\"\n\n# See script/setup for instructions on how to generate keys\nDarrrr::AccountProvider.configure do |config|\n  config.signing_private_key = ENV[\"ACCOUNT_PROVIDER_PRIVATE_KEY\"]\n  config.symmetric_key = ENV[\"TOKEN_DATA_AES_KEY\"]\n  config.tokensign_pubkeys_secp256r1 = [ENV[\"ACCOUNT_PROVIDER_PUBLIC_KEY\"]] || lambda { |provider, context| \"you wouldn't do this in real life but procs are supported for this value\" }\n  config.save_token_return = \"#{Darrrr.authority}/account-provider/save-token-return\"\n  config.recover_account_return = \"#{Darrrr.authority}/account-provider/recover-account-return\"\nend\n\nDarrrr::RecoveryProvider.configure do |config|\n  config.signing_private_key = ENV[\"RECOVERY_PROVIDER_PRIVATE_KEY\"]\n  config.countersign_pubkeys_secp256r1 = [ENV[\"RECOVERY_PROVIDER_PUBLIC_KEY\"]] || lambda { |provider, context| \"you wouldn't do this in real life but procs are supported for this value\" }\n  config.token_max_size = 8192\n  config.save_token = \"#{Darrrr.authority}/recovery-provider/save-token\"\n  config.recover_account = \"#{Darrrr.authority}/recovery-provider/recover-account\"\nend\n```\n\nThe delegated recovery spec depends on publicly available endpoints serving standard configs. These responses can be cached but are not by default. To configure your cache store, provide the reference:\n\n```ruby\nDarrrr.cache = Dalli::Client.new('localhost:11211', options)\n```\n\nThe spec disallows `http` URIs for basic security, but sometimes we don't have this setup locally.\n\n```ruby\nDarrrr.allow_unsafe_urls = true\n```\n\n## Provider registration\n\nIn order to allow a site to act as a provider, it must be \"registered\" on boot to prevent unauthorized providers from managing tokens.\n\n```ruby\n# Only configure this if you are acting as a recovery provider\nDarrrr.register_account_provider(\"https://github.com\")\n\n# Only configure this if you are acting as an account provider\nDarrrr.register_recovery_provider(\"https://www.facebook.com\")\n```\n\n## Custom crypto\n\nCreate a module that responds to `Module.sign`, `Module.verify`, `Module.decrypt`, and `Module.encrypt`. You can use the template below. I recommend leaving the `#verify` method as is unless you have a compelling reason to override it.\n\n### Global config\n\nSet `Darrrr.this_account_provider.custom_encryptor = MyCustomEncryptor`\nSet `Darrrr.this_recovery_provider.custom_encryptor = MyCustomEncryptor`\n\n### On-demand\n\n```ruby\nDarrrr.with_encryptor(MyCustomEncryptor) do\n  # perform DAR actions using MyCustomEncryptor as the crypto provider\n  recovery_token, sealed_token = Darrrr.this_account_provider.generate_recovery_token(data: \"foo\", audience: recovery_provider, context: { user: current_user })\nend\n```\n\n```ruby\nmodule MyCustomEncryptor\n  class \u003c\u003c self\n    # Encrypts the data in an opaque way\n    #\n    # data: the secret to be encrypted\n    #\n    # returns a byte array representation of the data\n    def encrypt(data)\n\n    end\n\n    # Decrypts the data\n    #\n    # ciphertext: the byte array to be decrypted\n    #\n    # returns a string\n    def decrypt(ciphertext)\n\n    end\n\n    # payload: binary serialized recovery token (to_binary_s).\n    #\n    # key: the private EC key used to sign the token\n    #\n    # returns signature in ASN.1 DER r + s sequence\n    def sign(payload, key)\n\n    end\n\n    # payload: token in binary form\n    # signature: signature of the binary token\n    # key: the EC public key used to verify the signature\n    #\n    # returns true if signature validates the payload\n    def verify(payload, signature, key)\n      # typically, the default verify function should be used to ensure compatibility\n      Darrrr::DefaultEncryptor.verify(payload, signature, key)\n    end\n  end\nend\n```\n\n## Example implementation\n\nI strongly suggest you read the specification, specifically section 3.1 (save-token) and 3.5 (recover account) as they contain the most dangerous operations.\n\n**NOTE:** this is NOT meant to be a complete implementation, it is just the starting point. Crucial aspects such as authentication, audit logging, out of band notifications, and account provider persistence are not implemented.\n\n* [Account Provider](controllers/account_provider_controller.rb) (save-token-return, recover-account-return)\n* [Recovery Provider](controllers/recovery_provider_controller.rb) (save-token, recover-account)\n* [Configuration endpoint](controllers/well_known_config_controller.rb) (`/.well-known/delegated-account-recovery/configuration`)\n\nSpecifically, the gem exposes the following APIs for manipulating tokens.\n* Account Provider\n  * [Generating](https://github.com/github/darrrr/blob/faafda5b1773e077c9c10b55b46216f97d13cd3b/lib/github/delegated_account_recovery/account_provider.rb#L49) a token\n  * Signing ([`#seal`](https://github.com/github/darrrr/blob/faafda5b1773e077c9c10b55b46216f97d13cd3b/lib/github/delegated_account_recovery/crypto_helper.rb#L13)) a token\n  * Verifying ([`#unseal`](https://github.com/github/darrrr/blob/faafda5b1773e077c9c10b55b46216f97d13cd3b/lib/github/delegated_account_recovery/crypto_helper.rb#L30)) a countersigned token\n* Recovery Provider\n  * Verifying ([`#unseal`](https://github.com/github/darrrr/blob/faafda5b1773e077c9c10b55b46216f97d13cd3b/lib/github/delegated_account_recovery/crypto_helper.rb#L30)) a token\n  * [Countersigning](https://github.com/github/darrrr/blob/faafda5b1773e077c9c10b55b46216f97d13cd3b/lib/github/delegated_account_recovery/recovery_provider.rb#L60) a token\n\n### Development\n\nLocal development assumes a Mac OS environment with [homebrew](https://brew.sh/) available. Postgres and phantom JS will be installed.\n\nRun `./script/bootstrap` then run `./script/server`\n\n* Visit `http://localhost:9292/account-provider`\n  * (Optionally) Record the random number for verification\n  * Click \"connect to http://localhost:9292\"\n* You'll see some debug information on the page.\n  * Click \"setup recovery\".\n* If recovery setup was successful, click \"Recovery Setup Successful\"\n* Click the \"recover now?\" link\n* You'll see an intermediate page, where more debug information is presented. Click \"recover token\"\n* You should be sent back to your host\n  * And see something like `Recovered data: \u003cthe secret from step 1\u003e`\n\n### Tests\n\nRun `./script/test` to run all tests.\n\n## Deploying to heroku\n\nUse `heroku config:set` to set the environment variables listed in [script/setup](/script/setup). Additionally, run:\n\n```\nheroku config:set HOST_URL=$(heroku info -s | grep web_url | cut -d= -f2)\n```\n\nPush your app to heroku:\n\n```\ngit push heroku \u003cbranch-name\u003e:master\n```\n\nMigrate the database:\n\n```\nheroku run rake db:migrate\n```\n\nUse the app!\n\n```\nheroku restart\nheroku open\n```\n\n## Roadmap\n\n* Add support for `token-status` endpoints as defined by the spec\n* Add async API as defined by the spec\n* Implement token binding as part of the async API\n\n## Don't want to run `./script` entries?\n\nSee `script/setup` for the environment variables that need to be set.\n\n## Contributions\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md)\n\n## License\n\n`darrrr` is licensed under the [MIT license](LICENSE.md).\n\nThe MIT license grant is not for GitHub's trademarks, which include the logo designs. GitHub reserves all trademark and copyright rights in and to all GitHub trademarks. GitHub's logos include, for instance, the stylized designs that include \"logo\" in the file title in the following folder:  [logos](/logos).\n\nGitHub® and its stylized versions and the Invertocat mark are GitHub's Trademarks or registered Trademarks. When using GitHub's logos, be sure to follow the GitHub [logo guidelines](https://github.com/logos).\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgithub%2Fdarrrr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgithub%2Fdarrrr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgithub%2Fdarrrr/lists"}