session

package
v0.4.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 17, 2021 License: MIT Imports: 8 Imported by: 1

Documentation

Overview

Package session handles session management via secure cookies or JWT tokens. It can work in conjunction with the Authenticator middleware to ensure calls within sessions are properly authenticated.

Index

Examples

Constants

View Source
const (
	// AuthHeader is the header used to store the bearer token.
	AuthHeader = "Authorization"

	// TokenPrefix is applied/removed from the front of the JWT token.
	TokenPrefix = "Bearer "
)
View Source
const (
	// DefaultSize of a limiter of 1000 distinct actions.
	DefaultSize = 1000

	// DefaultLimit on an action which will trip the limiter if an action is
	// seen more than 10 times in the limiters window.
	DefaultLimit = 10

	// DefaultWindow for the limiter of a second.
	DefaultWindow = time.Second
)
View Source
const DefaultTTL = time.Hour * 12

DefaultTTL for signatory implementations, causing the session to expire after 12 hours.

Variables

View Source
var DefaultSigningMethod = jwt.SigningMethodHS512

DefaultSigningMethod is used by a JWT token if no signing method is set.

View Source
var ErrNoCodec = errors.New("no encoder/decoder defined")

ErrNoCodec is returned if a CookieSignatory is used without an codec being set.

View Source
var ErrNoCredentialsFound = errors.New("no credentials found")

ErrNoCredentialsFound is returned if an attempt is made to validate an empty bearer token.

Functions

func Delete

func Delete(r *http.Request)

Delete the context for the given request from the session cache.

Example
package main

import (
	"fmt"
	"net/http"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	r := &http.Request{}
	ctx := session.Context{"key": "value"}

	session.Store(r, ctx)
	session.Delete(r)
	fmt.Println(session.Get(r))

}
Output:

map[]

func Store

func Store(r *http.Request, ctx Context)

Store a context for the given request in the session cache.

Example
package main

import (
	"fmt"
	"net/http"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	r := &http.Request{}
	ctx := session.Context{"key": "value"}

	session.Store(r, ctx)
	fmt.Println(session.Get(r))

}
Output:

map[key:value]

Types

type Codec

type Codec interface {
	// Encode the named value. The resulting string can be used in a cookie.
	Encode(name string, value interface{}) (string, error)

	// Decode the named cookie string into the given interface.
	Decode(name, value string, in interface{}) error
}

Codec provides an interface to encode and decode a names value to and from a cookie string.

type Context

type Context map[string]string

Context stores session claims encoded in the JWT or secure cookie.

func Get

func Get(r *http.Request) Context

Get a context for the given request from the session cache. If no context exists an empty context is returned.

type CookieSignatory

type CookieSignatory struct {
	Name string
	Path string

	Secure bool

	TTL time.Duration

	Codec
}

CookieSignatory is a signatory that stores claims in a secure cookie. Name, Path and TTL all have sensible defaults, however, a valid Codec must be set. It is likely the codec will not use the TTL information set on the cookie given this can be manipulated, so the TTL may need to be correctly set on the codec as well as the CookieSignatory.

func (CookieSignatory) Clear

func (c CookieSignatory) Clear(w http.ResponseWriter)

Clear the current cookie. An empty cookie set to expire immediately is set in place of any session cookie.

Example
package main

import (
	"fmt"
	"net/http/httptest"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	w := httptest.ResponseRecorder{}
	session.CookieSignatory{}.Clear(&w)

	fmt.Println(w.Header().Get("Set-Cookie"))

}
Output:

cookie=; Path=/; Max-Age=0; HttpOnly; Secure

func (CookieSignatory) Sign

Sign and drop a cookie on the response. If an error is returned then no cookie will have been dropped.

Example
package main

import (
	"fmt"
	"net/http/httptest"

	"github.com/gorilla/securecookie"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	codec := securecookie.New(make([]byte, 64), make([]byte, 32))
	w := httptest.ResponseRecorder{}
	ctx := session.Context{"k": "v"}
	err := session.CookieSignatory{Codec: codec}.Sign(&w, ctx)

	if err != nil {
		fmt.Println(err)
	}

}
Output:

func (CookieSignatory) Validate

func (c CookieSignatory) Validate(r *http.Request) (Context, error)

Validate a request, returning the context for the request. If there is no valid context on the request then an empty Context is returned. An error will always return an empty Context.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/gorilla/securecookie"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	codec := securecookie.New(make([]byte, 64), make([]byte, 32))
	r := http.Request{Header: map[string][]string{}}
	w := httptest.ResponseRecorder{}
	sig := session.CookieSignatory{Codec: codec}

	_ = sig.Sign(&w, session.Context{"k": "v"})

	r.Header.Set("Cookie", w.Header().Get("Set-Cookie"))

	ctx, err := sig.Validate(&r)

	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(ctx)

}
Output:

