🍍Jeff provides the simplest way to manage web sessions in Go.

Overview

jeff

Build GoDoc Go Report Card License

A tool for managing login sessions in Go.

Motivation

I was looking for a simple session management wrapper for Go and from what I could tell there exists no simple sesssion library.

This library is requires a stateful backend to enable easy session revocation and simplify the security considerations. See the section on security for more details.

Features

  • Redirect to login
  • Middleware wrapper
  • Easy to clear sessions
  • Small, idiomatic API
  • CSRF Protection
  • Context aware
  • Fast
  • Multiple sessions under one key

Requirements

The module uses msgpack for encoding and requires a recent version of Go to function. It's recommended to have a version no older than 1 year, but there's a hard requirement to have at least Go 1.11+. Tests are only done against the latest stable version of Go.

Usage

There are three primary methods:

Set starts the session, sets the cookie on the given response, and stores the session token.

func (s Server) Login(w http.ResponseWriter, r *http.Request) {
    user = Authenticate(r)
    if user != nil {
        // Key must be unique to one user among all users
        err := s.jeff.Set(r.Context(), w, user.Email)
        // handle error
    }
    // finish login
}

Wrap authenticates every http.Handler it wraps, or redirects if authentication fails. Wrap's signature works with alice. The "Public" wrapper checks for an active session but does not call the redirect handler if there is no active session. It's a way to set the active session on the request without denying access to anonymous users.

    mux.HandleFunc("/login", loginHandler)
    mux.HandleFunc("/products", j.Public(productHandler))
    mux.HandleFunc("/users", j.Wrap(usersHandler))
    http.ListenAndServe(":8080", mux)

Clear deletes the active session from the store for the given key.

func (s Server) Revoke(w http.ResponseWriter, r *http.Request) {
    // stuff to get user: admin input form or perhaps even from current session
    err = s.jeff.Clear(r.Context(), user.Email)
    // handle err
}

The default redirect handler redirects to root. Override this behavior to set your own login route.

    sessions := jeff.New(store, jeff.Redirect(
        http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            http.Redirect(w, r, "/login", http.StatusFound)
        })))

This is primarily helpful to run custom logic on redirect:

    // customHandler gets called when authentication fails
    sessions := jeff.New(store, jeff.Redirect(customHandler))

Design

Session tokens are securely generated on Set (called after successful login). This library is unique in that the user gets to decide the session key. This is to make it easier for operators to manage sessions by not having to track/store session tokens after creating a session. Session keys don't have to be cryptographically secure, just unique per user. A good key that works for most people is the user's email.

The cookie format is as follows:

CookieName=SessionKey::SessionToken

The SessionKey is used to find the given session in the backend. If found, the client SessionToken is then constant-time compared with the stored token.

Sessions are stored in the backend as a map from the application-chosen session key to a list of active sessions. Sessions are lazily cleaned up once they expire.

Security

Most of the existing solutions use encrypted cookies for authentication. This enables you to have stateless sessions. However, this strategy has two major drawbacks:

  • Single ultra-secret key.
  • Hard to revoke sessions.

It's possible to alleviate these concerns, but in the process one will end up making a stateful framework for revocation, and a complicated key management strategy for de-risking the single point of failure key.

Why aren't we encrypting the cookie?

Encrypting the cookie implies the single secret key used to encrypt said cookie. Programs like chamber can aid in handling these secrets, but any developer can tell you that accidentally logging environment variables is commonplace. I'd rather reduce the secrets required for my service to a minimum.

CSRF Protection

This library also provides limited CSRF protection via the SameSite session cookie attribute. This attribute (implemented in modern browsers) limits a Cross Origin Request to a subset of safe HTTP methods. See the OWASP Guide for more details.

Development

Clone the repo, run docker-compose up -d, then run make test.

With the local redis instance running, you can then run the example application: go run ./cmd/example/main.go.

Limitations

Also excluded from this library are flash sessions. While useful, this is not a concern for this library. If you need this feature, please see one of the libraries below.

Race Conditions

There is a race condition inherant in how this library handles expiration and deletion of sessions. Because sessions are stored as a list for each user, to add, delete, or prune sessions, it's required to do a read, modify, write without any kind of transaction. That means that it's possible, for example, for a new session to be wiped out if it's created between reading and writing in another concurrent read-modify-write operation, or for a session which was meant to be cleared, didn't get cleared because the clear was issued during another processes' modify step in the read-modify-write cycle.

In practice, this should be quite rare but for people considering this for short-lived sessions with high numbers of concurrent sessions per user, you might want to reconsider.

Alternatives

The most popular session management tool is in the gorilla toolkit. It uses encrypted cookies by default. Has a very large API.

https://github.com/gorilla/sessions

A comprehensive session management tool. Also a very large API. Heavy use of naked interfaces.

https://github.com/kataras/go-sessions

Encrypted cookie manager by default. Has middleware feature. Big API. No easy way to clear session without storing session token elsewhere.

https://github.com/alexedwards/scs

Lightweight, server-only API. Uncertain about what the purpose of the Manager interface is. Heavy use of naked interface.

https://github.com/icza/session

Lightweight, server-only API. Includes concept of Users in library. No wrapping or middleware.

https://github.com/rivo/sessions

Batteries-included middleware for keeping track of users, login states and permissions. Very large API.

https://github.com/xyproto/permissions2

Issues
  • Fix redigo import

    Fix redigo import

    The currently used redigo import is incorrect, as is uses an old, invalid redigo version. See here for citation: https://github.com/gomodule/redigo/issues/486#issuecomment-633237866

    This PR fixes that, and tidies up all imports too

    opened by rsheasby 6
  • sessions: Clear doesn't return potential error

    sessions: Clear doesn't return potential error

    Clear function in sessions.go doesn't handle the return value of the clear function.

    https://github.com/abraithwaite/jeff/blob/a6a878dbd405856816ca5bd5b0899346b8ff8f5a/sessions.go#L225

    opened by yanniszark 4
  • Comparison to github.com/alexedwards/scs

    Comparison to github.com/alexedwards/scs

    A quick look at both libraries seems to show an important difference: github.com/alexedwards/scs buffers all response bytes. A bufferedResponseWriter{} buffering all output to catch session changes before headers are sent (

    Jeff seems to handle this better (but not easier) by making you call Set manually when headers should be set without wrapping your http.Handler.

    Don't use alexedwards/scs if you send large payloads to clients, stream response bodies, use websockets(?), etc.

    opened by Xeoncross 3
  • Can I replace authboss with yours?

    Can I replace authboss with yours?

    Excuse the question maybe very basic for you, but I'm learning day by day and I want to understand well.

    I am using authboss for a hobby project.

    But I do not need most of its features: I would just like to authenticate a user (email and password) if he has to use specific APIs.

    Do you think I can replace authboss with your project?

    opened by frederikhors 2
  • sessions: load all sessions for user

    sessions: load all sessions for user

    Hi @abraithwaite! First of all, I want to say thanks a lot for this package, I found out that popular packages were lacking for my use-case where I want to search sessions by user-id.

    However, after taking a look around, I couldn't find an exported function to get all active sessions for a user. This is helpful, for example, if the user is logged in from many devices and we want to revoke them all at once.

    Thoughts?

    opened by yanniszark 2
  • No way to delete a single session for a given user

    No way to delete a single session for a given user

    Currently, user's sessions are stored as a list of active sessions under a single key in redis, each with their own unique token.

    When we moved to this model, we didn't update the Clear and Delete methods to be able to clear just an individual session. As it stands, calling either of these methods will terminate all active sessions instead of just one, as it was originally intended.

    opened by abraithwaite 1
  • Evaluate using gokv as storage interface

    Evaluate using gokv as storage interface

    Hi, I saw your project in my GitHub feed because someone I follow starred it and when seeing the Storage interface I immediately thought that it would be a great fit for a project of mine: https://github.com/philippgille/gokv

    I'll paste the code here so you don't have to follow the links:

    jeff.Storage:

    type Storage interface {
    	Store(ctx context.Context, key, value []byte, exp time.Time)
    	Fetch(ctx context.Context, key []byte) (value []byte, err error)
    	Delete(ctx context.Context, key []byte) error
    }
    

    gokv.Store:

    type Store interface {
        Set(k string, v interface{}) error
        Get(k string, v interface{}) (found bool, err error)
        Delete(k string) error
        Close() error
    }
    

    Downsides:

    • gokv doesn't work with contexts yet
    • There's no expiration handling in the implementations, but you can save the whole item (that includes the expiration) and similarly to your memory implementation (here), just not return it when it's expired.

    Upsides:

    • The main upside is the number of implementations for the interface. There's a simple Go map, sync.Map, FreeCache, BigCache, bbolt (a.k.a. BoltDB), BadgerDB, LevelDB, Local files, Redis, Consul, etcd, Apache ZooKeeper, Memcached, Hazelcast, Amazon DynamoDB, Amazon S3, Azure Table Storage, Google Cloud Datastore, Alibaba Cloud Tablestore, MySQL, PostgreSQL, MongoDB, CockroachDB and Apache Ignite. And more to come :)
    • All implementations allow to save any struct instead of just a slice of bytes. That's useful for example to include the expiration when storing a session and allows extending the stored struct with more fields in the future

    This is exactly the use case that I had in mind when creating it: As a package creator you want your package users to to be able to use as many storage implementations as possible, so you only use a common key-value interface and then point package users to existing implementations.

    Maybe you can have a look at it and then I'd love to hear what you think :)

    opened by philippgille 1
  • Add control of SameSite prefix

    Add control of SameSite prefix

    For our application, we need our session cookies to have SameSite=None. SameSite=lax is hardcoded in the cookie setup, here: https://github.com/abraithwaite/jeff/blob/ade959fcada38fb6f6ad3f421ef28a4c6e9040ea/sessions.go#L198

    Can SameSite's setting become an Option on jeff.New()?

    opened by gavbaa 0
  • sessions: implictly clear session cookie

    sessions: implictly clear session cookie

    Breaking API change:

    Clear now implicitly clears the active session on the given context and also clears the cookie by setting the expiry date to the zero value of time.Time.

    Delete was added to replace the previous Clear behavior.

    opened by abraithwaite 0
  • context: add basic support for context

    context: add basic support for context

    This doesn't add full support. Doing so requires cooperation from the libraries. However, this does solve the case where a request is cancelled, we don't block and return control to the caller. The consequence is that we're leaking a goroutine for the remainder of the open request to memcache/redis (which shouldn't be long)

    Both libraries expose the ability to set timeouts on connections. It might be worthwhile to see how to integrate them more seamlessly:

    https://godoc.org/github.com/bradfitz/gomemcache/memcache#ConnectTimeoutError https://godoc.org/github.com/gomodule/redigo/redis#DoWithTimeout

    opened by abraithwaite 0
Owner
Alan Braithwaite
Currently moving bytes around @Segmentio. https://www.abraithwaite.net https://twitter.com/Caust1c
Alan Braithwaite
A dead simple, highly performant, highly customizable sessions middleware for go http servers.

If you're interested in jwt's, see my jwt library! Sessions A dead simple, highly performant, highly customizable sessions service for go http servers

Adam Hanna 66 Jul 25, 2022
makes it easy to keep track of user sessions on a Go API.

usersession is a simple way to keep track of user information on a Go API. it assigns a session ID and gives you a place to store the IP and some user

William Dillon 0 Dec 22, 2021
Auth Go microservice for managing authentication sessions

cryptomath-go-auth Auth Go microservice for managing authentication sessions. Install dependencies $ make deps Build $ make vendor $ make build Databa

Crypto Math 0 Mar 4, 2022
Go-Guardian is a golang library that provides a simple, clean, and idiomatic way to create powerful modern API and web authentication.

❗ Cache package has been moved to libcache repository Go-Guardian Go-Guardian is a golang library that provides a simple, clean, and idiomatic way to

Sanad Haj Yahya 388 Aug 2, 2022
Package goth provides a simple, clean, and idiomatic way to write authentication packages for Go web applications.

Goth: Multi-Provider Authentication for Go Package goth provides a simple, clean, and idiomatic way to write authentication packages for Go web applic

Mark Bates 3.8k Aug 9, 2022
This package provides json web token (jwt) middleware for goLang http servers

jwt-auth jwt auth middleware in goLang. If you're interested in using sessions, checkout my sessions library! README Contents: Quickstart Performance

Adam Hanna 218 Jul 29, 2022
SSH Manager - manage authorized_keys file on remote servers

SSH Manager - manage authorized_key file on remote servers This is a simple tool that I came up after having to on-boarding and off-boarding developer

Sam Ban 32 Jun 27, 2022
A tool to manage accounts and codes of Google Authenticator.

A tool to manage accounts and codes of Google Authenticator.

Jormin 5 Sep 10, 2021
K8s controller to manage the aws-auth configmap

aws-auth-manager A kuberneres controller to manage the aws-auth configmap in EKS using a new AWSAuthItem CRD. The aws-auth configmap is used to give R

Matteo Ruina 12 Jul 9, 2022
goRBAC provides a lightweight role-based access control (RBAC) implementation in Golang.

goRBAC goRBAC provides a lightweight role-based access control implementation in Golang. For the purposes of this package: * an identity has one or mo

Xing 1.3k Aug 11, 2022
Provides AWS STS credentials based on Google Apps SAML SSO auth with interactive GUI support

What's this This command-line tool allows you to acquire AWS temporary (STS) credentials using Google Apps as a federated (Single Sign-On, or SSO) pro

Quan Hoang 33 Jun 3, 2022
A reverse proxy that provides authentication with Google, Github or other providers.

A reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others) to validate accounts by email, domain or group.

OAuth2 Proxy 5.6k Aug 6, 2022
The mep-agent module provides proxy services for 3rd applications to MEP.

Mep-Agent Introduction Mep-Agent is a middleware that provides proxy services for third-party apps. It can help apps, which do not implement the ETSI

EdgeGallery 21 Mar 9, 2022
A reverse proxy that provides authentication with Google, Github or other providers.

A reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others) to validate accounts by email, domain

OAuth2 Proxy 5.6k Aug 7, 2022
AuthService is a service that provides authentication with Minecraft Mojang.

AuthService AuthService is a service that provides authentication with Minecraft Mojang. Protobuf is managed by Buf. Command to pull Protobuf files wi

Layercraft 1 Mar 11, 2022
JSON Web Token library

About … a JSON Web Token (JWT) library for the Go programming language. Feature complete Full test coverage Dependency free Key management The API enf

Pascal S. de Kloe 287 Jul 27, 2022
Safe, simple and fast JSON Web Tokens for Go

jwt JSON Web Token for Go RFC 7519, also see jwt.io for more. The latest version is v3. Rationale There are many JWT libraries, but many of them are h

cristaltech 557 Aug 6, 2022
Golang implementation of JSON Web Tokens (JWT)

jwt-go A go (or 'golang' for search engine friendliness) implementation of JSON Web Tokens NEW VERSION COMING: There have been a lot of improvements s

Dave Grijalva 10.4k Aug 3, 2022
Go session management for web servers (including support for Google App Engine - GAE).

Session The Go standard library includes a nice http server, but unfortunately it lacks a very basic and important feature: HTTP session management. T

András Belicza 107 Jun 1, 2022