idp

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2016 License: MIT Imports: 13 Imported by: 0

README

Identity Provider (IdP) for Hydra Build Status Gitter

This is a helper library for handling challenge requests from Hydra. IdP handles:

  • Storing challenge in a short lived cookie
  • Passing user's consent to Hydra
  • Retriving keys from Hydra and using them for JWT verification

About

Let's say we have an Identity Provider with:

  • /login endpoint that accepts Hydra's challenges
  • /consent endpoint that handles getting consent from the user

This is how challenge request is hadled with the IdP library:

Sequence Diagram

Initialization

import (
	"github.com/janekolszak/idp"
	"github.com/boj/rethinkstore"
	"time"
)

func main() {
	challengeCookieStore, err = rethinkstore.NewRethinkStore(/* RethinkDB address */,
	                                                         /* Database name */,
	                                                         "challengeCookies", 5, 5, []byte("something-very-secret"))
	// Return on error

	// How long do Challenge cookies live?
	challengeCookieStore.MaxAge(60 * 5) // 5 min

	// Create the IDP
	IDP = idp.NewIDP(&idp.IDPConfig{
		ClusterURL:            /* Hydra's address */,
		ClientID:              /* IDP's client ID */,
		ClientSecret:          /* IDP's client secret */,
		KeyCacheExpiration:    time.Duration(/* Key expiration time */) * time.Second,
		ClientCacheExpiration: time.Duration(/* Client info expiration */) * time.Second,
		CacheCleanupInterval:  time.Duration(/* Cache cleanup interval. Eg. 30 */) * time.Second,
		ChallengeStore:        challengeCookieStore,
	})

	// Connects with Hydra and fills caches
	err = IDP.Connect()
	// Return on error

}

Usage


func HandleChallengeGET(w http.ResponseWriter, r *http.Request) {
	// 0. Render HTML page with a login form
}

func HandleChallengePOST(w http.ResponseWriter, r *http.Request) {
	// 0. Parse and validate login data (username:password, login cookie etc)
	//    Return on error

	// 1. Verify user's credentials (eg. check username:password).
	//    Return on error
	//    Obtain userid

	// 2. Create a Challenge
	challenge, err := IDP.NewChallenge(r, userid)
	//    Return on error

	// 3. Save the Challenge to a cookie with a small TTL
	err = challenge.Save(w, r)
	//    Return on error

	// 4. Redirect to the consent endpoint
}

// Displays Consent screen. Here user agrees for listed scopes
func HandleConsentGET(w http.ResponseWriter, r *http.Request) {

	// 0. Get the Challenge from the cookie
	challenge, err := IDP.GetChallenge(r)
	//    Return on error

	// 1. Display consent screen
	//    Use challenge.User to get user's ID
	//    Use challenge.Scopes to display requested scopes

	// 2. If any error occured delete the Challenge cookie (optional)
	if err != nil {
		err = challenge.Delete(c.Writer, c.Request)
	}

	// 3. Render the HTML consent page
}

func HandleConsentPOST(w http.ResponseWriter, r *http.Request) {
	// 0. Get the Challenge from the cookie
	challenge, err := model.IDP.GetChallenge(c.Request)
	//    Return on error

    // 1. Parse and validate consent data (eg. form answer=y or list of scopes)
	//    Return on error

	// 2. If user refused access
	err = challenge.RefuseAccess(w, r)
	//    Return

	// 3. If userf agreed to grant access
	err = challenge.GrantAccessToAll(w, r)
	//    Return
}

Documentation

Index

Constants

View Source
const (
	VerifyPublicKey   = "VerifyPublic"
	ConsentPrivateKey = "ConsentPrivate"
)
View Source
const (
	SessionCookieName = "challenge"
)

Variables

View Source
var (
	ErrorBadPublicKey       = errors.New("cannot convert to public key")
	ErrorBadPrivateKey      = errors.New("cannot convert to private key")
	ErrorBadRequest         = errors.New("bad request")
	ErrorBadChallengeCookie = errors.New("bad format of the challenge cookie")
	ErrorChallengeExpired   = errors.New("challenge expired")
	ErrorNoSuchClient       = errors.New("there's no OIDC Client with such id")
	ErrorBadKey             = errors.New("bad key stored in the cache ")
	ErrorNotInCache         = errors.New("cache doesn't have the requested data")
)

Functions

func ClientInfoKey

func ClientInfoKey(clientID string) string

Types

type Challenge

type Challenge struct {
	Client   *hclient.Client
	Expires  time.Time
	Redirect string
	Scopes   []string

	// Set in the challenge endpoint, after authenticated.
	User string
	// contains filtered or unexported fields
}

func (*Challenge) Delete

func (c *Challenge) Delete(w http.ResponseWriter, r *http.Request) error

func (*Challenge) GrantAccessToAll

func (c *Challenge) GrantAccessToAll(w http.ResponseWriter, r *http.Request) error

func (*Challenge) RefuseAccess

func (c *Challenge) RefuseAccess(w http.ResponseWriter, r *http.Request) error

func (*Challenge) Save

func (c *Challenge) Save(w http.ResponseWriter, r *http.Request) error

Saves the challenge to it's session store

type IDP

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

func NewIDP

func NewIDP(config *IDPConfig) *IDP

func (*IDP) CacheConsentKey

func (idp *IDP) CacheConsentKey() error

func (*IDP) CacheVerificationKey

func (idp *IDP) CacheVerificationKey() error

func (*IDP) Close

func (idp *IDP) Close()

func (*IDP) Connect

func (idp *IDP) Connect() error

func (*IDP) GetChallenge

func (idp *IDP) GetChallenge(r *http.Request) (*Challenge, error)

func (*IDP) GetClient

func (idp *IDP) GetClient(clientID string) (*hclient.Client, error)

func (*IDP) GetConsentKey

func (idp *IDP) GetConsentKey() (*rsa.PrivateKey, error)

func (*IDP) GetVerificationKey

func (idp *IDP) GetVerificationKey() (*rsa.PublicKey, error)

func (*IDP) NewChallenge

func (idp *IDP) NewChallenge(r *http.Request, user string) (challenge *Challenge, err error)

type IDPConfig

type IDPConfig struct {
	ClientID              string        `yaml:"client_id"`
	ClientSecret          string        `yaml:"client_secret"`
	ClusterURL            string        `yaml:"hydra_address"`
	KeyCacheExpiration    time.Duration `yaml:"key_cache_expiration"`
	ClientCacheExpiration time.Duration `yaml:"client_cache_expiration"`
	CacheCleanupInterval  time.Duration `yaml:"cache_cleanup_interval"`
	ChallengeStore        sessions.Store
}

Jump to

Keyboard shortcuts

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