{"id":13408731,"url":"https://github.com/abraithwaite/jeff","last_synced_at":"2026-01-21T20:46:27.520Z","repository":{"id":49211358,"uuid":"143336943","full_name":"abraithwaite/jeff","owner":"abraithwaite","description":"🍍Jeff provides the simplest way to manage web sessions in Go.","archived":false,"fork":false,"pushed_at":"2025-01-23T02:09:23.000Z","size":90,"stargazers_count":270,"open_issues_count":2,"forks_count":17,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-01-21T15:36:26.046Z","etag":null,"topics":["cookie","csrf-protection","go","golang","http","middleware","session-management","sessions","web-framework","web-sessions"],"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/abraithwaite.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-02T19:31:23.000Z","updated_at":"2025-11-23T22:53:28.000Z","dependencies_parsed_at":"2024-01-08T14:30:43.194Z","dependency_job_id":"7d1acd8b-637c-401e-bce5-1269a6f42027","html_url":"https://github.com/abraithwaite/jeff","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/abraithwaite/jeff","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abraithwaite%2Fjeff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abraithwaite%2Fjeff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abraithwaite%2Fjeff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abraithwaite%2Fjeff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abraithwaite","download_url":"https://codeload.github.com/abraithwaite/jeff/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abraithwaite%2Fjeff/sbom","scorecard":{"id":161547,"data":{"date":"2025-08-11","repo":{"name":"github.com/abraithwaite/jeff","commit":"679b44de3bf63bbfc27db4b867330e2b37a6b9ad"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 1/13 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: containerImage not pinned by hash: Dockerfile:1: pin your Docker image by updating golang:1.10 to golang:1.10@sha256:6d5e79878a3e4f1b30b7aa4d24fb6ee6184e905a9b172fc72593935633be4c46","Warn: downloadThenRun not pinned by hash: Dockerfile:3","Info:   0 out of   1 containerImage dependencies pinned","Info:   0 out of   1 downloadThenRun dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T13:36:41.710Z","repository_id":49211358,"created_at":"2025-08-16T13:36:41.710Z","updated_at":"2025-08-16T13:36:41.710Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28642318,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T18:04:35.752Z","status":"ssl_error","status_checked_at":"2026-01-21T18:03:55.054Z","response_time":86,"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":["cookie","csrf-protection","go","golang","http","middleware","session-management","sessions","web-framework","web-sessions"],"created_at":"2024-07-30T20:00:54.877Z","updated_at":"2026-01-21T20:46:27.504Z","avatar_url":"https://github.com/abraithwaite.png","language":"Go","funding_links":[],"categories":["Authentication and Authorization","Authentication and OAuth","认证和OAuth授权","身份验证和OAuth","Uncategorized"],"sub_categories":["Contents"],"readme":"# jeff\n\n[![Build](https://circleci.com/gh/abraithwaite/jeff.svg?style=shield)](https://circleci.com/gh/abraithwaite/jeff)\n[![GoDoc](https://godoc.org/github.com/abraithwaite/jeff?status.svg)](https://godoc.org/github.com/abraithwaite/jeff)\n[![Go Report Card](https://goreportcard.com/badge/github.com/abraithwaite/jeff)](https://goreportcard.com/report/github.com/abraithwaite/jeff)\n[![License](https://img.shields.io/badge/license-BSD--3--Clause-5B74AD.svg)](https://github.com/abraithwaite/jeff/blob/master/LICENSE)\n\n\nA tool for managing login sessions in Go.\n\n## Motivation\n\nI was looking for a simple session management wrapper for Go and from what I\ncould tell there exists no simple sesssion library.\n\nThis library is requires a stateful backend to enable easy session revocation\nand simplify the security considerations.  See the section on security for more\ndetails.\n\n## Features\n\n- Redirect to login\n- Middleware wrapper\n- Easy to clear sessions\n- Small, idiomatic API\n- CSRF Protection\n- Context aware\n- Fast\n- Multiple sessions under one key\n\n## Requirements\n\nThe module uses msgpack for encoding and requires a recent version of Go to\nfunction.  It's recommended to have a version no older than 1 year, but\nthere's a hard requirement to have at least Go 1.11+.  Tests are only done\nagainst the latest stable version of Go.\n\n## Usage\n\nThere are three primary methods:\n\nSet starts the session, sets the cookie on the given response, and stores the\nsession token.\n\n```go\nfunc (s Server) Login(w http.ResponseWriter, r *http.Request) {\n    user = Authenticate(r)\n    if user != nil {\n        // Key must be unique to one user among all users\n        err := s.jeff.Set(r.Context(), w, user.Email)\n        // handle error\n    }\n    // finish login\n}\n```\n\nWrap authenticates every http.Handler it wraps, or redirects if authentication\nfails.  Wrap's signature works with [alice](https://github.com/justinas/alice).\nThe \"Public\" wrapper checks for an active session but _does not_ call the\nredirect handler if there is no active session.  It's a way to set the active\nsession on the request without denying access to anonymous users.\n\n```go\n    mux.HandleFunc(\"/login\", loginHandler)\n    mux.HandleFunc(\"/products\", j.Public(productHandler))\n    mux.HandleFunc(\"/users\", j.Wrap(usersHandler))\n    http.ListenAndServe(\":8080\", mux)\n```\n\nClear deletes the active session from the store for the given key.\n\n```go\nfunc (s Server) Revoke(w http.ResponseWriter, r *http.Request) {\n    // stuff to get user: admin input form or perhaps even from current session\n    err = s.jeff.Clear(r.Context(), user.Email)\n    // handle err\n}\n```\n\nThe default redirect handler redirects to root.  Override this behavior to set\nyour own login route.\n\n```go\n    sessions := jeff.New(store, jeff.Redirect(\n        http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n            http.Redirect(w, r, \"/login\", http.StatusFound)\n        })))\n```\n\nThis is primarily helpful to run custom logic on redirect:\n\n```go\n    // customHandler gets called when authentication fails\n    sessions := jeff.New(store, jeff.Redirect(customHandler))\n```\n\n## Design\n\nSession tokens are securely generated on `Set` (called after successful login).\nThis library is unique in that the user gets to decide the session key. This is\nto make it easier for operators to manage sessions by not having to track/store\nsession tokens after creating a session. Session keys don't have to be\ncryptographically secure, just unique per user.  A good key that works for\nmost people is the user's email.\n\nThe cookie format is as follows:\n\n    CookieName=SessionKey::SessionToken\n\nThe SessionKey is used to find the given session in the backend. If found, the\nclient SessionToken is then constant-time compared with the stored token.\n\nSessions are stored in the backend as a map from the application-chosen session\nkey to a list of active sessions.  Sessions are lazily cleaned up once they\nexpire.\n\n## Security\n\nMost of the existing solutions use encrypted cookies for authentication. This\nenables you to have stateless sessions.  However, this strategy has two major\ndrawbacks:\n\n- Single ultra-secret key.\n- Hard to revoke sessions.\n\nIt's possible to alleviate these concerns, but in the process one will end up\nmaking a stateful framework for revocation, and a complicated key management\nstrategy for de-risking the single point of failure key.\n\nWhy aren't we encrypting the cookie?\n\nEncrypting the cookie implies the single secret key used to encrypt said\ncookie.  Programs like [chamber](https://github.com/segmentio/chamber) can aid\nin handling these secrets, but any developer can tell you that accidentally\nlogging environment variables is commonplace.  I'd rather reduce the secrets\nrequired for my service to a minimum.\n\n### CSRF Protection\n\nThis library also provides limited CSRF protection via the SameSite session\ncookie attribute.  This attribute (implemented in modern browsers) limits a\nCross Origin Request to a subset of safe HTTP methods.  See the [OWASP\nGuide](https://www.owasp.org/index.php/SameSite) for more details.\n\n## Development\n\nClone the repo, run `docker-compose up -d`, then run `make test`.\n\nWith the local redis instance running, you can then run the example\napplication:  `go run ./cmd/example/main.go`.\n\n## Limitations\n\nAlso excluded from this library are flash sessions.  While useful, this is not\na concern for this library.  If you need this feature, please see one of the\nlibraries below.\n\n### Race Conditions\n\nThere is a race condition inherant in how this library handles expiration and\ndeletion of sessions.  Because sessions are stored as a list for each user, to\nadd, delete, or prune sessions, it's required to do a read, modify, write\nwithout any kind of transaction.  That means that it's possible, for example,\nfor a new session to be wiped out if it's created between reading and writing\nin another concurrent read-modify-write operation, or for a session which was\nmeant to be cleared, didn't get cleared because the clear was issued during\nanother processes' modify step in the read-modify-write cycle.\n\nIn practice, this should be quite rare but for people considering this for\nshort-lived sessions with high numbers of concurrent sessions per user, you\nmight want to reconsider.\n\n\n\n## Alternatives\n\nThe most popular session management tool is in the gorilla toolkit. It uses\nencrypted cookies by default.  Has a very large API.\n\nhttps://github.com/gorilla/sessions\n\nA comprehensive session management tool.  Also a very large API.  Heavy use of\nnaked interfaces.\n\nhttps://github.com/kataras/go-sessions\n\nEncrypted cookie manager by default.  Has middleware feature.  Big API. No easy\nway to clear session without storing session token elsewhere.\n\nhttps://github.com/alexedwards/scs\n\nLightweight, server-only API.  Uncertain about what the purpose of the Manager\ninterface is.  Heavy use of naked interface.\n\nhttps://github.com/icza/session\n\nLightweight, server-only API.  Includes concept of Users in library. No\nwrapping or middleware.\n\nhttps://github.com/rivo/sessions\n\nBatteries-included middleware for keeping track of users, login states and\npermissions.  Very large API.\n\nhttps://github.com/xyproto/permissions2\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabraithwaite%2Fjeff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabraithwaite%2Fjeff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabraithwaite%2Fjeff/lists"}