htsec

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Dec 7, 2024 License: MIT Imports: 8 Imported by: 1

README

gregoryv/htsec provides handler security using oauth2

The security detail, with it's guards, protect your handlers. In the oauth2 flow the state parameter is signed and verified.

Quick start

go get github.com/gregoryv/htsec

Documentation

Overview

Package htsec provides security detail for your endpoints.

The security detail with it's guards is used to authorize requests. Once authorized a slip with e.g. name, email and toke is returned for further use.

In the oauth2 flow the state is

GUARDNAME.RANDOM.SIGNATURE.DESTINATION

The random value, signature and security detail private key are used to verify the state.

The destination part can be used by you as a way to redirect users to whatever they wanted to get in the first place.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrState indicates something in the state data is invalid
	ErrState = fmt.Errorf("state")

	// ErrNotFound indicates named guard is not found
	ErrNotFound = fmt.Errorf("not found")
)

Functions

This section is empty.

Types

type Guard

type Guard struct {
	// A short name for use during login
	Name string

	// Used for the oauth2 flow
	oauth2.Config

	// Used to read contact information once authorized
	NewSlip func(client *http.Client) (*Slip, error)
	// contains filtered or unexported fields
}

type SecurityDetail added in v0.2.0

type SecurityDetail struct {
	// PrivateKey is used to verify the state during authorization.
	PrivateKey []byte
	// contains filtered or unexported fields
}
Example (Setup)
package main

import (
	"fmt"
	"net/http"

	"github.com/gregoryv/htsec"
	"github.com/gregoryv/htsec/github"
	"github.com/gregoryv/htsec/google"
)

// For a more complete example, see
// https://github.com/gregoryv/servant

func main() {
	sec := htsec.NewSecurityDetail(
		// define guards that will protect resources
		github.Guard(),
		google.Guard(),
	)
	h := NewRouter(sec)
	http.ListenAndServe(":8080", h)
}

func NewRouter(sec *htsec.SecurityDetail) *http.ServeMux {
	mx := http.NewServeMux()
	mx.HandleFunc("/{$}", frontpage)
	mx.Handle("/login", login(sec))
	// reuse the same callback endpoint
	mx.Handle("/oauth/redirect", callback(sec))

	// everything else is private
	mx.Handle("/", private())
	return mx
}

func frontpage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, `Login: <a href="/login?use=github">github</a>,
            <a href="/login?use=google>google></a>`,
	)
}

// login handles requests for selecting login method
func login(sec *htsec.SecurityDetail) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		name := r.URL.Query().Get("use")
		url, err := sec.GuardURL(name, "/")
		if err != nil {
			http.Redirect(w, r, "/", http.StatusSeeOther)
			return
		}
		http.Redirect(w, r, url, http.StatusTemporaryRedirect)
	}
}

func callback(sec *htsec.SecurityDetail) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		slip, err := sec.Authorize(r)
		if err != nil {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		// setup session ...
		_ = slip
		http.Redirect(w, r, "/inside", http.StatusSeeOther)
	}
}

// private returns handler of all protected endpoints
func private() http.Handler {
	mx := http.NewServeMux()
	mx.HandleFunc("/inside", inside)
	return protect(mx)
}

// protect wraps the given handler
func protect(next http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// verify session, see callback
		//if err := sessionValid(r); err != nil {
		//	http.Redirect(w, r, "/", http.StatusSeeOther)
		//  return
		//}
		next.ServeHTTP(w, r)
	}
}

func inside(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "You are inside!")
}
Output:

func NewSecurityDetail added in v0.2.0

func NewSecurityDetail(guards ...*Guard) *SecurityDetail

NewSecurityDetail returns a new group of guards. Names must be unique and PrivateKey is a random 32 byte slice.

func (*SecurityDetail) Authorize added in v0.2.0

func (s *SecurityDetail) Authorize(r *http.Request) (*Slip, error)

Authorize request based on the guardname in the request state.

func (*SecurityDetail) GuardURL added in v0.2.0

func (s *SecurityDetail) GuardURL(name, dest string) (string, error)

GuardURL returns url to the gate.

type Slip

type Slip struct {
	// GUARDNAME.RANDOM.SIGNATURE.DESTINATION
	//
	// GUARNAME - identifies the guard used
	// RANDOM - a random server side string used during verification
	// SIGNATURE - signature of the state based on the security detail private key
	// DESTINATION - the protected path you wanted to reach
	State string
	Token *oauth2.Token

	// Name of the authorized account
	Name string

	// Email of the authorized account
	Email string
}

func (*Slip) Destination added in v0.2.0

func (s *Slip) Destination() string

Destination returns the DESTINATION part of the state or empty if invalid.

Directories

Path Synopsis
Package github provides a github.com guard
Package github provides a github.com guard
Package google provides a google.com guard
Package google provides a google.com guard

Jump to

Keyboard shortcuts

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