guard

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Jul 23, 2020 License: MIT Imports: 8 Imported by: 0

README

Guard

Guard provides a mechanism for authentication.

Usage

// main.go
package main

import (
        "net/http"
        "time"

        "github.com/danikarik/guard"
)

func main() {
    grd, _ := guard.NewGuard([]byte("some secret")

    var mainHandler = func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    }

    var loginHandler = func(w http.ResponseWriter, r *http.Request) {
        if err := grd.Authenticate(w, "user_id"); err != nil {
            http.Error(w, "cannot set cookie", http.StatusBadRequest)
            return
        }
    }

    var protectedHandler = func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
    }

    var authRequired = func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            if r.URL.Path != "/protected" {
                next.ServeHTTP(w, r)
                return
            }

            sub, err := grd.Validate(r)
            if err != nil {
                switch err {
                case guard.ErrInvalidAccessToken:
                    http.Error(w, "invalid access token", http.StatusUnauthorized)
                    return
                case guard.ErrInvalidCSRFToken:
                    http.Error(w, "invalid csrf token", http.StatusBadRequest)
                    return
                default:
                    http.Error(w, err.Error(), http.StatusInternalServerError)
                    return
                }
            }

            // Lookup subject
            if sub != "user_id" {
                http.Error(w, "invalid user id", http.StatusUnauthorized)
                return
            }

            next.ServeHTTP(w, r)
        })
    }

    mux := http.NewServeMux()
    mux.HandleFunc("/", mainHandler)
    mux.HandleFunc("/login", loginHandler)
    mux.HandleFunc("/protected", protectedHandler)

    http.ListenAndServe(":8000", authRequired(mux))
}

Maintainers

@danikarik

License

This project is licensed under the MIT License.

Documentation

Overview

Example

Example is a package-level documentation example.

package main

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

	"github.com/danikarik/guard"
)

func main() {
	var (
		secret           = []byte("9ae55e7ad6c6d3a78312567840588eff0ece5a9be9e2d6e36a6e5b5547361ecb")
		userID           = "user_id"
		accessCookieName = "access_token"
		csrfCookieName   = "csrf_token"
		csrfHeaderName   = "X-CSRF-TOKEN"
	)

	// Create a new guard instance with unsecured cookie for testing purpose.
	grd, _ := guard.NewGuard(secret,
		guard.WithAccessCookieName(accessCookieName),
		guard.WithCSRFCookieName(csrfCookieName),
		guard.WithCSRFHeaderName(csrfHeaderName),
		guard.WithSecure(false),
	)

	// mainHandler responds 200
	var mainHandler = func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	}

	// loginHandler sets auth cookie.
	var loginHandler = func(w http.ResponseWriter, r *http.Request) {
		if err := grd.Authenticate(w, userID); err != nil {
			http.Error(w, "cannot set cookie", http.StatusBadRequest)
			return
		}
	}

	// protected responds 200 if cookie is set
	var protectedHandler = func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	}

	// auth middleware checks cookie with guard instance.
	var authRequired = func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.URL.Path != "/protected" {
				next.ServeHTTP(w, r)
				return
			}

			sub, err := grd.Validate(r)
			if err != nil {
				switch err {
				case guard.ErrInvalidAccessToken:
					http.Error(w, "invalid access token", http.StatusUnauthorized)
					return
				case guard.ErrInvalidCSRFToken:
					http.Error(w, "invalid csrf token", http.StatusBadRequest)
					return
				default:
					http.Error(w, err.Error(), http.StatusInternalServerError)
					return
				}
			}

			// Lookup subject
			if sub != userID {
				http.Error(w, "invalid user id", http.StatusUnauthorized)
				return
			}

			next.ServeHTTP(w, r)
		})
	}

	// Create a new router.
	mux := http.NewServeMux()
	mux.HandleFunc("/", mainHandler)
	mux.HandleFunc("/login", loginHandler)
	mux.HandleFunc("/protected", protectedHandler)

	// Insert our middleware before the router
	handlerChain := authRequired(mux)

	// Request public endpoint
	req, _ := http.NewRequest("GET", "/", nil)
	rr := httptest.NewRecorder()
	handlerChain.ServeHTTP(rr, req)

	fmt.Println(rr.Code)

	// Request protected endpoint without cookie
	req, _ = http.NewRequest("GET", "/protected", nil)
	rr = httptest.NewRecorder()
	handlerChain.ServeHTTP(rr, req)

	fmt.Println(rr.Code)

	// Request login endpoint
	req, _ = http.NewRequest("GET", "/login", nil)
	rr = httptest.NewRecorder()
	handlerChain.ServeHTTP(rr, req)

	// Set access and csrf cookie and repeat request
	req, _ = http.NewRequest("GET", "/protected", nil)
	for _, cookie := range rr.Result().Cookies() {
		if cookie.Name == csrfCookieName {
			// Set csrf cookie value into header
			req.Header.Set(csrfHeaderName, cookie.Value)
		} else {
			// Save access token cookie
			req.AddCookie(cookie)
		}
	}
	rr = httptest.NewRecorder()
	handlerChain.ServeHTTP(rr, req)

	fmt.Println(rr.Code)

}
Output:

