{"id":13509977,"url":"https://github.com/namsral/multipass","last_synced_at":"2026-01-27T11:30:53.881Z","repository":{"id":57482818,"uuid":"64835811","full_name":"namsral/multipass","owner":"namsral","description":"Better authentication for HTTP","archived":false,"fork":false,"pushed_at":"2017-01-10T14:36:11.000Z","size":175,"stargazers_count":73,"open_issues_count":9,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-30T15:36:05.624Z","etag":null,"topics":["authentication","caddy","http","reverse-proxy","two-factor"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/namsral.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-08-03T10:05:00.000Z","updated_at":"2023-09-16T18:28:40.000Z","dependencies_parsed_at":"2022-09-03T06:25:26.226Z","dependency_job_id":null,"html_url":"https://github.com/namsral/multipass","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/namsral/multipass","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namsral%2Fmultipass","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namsral%2Fmultipass/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namsral%2Fmultipass/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namsral%2Fmultipass/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/namsral","download_url":"https://codeload.github.com/namsral/multipass/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/namsral%2Fmultipass/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28812382,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-27T07:41:26.337Z","status":"ssl_error","status_checked_at":"2026-01-27T07:41:08.776Z","response_time":168,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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","caddy","http","reverse-proxy","two-factor"],"created_at":"2024-08-01T02:01:19.561Z","updated_at":"2026-01-27T11:30:53.864Z","avatar_url":"https://github.com/namsral.png","language":"Go","readme":"Multipass\n=========\n\n[![Build Status](https://travis-ci.org/namsral/multipass.svg?branch=master)](https://travis-ci.org/namsral/multipass)\n[![GoDoc](https://godoc.org/github.com/namsral/multipass?status.svg)](http://godoc.org/github.com/namsral/multipass)\n\n![mutipass preview][preview]\n\n__Better authentication for HTTP__\n\nMultipass is like [HTTP Basic authentication][basic-auth] but better and without passwords.\n\nMultipass implements the idea to authenticate users based on __something they own__ instead of __something they know__. This is better known as the second factor of [Two-factor Authentication][2fa].\n\nMultipass comes in two forms; a single binary to run in front of your web services and as a [package](#include-in-go-project) to include in your Go project.\n\n\n### Installation\n\nDownload the binary from the [releases][releases] page or [build](#build) from source.\n\n\n### Usage\n\n\n```sh\n$ multipass -conf multipass.conf\n```\n\nFor an example configuration see [Configuration](#configuration).\n\n\n### Contribution\n\nBug reports and feature requests are welcome. Follow GiHub's guide to [using-pull-requests].\n\n\n### Goal\n\nProtect internet exposed web resources and services with automatic HTTPS (TLS) and provide user friendly authentication.\n\n\n### Motivation\n\nMany private web resources and services end up exposed on the internet, accessible by anyone. Think IP video cameras, Key-value stores, analytic applications and many more. Using Multipass, these web resources and services can be protected using automatic HTTPS (TLS) and access can be granted on an individual basis.\n\n\nFurther reading\n---------------\n\n- [Configuration](#configuration)\n- [How it Works](#how-it-works)\n\t- User Flow\n\t- Configuration\n\t- JWT\n\t- RSA Key Pairs\n\t- Automatic HTTPS\n\t- Reverse Proxy\n- [Include in Go project](#include-in-go-project)\n- [Extending](#extending)\n\n\nBuild\n-----\n\nThe Multipass binary depends on the excellent [Caddy][caddy] webserver.\n\n\n1. Get the Caddy web server source code:\n\n\t```sh\n\t$ go get github.com/mholt/caddy\n\t```\n\n2. Register Multipass as a caddy plugin by adding multipass to the caddy directive:\n\n\tOpen `$GOPATH/src/github.com/mholt/caddy/caddyhttp/httpserver/plugin.go` in your favorite editor and make the following changes.\n\n\t```go\n\tvar directives = []string{\n\t\t...\n\t \t\"expvar\",\n\t\t\"multipass\", // \u003c- insert this line somewhere before \"proxy\"\n\t\t\"proxy\",\n\t\t...\n\t}\n\t```\n\n3. Get the Multipass source code and build the command:\n\n\t```sh\n\t$ go get github.com/namsral/multipass\n\t$ go install github.com/namsral/multipass/cmd/multipass\n\t```\n\nThe next thing is to create a configuration file and run the multipass command.\n\n\nConfiguration\n-------------\n\n### Syntax\n\nUse the following syntax:\n\n```\nmultipass {\n\tresources   path [path]\n\thandles     email [email]\n\tbasepath    path\n\texpires     duration\n\tsmtp_addr   host:port\n\tsmtp_user   username\n\tsmtp_pass   password\n\tsmtp_client command [args...]\n\tmail_from   email\n\tmail_tmpl   path\n}\n```\n\n- __resources__: path of resources to protect. _Default: /_\n- __handles__: the handles which identify the users; accepts wildcards like '@' and '@dallas'. _Required_\n- __basepath__: path to the log-in and sign-out page. _Default: /multipass_\n- __expires__: The time duration after which the token expires. Any time duration Go can [parse][goduration]. _Default: 24h_\n- __smtp_addr__: Mailserver address used for sending login links. _Default: localhost:25_\n- __smtp_user__: Mailserver username used for authentication.\n- __smtp_pass__: Mailserver password used for authentication.\n- __smtp_client__: SMTP client command with arguments. Mutually exclusive with __smtp_addr__\n- __mail_from__: From address used in email messages sent to users. _Required_\n- __mail_tmpl__: Path to mail template to override deault subject, plain and html body.\n\n### Examples:\n\nIn the following example, the service running on `localhost:2016` is proxied and protected to allow only users with handles leeloo@dallas and korben@dallas to access the  `/fhloston` and `/paradise` resources.\n\n```\nexample.com {\n\tbind 0.0.0.0\n\tmultipass {\n\t\tresources /fhloston /paradise\n\t\thandles leeloo@dallas korben@dallas\n\t\tbasepath /multipass\n\t\texpires 24h\n\t\tsmtp_addr localhost:2525\n\t\tmail_from \"Multipass \u003cno-reply@dallas\u003e\"\n\t}\n\tproxy / localhost:2016\n\tlog stdout\n}\n```\n\nSame example but replaced the SMTP server with a SMTP client and accepts a domain wildcard:\n\n```\nexample.com {\n\tbind 0.0.0.0\n\tmultipass {\n\t\tresources /fhloston /paradise\n\t\thandles @dallas\n\t\tbasepath /multipass\n\t\texpires 24h\n\t\tsmtp_client /usr/sbin/sendmail -t -i\n\t\tmail_from \"Multipass \u003cno-reply@dallas\u003e\"\n\t}\n\tproxy / localhost:2016\n\tlog stdout\n}\n```\n\n\nHow it works\n------------\n\nMultipass works by sending the user a login link with an embedded access token. When the user follows the login link the access token is stored in the browser session and used to authenticate the user on successive requests. The access token is a JSON web token containing claims specific to Multipass and signed with a RSA key pair.\n\n__User flow:__\n\n1. User visits protected resource\n2. User is redirected to log-in page and enters a known handle, e.g. email address\n3. An user access token is sent to user in the form of a login link\n4. User follows the login link and is granted access the protected resource\n\n\n__JWT__\n\nAccess tokens are signed [JSON Web Tokens][jwt] with specific claims like user handle and expire date. The tokens are embedded in login links which are sent to user.\n\n\n__RSA key pairs__\n\nA RSA key pair is used to sign user access tokens. These access tokens and other signatures can be verified by others using the public key made available at the url `[siteaddr][basepath]/pub.cer` when Multipass is running.\n\nYou can set your own private RSA key in the `MULTIPASS_RSA_PRIVATE_KEY` environment variable; make sure to PEM encode the private key.\n\nWhen no private key is set, the `MULTIPASS_RSA_PRIVATE_KEY` environment variable is empty, a RSA key pair is randomly generated and stored in the environment. This ensures signatures still validate after Multipass reloads during a configuration reload.\n\n\n__Automatic HTTPS__\n\nMultipass piggybacks on the [Caddy][caddy] web server which comes with automatic HTTPS using [Let's Encrypt][lets] and many more [features and plugins][caddydocs].\n\n\n__Reverse Proxy__\n\nThe user handle which was used to authenticate the user is passed down to the protected web services as a HTTP header:\n\n```\nMultipass-Handle: \u003cuser handle\u003e\n```\n\n\nInclude in Go project\n---------------------\n\nMultipass comes with `multipass.AuthHandler` which can wrap any [http.Handler][handler] to provide Multipass authentication. Handlers from other routers and frameworks can be supported, see the [caddy sub-package][caddy-sub-package] for an example.\n\nIn the example below, the appHandler function is wrapped using the AuthHandler\nwrapper. It assumes you have a SMTP service running on `localhost:2525` and\na user identified by email address leeloo@dallas whom has access to the resource at\n`/private`.\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/namsral/multipass\"\n\t\"github.com/namsral/multipass/services/email\"\n)\n\nfunc appHandler(w http.ResponseWriter, r *http.Request) {\n\thandle := r.Header.Get(\"Multipass-Handle\")\n\tif len(handle) == 0 {\n\t\thandle = \"anonymous\"\n\t}\n\tswitch r.URL.Path {\n\tcase \"/\", \"/private\":\n\t\tfmt.Fprintf(w, \"Hello %s, welcome to %s\", handle, r.URL.Path)\n\t\treturn\n\t}\n\thttp.NotFound(w, r)\n}\n\nfunc main() {\n\tservice, err := email.NewUserService(email.Options{\n\t\tSMTPAddr: \"localhost:2525\",\n\t\tFromAddr: \"Multipass Bot \u003cnoreply@dallas\u003e\",\n\t})\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tservice.AddHandle(\"leeloo@dallas\") // Register user\n\tservice.AddResource(\"/private\")    // Make resource private\n\n\taddr := \"localhost:6080\"\n\tsiteaddr := \"http://\" + addr\n\tm := multipass.New(siteaddr, multipass.Service(service))\n\n\th := multipass.AuthHandler(http.HandlerFunc(appHandler), m)\n\tlog.Fatal(http.ListenAndServe(addr, h))\n}\n```\n\n\nExtending\n---------\n\n_Extending Multipass by implementing the UserService interface._\n\nBy implementing the UserService, shown below, Multipass can be extended to support other _user handles_ which can identify and other ways to notify users of requested login URLs.\n\n```go\n// UserService is an interface used by the Multipass instance to query about\n// listed handles, authorized resource access and to notify users about login\n// urls. A handle is a unique user identifier, e.g. username, email address.\ntype UserService interface {\n\t// Listed returns true when the given handle is listed with the\n\t// service.\n\tListed(handle string) bool\n\n\t// Authorized returns true when the user identified by the given handle is\n\t// authorized to access the given resource at rawurl with the given method.\n\tAuthorized(handle, method, rawurl string) bool\n\n\t// Notify returns nil when the given login URL is successfully\n\t// communicated to the given handle.\n\tNotify(handle, loginurl string) error\n\n\t// Close closes any open connections.\n\tClose() error\n}\n```\n\n\n[lets]:https://letsencrypt.org\n[caddy]:https://caddyserver.com\n[caddydocs]:https://caddyserver.com/docs\n[jwt]:https://jwt.io\n[goduration]:https://golang.org/pkg/time/#ParseDuration\n[releases]:https://github.com/namsral/multipass/releases\n[2fa]:https://en.wikipedia.org/wiki/Multi-factor_authentication\n[using-pull-requests]:https://help.github.com/articles/using-pull-requests/\n[preview]: https://namsral.github.io/multipass/img/multipass.png \"Multipass preview image\"\n[handler]: https://golang.org/pkg/net/http/#Handler\n[basic-auth]: https://en.wikipedia.org/wiki/Basic_access_authentication \"Basic access authentication\"\n[caddy-sub-package]: https://godoc.org/github.com/namsral/multipass/caddy\n","funding_links":[],"categories":["Go","http"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnamsral%2Fmultipass","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnamsral%2Fmultipass","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnamsral%2Fmultipass/lists"}