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 ¶
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 " )
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 )
const DefaultTTL = time.Hour * 12
DefaultTTL for signatory implementations, causing the session to expire after 12 hours.
Variables ¶
var DefaultSigningMethod = jwt.SigningMethodHS512
DefaultSigningMethod is used by a JWT token if no signing method is set.
var ErrNoCodec = errors.New("no encoder/decoder defined")
ErrNoCodec is returned if a CookieSignatory is used without an codec being set.
var ErrNoCredentialsFound = errors.New("no credentials found")
ErrNoCredentialsFound is returned if an attempt is made to validate an empty bearer token.
Functions ¶
func Delete ¶
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 ¶
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 CookieSignatory ¶
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 ¶
func (c CookieSignatory) Sign(w http.ResponseWriter, ctx Context) error
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 ¶
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.