map[k:v]

type JWTSignatory

type JWTSignatory struct {
	Secret string
	TTL    time.Duration
	Method jwt.SigningMethod
}

JWTSignatory is a signatory that stores claims in a JWT. While the blank signatory can be used it is advisable to set a Secret to properly secure the token.

func (JWTSignatory) Sign

func (j JWTSignatory) Sign(w http.ResponseWriter, ctx Context) error

Sign a response with the given Context. The Authorisation header is set on the response.

Example
package main

import (
	"fmt"
	"net/http/httptest"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	w := httptest.ResponseRecorder{}
	ctx := session.Context{"k": "v"}

	err := session.JWTSignatory{}.Sign(&w, ctx)

	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(w.Header().Get(session.AuthHeader)[:len(session.TokenPrefix)])

}
Output:

Bearer

func (JWTSignatory) Validate

func (j JWTSignatory) Validate(r *http.Request) (Context, error)

Validate a request by checking the Authorisation header. An empty Context is returned if there is an error validating the session.

Example
package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	r := http.Request{Header: map[string][]string{}}
	w := httptest.ResponseRecorder{}
	sig := session.JWTSignatory{}

	_ = sig.Sign(&w, session.Context{"k": "v"})

	r.Header.Set(session.AuthHeader, w.Header().Get(session.AuthHeader))

	ctx, err := sig.Validate(&r)

	if err != nil {
		fmt.Println(err)
	}

	fmt.Println(ctx)

}
Output:

map[k:v]

type Limiter

type Limiter struct {
	// Size of the limiter, in terms of how many distinct actions can be stored.
	// If adding an action takes the limiter over its defined size then the
	// least recently used action is removed. A size of less than 1 will result
	// in DefaultSize being used.
	Size int

	// Limit for the limiter. If an action string is seen more than Limit times
	// in Window the limiter will fail a check for the action. A Limit of less
	// than 1 will result in DefaultLimit being used.
	Limit int

	// Window of time the Limiter checks. Actions seen outside of the window
	// will not count towards the limit check. A Window of less than 1 will
	// result in DefaultWindow being used.
	Window time.Duration
	// contains filtered or unexported fields
}

A Limiter is used to limit an action, defined by a string describing that action. An action will fail a limit check if the limiter has seen the action string more than Limit times within the limiter Window. Limiters have sensible defaults if no limit values are set.

A Limiter will use at most approximately Size * Limit * 60bytes, however, actual memory usage is likely to be lower depending on the number of actions being handled.

func (*Limiter) Reject

func (l *Limiter) Reject(action string) bool

Reject return true if the given action should be rejected due to rate limits. Any expired hits for the action are pruned. If adding this action causes the backing cache to exceed its maximum size the least recently used item is ejected.

Example
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	l := session.Limiter{Limit: 2}

	fmt.Println(l.Reject("action"))
	fmt.Println(l.Reject("action"))
	fmt.Println(l.Reject("action"))

}
Output:

false
false
true
Example (Defaults)
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	l := session.Limiter{}

	// We don't expect to see these rejected as they're within the limit
	for i := 0; i < session.DefaultLimit; i++ {
		if l.Reject("action") {
			fmt.Println("rejected!")
		}
	}

	// This will be rejected as it takes us past the limit
	fmt.Println(l.Reject("action"))

}
Output:

true
Example (Overflow)
package main

import (
	"fmt"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	l := session.Limiter{Size: 1, Limit: 1}

	fmt.Println(l.Reject("action 1"))

	// Action 2 forces action 1 out of the limiter
	fmt.Println(l.Reject("action 2"))
	// Action 2 is now limited on its second action
	fmt.Println(l.Reject("action 2"))
	// Action 1 is now treated as a new action, pushing action 2 out
	fmt.Println(l.Reject("action 1"))
	// Action 2 is now a new action
	fmt.Println(l.Reject("action 2"))

}
Output:

false
false
true
false
false
Example (Timeout)
package main

import (
	"fmt"
	"time"

	"bitbucket.org/idomdavis/gohttp/session"
)

func main() {
	l := session.Limiter{Window: time.Nanosecond, Size: 1}

	fmt.Println(l.Reject("action"))
	time.Sleep(time.Nanosecond)
	// Second action is not limited as the first action has expired
	fmt.Println(l.Reject("action"))

}
Output:

false
false

type Signatory

type Signatory interface {
	// Sign a session with the given Context. A valid response will be written
	// as part of this.
	Sign(w http.ResponseWriter, ctx Context) error

	// Validate a request, returning a Context if the request is validated, or
	// and error otherwise.
	Validate(r *http.Request) (Context, error)
}

A Signatory is used to sign and validate sessions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL