{"id":32190245,"url":"https://github.com/zonotope/zanmi","last_synced_at":"2025-10-22T01:08:15.912Z","repository":{"id":62435253,"uuid":"61967383","full_name":"zonotope/zanmi","owner":"zonotope","description":"HTTP identity service","archived":false,"fork":false,"pushed_at":"2017-02-17T03:29:06.000Z","size":181,"stargazers_count":36,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-22T01:02:16.522Z","etag":null,"topics":["authentication","clojure","identity","jwt","jwt-authentication","password-database","service"],"latest_commit_sha":null,"homepage":"","language":"Clojure","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/zonotope.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-06-26T00:33:56.000Z","updated_at":"2024-05-31T07:56:38.000Z","dependencies_parsed_at":"2022-11-01T21:15:52.763Z","dependency_job_id":null,"html_url":"https://github.com/zonotope/zanmi","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/zonotope/zanmi","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zonotope%2Fzanmi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zonotope%2Fzanmi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zonotope%2Fzanmi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zonotope%2Fzanmi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zonotope","download_url":"https://codeload.github.com/zonotope/zanmi/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zonotope%2Fzanmi/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280360663,"owners_count":26317529,"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-21T02:00:06.614Z","response_time":58,"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","clojure","identity","jwt","jwt-authentication","password-database","service"],"created_at":"2025-10-22T01:01:46.435Z","updated_at":"2025-10-22T01:08:15.901Z","avatar_url":"https://github.com/zonotope.png","language":"Clojure","readme":"# zanmi [![Build Status](https://travis-ci.org/zonotope/zanmi.svg?branch=master)](https://travis-ci.org/zonotope/zanmi)\n\nAn HTTP identity service based on JWT auth tokens, and built\non [buddy](https://github.com/funcool/buddy). Authenticate users while managing\ntheir passwords and auth tokens independently of the apps or services they use.\n\nzanmi serves auth tokens in response to requests with the correct user\ncredentials. It manages a self contained password database with configurable\nback ends (current support for PostgreSQL and MongoDB) and hashes passwords\nwith [BCrypt + SHA512](https://en.wikipedia.org/wiki/Bcrypt) before they're\nstored. The signing algorithm zanmi signs it's auth tokens with is also\nconfigurable. [RSASSA-PSS](https://en.wikipedia.org/wiki/PKCS_1) is the default,\nbut\n[ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm) and\n[SHA512 HMAC](https://en.wikipedia.org/wiki/SHA-2) are also supported.\n\nOther back-end services can then authenticate users by verifying these auth\ntokens with the zanmi keypair's public key.\n\n## Project Maturity\nzanmi is still alpha software. There are probably bugs, and the api will most\nlikely change.\n\n## Usage\nzanmi is designed to be deployed with SSL/TLS in production. User passwords will\nbe sent in the clear otherwise.\n\nzanmi is a way to share authentication across many independent services and\nfront ends. it depends on a database back end and a key pair/secret to sign\ntokens. Both the database and the algorithm used to sign tokens is configurable.\nThe supported databases are PostgreSQL (default) and MongoDB, and the supported\ntoken signing algorithms are RSASSA-PSS (default), ECDSA, and SHA512 HMAC. Both\nRSA-PSS and ECDSA require paths to both a public and private key file, and\nSHA512 HMAC needs a secret supplied in the server config.\n\nTo try it out in development:\n\n* download the latest [release jar](https://github.com/zonotope/zanmi/releases/download/0.1.0-alpha0/zanmi-0.1.0-alpha0-standalone.jar)\n\n* generate an RSA keypair with [openssl](https://www.openssl.org/) by running\n  the following in a terminal, where `\u003ckeypair path\u003e` is some path of your\n  choosing:\n\n  ```sh\n  mkdir -p \u003ckeypair path\u003e\n  openssl genrsa -out \u003ckeypair path\u003e/priv.pem 2048\n  openssl rsa -pubout -in \u003ckeypair path\u003e/priv.pem -out \u003ckeypair path\u003e/pub.pem\n  ```\n\n* run either a PostgreSQL or MongoDB server including a database user that can\n  update and delete databases.\n\n* download and edit the\n  [example config](https://github.com/zonotope/zanmi/blob/master/config.edn.example)\n  by adding a random string as an api key and replacing the keypair paths and\n  database credentials with your own.\n\n* initialize the database by running:\n\n  ```sh\n  ZANMI_CONFIG=\u003cpath to edited config\u003e java -jar \u003czanmi release jar\u003e --init-db\n  ```\n\n* zanmi was designed to be run with ssl, but we'll turn it off for now. Start\n  the server by running:\n\n  ```sh\n  ZANMI_CONFIG=\u003cpath to edited config\u003e java -jar \u003czanmi release jar\u003e --skip-ssl\n  ```\n\nThe server will be listening at `localhost:8686` (unless you changed the port in\nthe config). zanmi speaks json by default, but can also use transit/json if you\nset the request's accept/content-type headers.\n\n### Clients\nThere is a [Clojure zanmi client](https://github.com/zonotope/zanmi-client), and\nsince zanmi is just a plain http server, clients for other languages should be\neasy to write as long as those languages have good http and jwt libraries.\n\nWe'll use [cURL](https://curl.haxx.se) to make requests to the running server to\nbe as general as possible. Enter the following commands into a new terminal\nwindow.\n\n#### Registering User Profiles\nSend a `post` request to the profiles url with your credentials to register a\nnew user:\n\n```bash\ncurl -XPOST --data \"profile[username]=gwcarver\u0026profile[password]=pulverized peanuts\" localhost:8686/profiles/\n```\n\nzanmi uses [zxcvbn-clj](https://github.com/zonotope/zxcvbn-clj) to validate\npassword strength, so simple passwords like \"p4ssw0rd\" will fail validations and\nan error response will be returned. The server will respond with an auth token\nif the password is strong enough and the username isn't already taken.\n\n#### Authenticating Users\nClients send user credentials with http basic auth to zanmi servers to\nauthenticate against existing user profiles. To verify that you have the right\npassword, send a `post` request to the user's profile auth url with the\ncredentials formatted `\"username:password\"` after the `-u` command switch to\ncURL:\n\n```bash\ncurl -XPOST -u \"gwcarver:pulverized peanuts\" localhost:8686/profiles/gwcarver/auth\n```\n\nThe server will respond with an auth token if the credentials are correct.\n\n#### Resetting Passwords\n\n##### With the Current Password\nTo reset the user's password, send a `put` request to the user's profile url\nwith the existing credentials through basic auth and the new password in the\nrequest body:\n\n```bash\ncurl -XPUT -u \"gwcarver:pulverized peanuts\" --data \"profile[password]=succulent sweet potatos\" localhost:8686/profiles/gwcarver\n```\n\nThe server will respond with a new auth token if your credentials are correct\nand the new password is strong enough according to zxcvbn\n\n##### With a Reset Token\nzanmi also supports resetting user passwords if they've forgotten them. First,\ncreate a JWT of the hash `{\"username\" : \u003cusername value\u003e }` sha512 signed with\nthe api-key from the zanmi config and send a `post` request with that JWT as the\n`ZanmiAppToken` in the authorization header to get a reset token\nfor that user:\n\n```bash\ncurl -XPOST -H \"Authorization: ZanmiAppToken \u003cjwt\u003e\" localhost:8686/profiles/gwcarver/reset\n```\n\nThen send the same `put` request as resetting with the current password above,\nbut change the authorization header value to `ZanmiResetToken \u003creset token\u003e`,\nwhere `\u003creset token\u003e`.\n\n```bash\ncurl -XPUT -H \"Authorization: ZanmiResetToken \u003creset token\u003e\" --data \"profile[password]=succulent sweet potatos\" localhost:8686/profiles/gwcarver\n```\n\nThe [clojure zanmi client](https://github.com/zonotope/zanmi-client) builds the\nauthorization JWT used to get the reset token automatically.\n\nIn production, your back-end application should request the reset token and send\nthat to the user's email, or some other trusted channel that will verify their\nidentity.\n\n#### Removing User Profiles\n\n##### With Valid Credentials\nTo remove a user's profile from the database, send a delete request to the\nusers's profile url with the right credentials:\n\n```bash\ncurl -XDELETE -u \"gwcarver:succulent sweet potatos\" localhost:8686/profiles/gwcarver\n```\n\n## Deployment\n\n### Configuration\nSee the\n[example config](https://github.com/zonotope/zanmi/blob/master/config.edn.example)\nfor configuration options. The easiest way to configure zanmi is to edit the\nexample config file linked above and set the `ZANMI_CONFIG` environment variable\nwhen running the relase jar. You can also set the configuration from the\nenvironment. See `environ` in `src/zanmi/config.clj` for environment variable\noverrides.\n\n### Database Initialization\nPostgreSQL and MongoDB are the only databases supported currently, but pull\nrequests are welcome.\n\nThere are no migrations. Use the `--init-db` command line switch to set up the\ndatabase and database tables.\n\n## FAQs\n* What about OAuth?\n  - While I do have plans to implement an OAuth2 provider based on zanmi\n    eventually, full OAuth2 support was a little overkill for my immediate use\n    case. I wrote zanmi because I primarily wanted to (1) share user\n    authentication among decoupled services and (2) isolate user password\n    storage from all the other application data.\n\n* How do users log out?\n  - zanmi only supports password database management and stateless\n    authentication, so there is no session management. Client applications are\n    free to manage their own separate sessions and use that in combination with\n    the `:iat` and `:updated` fields of the auth tokens to support logout.\n\n* What's with the name?\n  - \"zanmi\" means [friend](https://github.com/cemerick/friend) or\n    [buddy](https://github.com/funcool/buddy) in Haitian Creole.\n\n## TODO\n* Full OAuth2 implementation\n* Configurable password hashing schemes (support for pbkdf2, scrypt, etc)\n* Password database back ends for MySQL, Cassandra, etc.\n* More configurable password strength validations\n* Shared sessions (possibly with Redis)\n* Validate zanmi configuration map with clojure.spec\n* More tests!\n\n## Contributing\nPull requests welcome!\n\n### Developing\nFirst install [leiningen](http://leiningen.org/) and clone the repository.\n\n#### Setup\n\nWhen you first clone this repository, run:\n\n```sh\nlein setup\n```\n\nThis will create files for local configuration, and prep your system\nfor the project.\n\n#### Environment\n\nTo begin developing, start with a REPL.\n\n```sh\nlein repl\n```\n\nThen load the development environment.\n\n```clojure\nuser=\u003e (dev)\n:loaded\n```\n\nRun `go` to initiate and start the system.\n\n```clojure\ndev=\u003e (go)\n:started\n```\n\nBy default this creates a web server at \u003chttp://localhost:8686\u003e.\n\nWhen you make changes to your source files, use `reset` to reload any\nmodified files and reset the server.\n\n```clojure\ndev=\u003e (reset)\n:reloading (...)\n:resumed\n```\n\n#### Testing\n\nTesting is fastest through the REPL, as you avoid environment startup\ntime.\n\n```clojure\ndev=\u003e (test)\n...\n```\n\nBut you can also run tests through Leiningen.\n\n```sh\nlein test\n```\n\n## Legal\n\nCopyright © 2016 ben lamothe.\n\nDistributed under the MIT License\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzonotope%2Fzanmi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzonotope%2Fzanmi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzonotope%2Fzanmi/lists"}