200
401
200

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrNilStandardClaims returned by UserClaims.Valid if there is no standard claims found.
	ErrNilStandardClaims = errors.New("guard: standard claims cannot be nil")
	// ErrEmptyCSRFToken returned by UserClaims.Valid if csrf token is empty.
	ErrEmptyCSRFToken = errors.New("guard: empty CSRF token")
)
View Source
var (
	// ErrEmptySecret when NewGuard takes empty secret.
	ErrEmptySecret = errors.New("guard: empty signing secret")
	// ErrEmptySubject when Authenticate takes empty subject.
	ErrEmptySubject = errors.New("guard: empty subject")
	// ErrInvalidAccessToken when Validate cannot get access token cookie.
	ErrInvalidAccessToken = errors.New("guard: missing or malformed access token")
	// ErrInvalidCSRFToken when Validate cannot get csrf token or token mismatches with original.
	ErrInvalidCSRFToken = errors.New("guard: missing or invalid csrf token")
	// ErrUnexpectedSigningMethod when signing method differs from default.
	ErrUnexpectedSigningMethod = errors.New("guard: unexpected signing method")
)
View Source
var (
	// ErrEmptyCookieName raises if WithAccessCookieName or WithCSRFCookieName gets empty cookie name.
	ErrEmptyCookieName = errors.New("guard: cookie name cannot be blank")
	// ErrEmptyHeaderName raises if WithCSRFHeaderName gets empty header name.
	ErrEmptyHeaderName = errors.New("guard: header name cannot be blank")
	// ErrInvalidCookiePath raises if WithPath gets invalid cookie path.
	ErrInvalidCookiePath = errors.New("guard: invalid cookie path")
	// ErrInvalidCookieTTL raises if WithTTL gets zero cookie expiration duration.
	ErrInvalidCookieTTL = errors.New("guard: cookie expiration cannot be zero")
)

Functions

This section is empty.

Types

type Guard

type Guard struct {
	// contains filtered or unexported fields
}

Guard is used to authenticate response and validate request.

func NewGuard

func NewGuard(secret []byte, opts ...Option) (*Guard, error)

NewGuard returns a new instance of Guard.

func (*Guard) Authenticate

func (g *Guard) Authenticate(w http.ResponseWriter, subject string) error

Authenticate saves subject into access token cookie with csrf token.

func (*Guard) Validate

func (g *Guard) Validate(r *http.Request) (string, error)

Validate incoming request and returns extracted subject.

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option configures a Guard.

func WithAccessCookieName

func WithAccessCookieName(name string) Option

WithAccessCookieName sets access cookie name.

func WithCSRFCookieName

func WithCSRFCookieName(name string) Option

WithCSRFCookieName sets csrf cookie name.

func WithCSRFHeaderName

func WithCSRFHeaderName(name string) Option

WithCSRFHeaderName sets csrf header name.

func WithDomain

func WithDomain(name string) Option

WithDomain sets cookie domain.

func WithIssuer

func WithIssuer(name string) Option

WithIssuer sets jwt token issuer.

func WithPath

func WithPath(path string) Option

WithPath sets cookie path.

func WithSecure

func WithSecure(flag bool) Option

WithSecure sets cookie secure flag.

func WithTTL

func WithTTL(ttl time.Duration) Option

WithTTL sets cookie expiration time.

type UserClaims

type UserClaims struct {
	*jwt.StandardClaims

	CSRFToken string `json:"csrfToken"`
}

UserClaims holds default jwt claims and csrf token.

func (*UserClaims) Valid

func (c *UserClaims) Valid() error

Valid checks whether claims are valid or not.

Jump to

Keyboard shortcuts

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