{"id":28531187,"url":"https://github.com/pinterest/snappass","last_synced_at":"2025-07-07T12:31:00.816Z","repository":{"id":11026817,"uuid":"13357878","full_name":"pinterest/snappass","owner":"pinterest","description":"Share passwords securely","archived":false,"fork":false,"pushed_at":"2025-03-28T01:21:00.000Z","size":1218,"stargazers_count":865,"open_issues_count":36,"forks_count":256,"subscribers_count":31,"default_branch":"master","last_synced_at":"2025-06-09T15:09:54.070Z","etag":null,"topics":["passwords","security"],"latest_commit_sha":null,"homepage":"","language":"Python","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/pinterest.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.rst","contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.rst","dei":null}},"created_at":"2013-10-06T05:56:29.000Z","updated_at":"2025-06-07T20:34:00.000Z","dependencies_parsed_at":"2023-02-16T03:01:11.880Z","dependency_job_id":"56df1264-1be2-478f-86d1-5ab042f30ba4","html_url":"https://github.com/pinterest/snappass","commit_stats":{"total_commits":187,"total_committers":36,"mean_commits":5.194444444444445,"dds":0.7433155080213903,"last_synced_commit":"a2a887bb2c5154aebb4b01f4fcb866f4ab693709"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/pinterest/snappass","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinterest%2Fsnappass","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinterest%2Fsnappass/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinterest%2Fsnappass/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinterest%2Fsnappass/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pinterest","download_url":"https://codeload.github.com/pinterest/snappass/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pinterest%2Fsnappass/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264077207,"owners_count":23553807,"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":["passwords","security"],"created_at":"2025-06-09T15:09:50.354Z","updated_at":"2025-07-07T12:31:00.809Z","avatar_url":"https://github.com/pinterest.png","language":"Python","readme":"========\nSnapPass\n========\n\n|pypi|\n\n.. |pypi| image:: https://img.shields.io/pypi/v/snappass.svg\n    :target: https://pypi.python.org/pypi/snappass\n    :alt: Latest version released on PyPI\n\nIt's like SnapChat... for passwords.\n\nThis is a web app that lets you share passwords securely.\n\nLet's say you have a password.  You want to give it to your coworker, Jane.\nYou could email it to her, but then it's in her email, which might be backed up,\nand probably is in some storage device controlled by the NSA.\n\nYou could send it to her over chat, but chances are Jane logs all her messages\nbecause she uses Google Hangouts Chat, and Google Hangouts Chat might log everything.\n\nYou could write it down, but you can't find a pen, and there's way too many\ncharacters because your security person, Paul, is paranoid.\n\nSo we built SnapPass.  It's not that complicated, it does one thing.  If\nJane gets a link to the password and never looks at it, the password goes away.\nIf the NSA gets a hold of the link, and they look at the password... well they\nhave the password.  Also, Jane can't get the password, but now Jane knows that\nnot only is someone looking in her email, they are clicking on links.\n\nAnyway, this took us very little time to write, but we figure we'd save you the\ntrouble of writing it yourself, because maybe you are busy and have other things\nto do.  Enjoy.\n\nSecurity\n--------\n\nPasswords are encrypted using `Fernet`_ symmetric encryption, from the `cryptography`_ library.\nA random unique key is generated for each password, and is never stored;\nit is rather sent as part of the password link.\nThis means that even if someone has access to the Redis store, the passwords are still safe.\n\n.. _Fernet: https://cryptography.io/en/latest/fernet/\n.. _cryptography: https://cryptography.io/en/latest/\n\nRequirements\n------------\n\n* `Redis`_\n* Python 3.8+\n\n.. _Redis: https://redis.io/\n\nInstallation\n------------\n\n::\n\n    $ pip install snappass\n    $ snappass\n    * Running on http://0.0.0.0:5000/\n    * Restarting with reloader\n\nConfiguration\n-------------\n\nStart by ensuring that Redis is up and running.\n\nThen, you can configure the following via environment variables.\n\n``SECRET_KEY``: unique key that's used to sign key. This should\nbe kept secret.  See the `Flask Documentation`__ for more information.\n\n.. __: http://flask.pocoo.org/docs/quickstart/#sessions\n\n``DEBUG``: to run Flask web server in debug mode.  See the `Flask Documentation`__ for more information.\n\n.. __: http://flask.pocoo.org/docs/quickstart/#debug-mode\n\n``STATIC_URL``: this should be the location of your static assets.  You might not\nneed to change this.\n\n``NO_SSL``: if you are not using SSL.\n\n``URL_PREFIX``: useful when running snappass behind a reverse proxy like `nginx`. Example: ``\"/some/path/\"``, Defaults to ``None``\n\n``REDIS_HOST``: this should be set by Redis, but you can override it if you want. Defaults to ``\"localhost\"``\n\n``REDIS_PORT``: is the port redis is serving on, defaults to 6379\n\n``SNAPPASS_REDIS_DB``: is the database that you want to use on this redis server. Defaults to db 0\n\n``REDIS_URL``: (optional) will be used instead of ``REDIS_HOST``, ``REDIS_PORT``, and ``SNAPPASS_REDIS_DB`` to configure the Redis client object. For example: redis://username:password@localhost:6379/0\n\n``REDIS_PREFIX``: (optional, defaults to ``\"snappass\"``) prefix used on redis keys to prevent collisions with other potential clients\n\n``HOST_OVERRIDE``: (optional) Used to override the base URL if the app is unaware. Useful when running behind reverse proxies like an identity-aware SSO. Example: ``sub.domain.com``\n\n``SNAPPASS_BIND_ADDRESS``: (optional) Used to override the default bind address of 0.0.0.0 for flask app Example: ``127.0.0.1``\n\n``SNAPPASS_PORT``: (optional) Used to override the default port of 5000 Example: ``6000``\n\nAPIs\n----\n\nSnapPass has 2 APIs :\n1. A simple API : That can be used to create passwords links, and then share them with users\n2. A more REST-y API : Which facilitate programmatic interactions with SnapPass, without having to parse HTML content when retrieving the password\n\nSimple API\n^^^^^^^^^^\n\nThe advantage of using the simple API is that you can create a password and retrieve the link without having to open the web interface. This is useful if you want to embed it in a script or use it in a CI/CD pipeline.\n\nTo create a password, send a POST request to ``/api/set_password`` like so:\n\n::\n\n    $ curl -X POST -H \"Content-Type: application/json\"  -d '{\"password\": \"foobar\"}' http://localhost:5000/api/set_password/\n\nThis will return a JSON response with the password link:\n\n::\n\n    {\n        \"link\": \"http://127.0.0.1:5000/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D\",\n        \"ttl\":1209600\n    }\n\nthe default TTL is 2 weeks (1209600 seconds), but you can override it by adding a expiration parameter:\n\n::\n\n    $ curl -X POST -H \"Content-Type: application/json\"  -d '{\"password\": \"foobar\", \"ttl\": 3600 }' http://localhost:5000/api/set_password/\n\n\nREST API\n^^^^^^^^\n\nThe advantage of using the REST API is that you can fully manage the lifecycle of the password stored in SnapPass without having to interact with any web user interface.\n\nThis is useful if you want to embed it in a script,  use it in a CI/CD pipeline or share it between multiple client applications.\n\nCreate a password\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nTo create a password, send a POST request to ``/api/v2/passwords`` like so:\n\n::\n\n    $ curl -X POST -H \"Content-Type: application/json\"  -d '{\"password\": \"foobar\"}' http://localhost:5000/api/v2/passwords\n\nThis will return a JSON response with a token and the password link:\n\n::\n\n    {\n        \"token\": \"snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY=\",\n        \"links\": [{\n            \"rel\": \"self\",\n            \"href\": \"http://127.0.0.1:5000/api/v2/passwords/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D\",\n        },{\n            \"rel\": \"web-view\",\n            \"href\": \"http://127.0.0.1:5000/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D\",\n        }],\n        \"ttl\":1209600\n    }\n\nThe default TTL is 2 weeks (1209600 seconds), but you can override it by adding a expiration parameter:\n\n::\n\n    $ curl -X POST -H \"Content-Type: application/json\"  -d '{\"password\": \"foobar\", \"ttl\": 3600 }' http://localhost:5000/api/v2/passwords\n\nIf the password is null or empty, and the TTL is larger than the max TTL of the application, the API will return an error like this:\n\n\nOtherwise, the API will return a 404 (Not Found) response like so:\n\n::\n\n    {\n        \"invalid-params\": [{\n            \"name\": \"password\",\n            \"reason\": \"The password is required and should not be null or empty.\"\n        }, {\n            \"name\": \"ttl\",\n            \"reason\": \"The specified TTL is longer than the maximum supported.\"\n        }],\n        \"title\": \"The password and/or the TTL are invalid.\",\n        \"type\": \"https://127.0.0.1:5000/set-password-validation-error\"\n    }\n\nCheck if a password exists\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nTo check if a password exists, send a HEAD request to ``/api/v2/passwords/\u003ctoken\u003e``, where ``\u003ctoken\u003e`` is the token of the API response when a password is created (url encoded), or simply use the `self` link:\n\n::\n\n    $ curl --head http://localhost:5000/api/v2/passwords/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D\n\nIf :\n- the passwork_key is valid \n- the password :\n  - exists,\n  - has not been read \n  - is not expired\n\nThen the API will return a 200 (OK) response like so:\n\n::\n\n    HTTP/1.1 200 OK\n    Server: Werkzeug/3.0.1 Python/3.12.2\n    Date: Fri, 29 Mar 2024 22:15:54 GMT\n    Content-Type: text/html; charset=utf-8\n    Content-Length: 0\n    Connection: close\n\nOtherwise, the API will return a 404 (Not Found) response like so:\n\n::\n\n    HTTP/1.1 404 NOT FOUND\n    Server: Werkzeug/3.0.1 Python/3.12.2\n    Date: Fri, 29 Mar 2024 22:19:29 GMT\n    Content-Type: text/html; charset=utf-8\n    Content-Length: 0\n    Connection: close\n    \n\nRead a password\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nTo read a password, send a GET request to ``/api/v2/passwords/\u003cpassword_key\u003e``, where ``\u003cpassword_key\u003e`` is the token of the API response when a password is created, or simply use the `self` link:\n\n::\n\n    $ curl -X GET http://localhost:5000/api/v2/passwords/snappassbedf19b161794fd288faec3eba15fa41~hHnILpQ50ZfJc3nurDfHCb_22rBr5gGEya68e_cZOrY%3D\n\nIf :\n- the token is valid \n- the password :\n  - exists\n  - has not been read \n  - is not expired\n\nThen the API will return a 200 (OK) with a JSON response containing the password :\n\n::\n\n    {\n        \"password\": \"foobar\"\n    }\n\nOtherwise, the API will return a 404 (Not Found) response like so:\n\n::\n\n    {\n        \"invalid-params\": [{\n            \"name\": \"token\"\n        }],\n        \"title\": \"The password doesn't exist.\",\n        \"type\": \"https://127.0.0.1:5000/get-password-error\"\n    }\n\nNotes on APIs\n^^^^^^^^^^^^^\n\nNotes:\n\n- When using the APIs, you can specify any ttl, as long as it is lower than the default.\n- The password is passed in the body of the request rather than in the URL. This is to prevent the password from being logged in the server logs.\n- Depending on the environment you are running it, you might want to expose the ``/api`` endpoint to your internal network only, and put the web interface behind authentication.\n\n\nDocker\n------\n\nAlternatively, you can use `Docker`_ and `Docker Compose`_ to install and run SnapPass:\n\n.. _Docker: https://www.docker.com/\n.. _Docker Compose: https://docs.docker.com/compose/\n\n::\n\n    $ docker-compose up -d\n\nThis will pull all dependencies, i.e. Redis and appropriate Python version (3.7), then start up SnapPass and Redis server. SnapPass server is accessible at: http://localhost:5000\n\nSimilar Tools\n-------------\n\n- `Snappass.NET \u003chttps://github.com/generateui/Snappass.NET\u003e`_ is a .NET\n  (ASP.NET Core) port of SnapPass.\n\n\nWe're Hiring!\n-------------\n\nAre you really excited about open-source and great software engineering?\n`Pinterest is hiring \u003chttps://careers.pinterest.com\u003e`_!\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpinterest%2Fsnappass","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpinterest%2Fsnappass","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpinterest%2Fsnappass/lists"}