fosite

package module
Version: v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2016 License: Apache-2.0 Imports: 13 Imported by: 401

README

Fosite security first OAuth2 framework

Build Status Coverage Status Go Report Card

The security first OAuth2 & OpenID Connect framework for Google's Go Language. Built simple, powerful and extensible. This library implements peer-reviewed IETF RFC6749, counterfeits weaknesses covered in peer-reviewed IETF RFC6819 and countermeasures various database attack scenarios, keeping your application safe when that hacker penetrates or leaks your database. OpenID Connect is implemented according to OpenID Connect Core 1.0 incorporating errata set 1 and includes all flows: code, implicit, hybrid.

This library considered and implemented:

OAuth2 and OpenID Connect are difficult protocols. If you want quick wins, we strongly encourage you to look at Hydra. Hydra is a secure, high performance, cloud native OAuth2 and OpenID Connect service that integrates with every authentication method imaginable and is built on top of Fosite.

Table of Contents

Motivation

Fosite was written because our OAuth2 and OpenID Connect service Hydra required a secure and extensible OAuth2 library. We had to realize that nothing matching our requirements was out there, so we decided to build it ourselves.

API Stability

The core public API is almost stable as most changes will only touch the inner workings.

We strongly encourage vendoring fosite using glide or comparable tools.

Example

The example does not have nice visuals but it should give you an idea of what you can do with Fosite and a few lines of code.

Authorize Code Grant

You can run this minimalistic example by doing

go get github.com/Masterminds/glide
go get -d github.com/ory-am/fosite
cd $GOPATH/src/github.com/ory-am/fosite
glide install
go install github.com/ory-am/fosite/fosite-example
fosite-example

There should be a server listening on localhost:3846. You can check out the example's source code here.

A word on quality

We tried to set up as many tests as possible and test for as many cases covered in the RFCs as possible. But we are only human. Please, feel free to add tests for the various cases defined in the OAuth2 RFCs 6749 and 6819 or any other cases that improve the tests.

Everyone writing an RFC conform test that breaks with the current implementation, will receive a place in the Hall of Fame!

A word on security

Please be aware that Fosite only secures parts your server side security. You still need to secure your apps and clients, keep your tokens safe, prevent CSRF attacks, ensure database security, use valid and strong TLS certificates and much more. If you need any help or advice feel free to contact our security staff through our website!

We have given the various specifications, especially OAuth 2.0 Threat Model and Security Considerations, a very close look and included everything we thought was in the scope of this framework. Here is a complete list of things we implemented in Fosite:

Additionally, we added these safeguards:

  • Enforcing random states: Without a random-looking state or OpenID Connect nonce the request will fail.
  • Advanced Token Validation: Tokens are layouted as <key>.<signature> where <signature> is created using HMAC-SHA256 using a global secret. This is what a token can look like: /tgBeUhWlAT8tM8Bhmnx+Amf8rOYOUhrDi3pGzmjP7c=.BiV/Yhma+5moTP46anxMT6cWW8gz5R5vpC9RbpwSDdM=

Sections below Section 5 that are not covered in the list above should be reviewed by you. If you think that a specific section should be something that is covered in Fosite, feel free to create an issue. Please be aware that OpenID Connect requires specific knowledge of the identity provider, which is why Fosite only implements core requirements and most things must be implemented by you (for example prompt, max_age, ui_locales, id_token_hint, user authentication, session management, ...).

It is strongly encouraged to use the handlers shipped with Fosite as they follow the specs and are well tested.

A word on extensibility

Fosite is extensible ... because OAuth2 is an extensible and flexible framework. Fosite let's you register custom token and authorize endpoint handlers with the security that the requests have been validated against the OAuth2 specs beforehand. You can easily extend Fosite's capabilities. For example, if you want to provide OpenID Connect on top of your OAuth2 stack, that's no problem. Or custom assertions, what ever you like and as long as it is secure. ;)

Installation

You will need Go installed on your machine and it is required that you have set up your GOPATH environment variable.

go get -d github.com/ory-am/fosite

We recommend to use Glide or Godep to mitigate compatibility breaks that come with new api versions.

Documentation

There is an API documentation available at godoc.org/ory-am/fosite.

Scopes

Before we skip ahead, it is important to note how scopes are being used in fosite per default. The general pattern can be abstracted as <scope>.<sub-scope>.<sub-sub-scope>.<sub-sub-...>. Every level ("subset") contains all sublevels, too. For example:

  • blogposts grants scopes blogposts.create, blogposts.delete, blockposts.read, blohposts.own.read.
  • blogposts.create does not grant blogposts.delete nor blogposts.read nor blogposts.own.read.
  • blogposts.own.read does not grant blogposts.create.
  • blogposts.own grants blogposts.own.read but not blogposts.create.

You can replace the default scope strategy if you need a custom one by implementing fosite.ScopeStrategy.

Quickstart

Instantiating fosite by hand can be painful. Therefore we created a few convenience helpers available through the compose package. It is strongly encouraged to use these well tested composers.

In this very basic example, we will instantiate fosite with all OpenID Connect and OAuth2 handlers enabled. Please refer to the example app for more details.

This little code snippet sets up a full-blown OAuth2 and OpenID Connect example.

import "github.com/ory-am/fosite"
import "github.com/ory-am/fosite/compose"
import "github.com/ory-am/fosite/fosite-example/store"

// This is the exemplary storage that contains:
// * an OAuth2 Client with id "my-client" and secret "foobar" capable of all oauth2 and open id connect grant and response types.
// * a User for the resource owner password credentials grant type with usename "peter" and password "secret".
//
// You will most likely replace this with your own logic once you set up a real world application.
var storage = store.NewExampleStore()

// This secret is being used to sign access and refresh tokens as well as authorize codes.
var secret = []byte{"my super secret password"}

// check the api docs of compose.Config for further configuration options
var config = compose.Config {
  	AccessTokenLifespan: time.Minute * 30,
  	// ...
}

var oauth2Provider = compose.ComposeAllEnabled(config *Config, storage, secret, privateKey)

// The session will be persisted by the store and made available when e.g. validating tokens or handling token endpoint requests.
// The default OAuth2 and OpenID Connect handlers require the session to implement a few methods. Apart from that, the 
// session struct can be anything you want it to be.
type session struct {
	UserID string
	Foobar int
	
	// this is used in the OAuth2 handlers. You can set per-session expiry times here, for example.
	*oauth2Strat.HMACSession
	
	// this is used by the OpenID connect handlers. You can set custom id token headers and claims.
	*oidcStrat.DefaultSession
}

// The authorize endpoint is usually at "https://mydomain.com/oauth2/auth".
func authorizeHandlerFunc(rw http.ResponseWriter, req *http.Request) {
	// This context will be passed to all methods. It doesn't fulfill a real purpose in the standard library but could be used
	// to abort database lookups or similar things.
	ctx := NewContext()

	// Let's create an AuthorizeRequest object!
	// It will analyze the request and extract important information like scopes, response type and others.
	ar, err := oauth2.NewAuthorizeRequest(ctx, req)
	if err != nil {
		oauth2.WriteAuthorizeError(rw, ar, err)
		return
	}
	
	// Normally, this would be the place where you would check if the user is logged in and gives his consent.
	// We're simplifying things and just checking if the request includes a valid username and password
	if req.Form.Get("username") != "peter" {
		rw.Header().Set("Content-Type", "text/html; charset=utf-8")
		rw.Write([]byte(`<h1>Login page</h1>`))
		rw.Write([]byte(`
			<p>Howdy! This is the log in page. For this example, it is enough to supply the username.</p>
			<form method="post">
				<input type="text" name="username" /> <small>try peter</small><br>
				<input type="submit">
			</form>
		`))
		return
	}

	// Now that the user is authorized, we set up a session. When validating / looking up tokens, we additionally get
	// the session. You can store anything you want in it.
	mySessionData := &session{
		UserID: req.Form.Get("username")
	}

	// It's also wise to check the requested scopes, e.g.:
	// if authorizeRequest.GetScopes().Has("admin") {
	//     http.Error(rw, "you're not allowed to do that", http.StatusForbidden)
	//     return
	// }

	// Now we need to get a response. This is the place where the AuthorizeEndpointHandlers kick in and start processing the request.
	// NewAuthorizeResponse is capable of running multiple response type handlers which in turn enables this library
	// to support open id connect.
	response, err := oauth2.NewAuthorizeResponse(ctx, req, ar, mySessionData)
	if err != nil {
		oauth2.WriteAuthorizeError(rw, ar, err)
		return
	}

	// Awesome, now we redirect back to the client redirect uri and pass along an authorize code
	oauth2.WriteAuthorizeResponse(rw, ar, response)
}

// The token endpoint is usually at "https://mydomain.com/oauth2/token"
func tokenHandlerFunc(rw http.ResponseWriter, req *http.Request) {
	ctx := NewContext()
	mySessionData := &session{}

	// This will create an access request object and iterate through the registered TokenEndpointHandlers to validate the request.
	accessRequest, err := oauth2.NewAccessRequest(ctx, req, mySessionData)
	if err != nil {
		oauth2.WriteAccessError(rw, accessRequest, err)
		return
	}
	
	if mySessionData.UserID == "super-admin-guy" {
		// do something...
	}

	// Next we create a response for the access request. Again, we iterate through the TokenEndpointHandlers
	// and aggregate the result in response.
	response, err := oauth2.NewAccessResponse(ctx, req, accessRequest)
	if err != nil {
		oauth2.WriteAccessError(rw, accessRequest, err)
		return
	}

	// All done, send the response.
	oauth2.WriteAccessResponse(rw, accessRequest, response)

	// The client has a valid access token now
}

func someResourceProviderHandlerFunc(rw http.ResponseWriter, req *http.Request) {
	ctx := NewContext()
	mySessionData := &session{}
	requiredScope := "blogposts.create"

	ar, err := oauth2.ValidateToken(ctx, fosite.AccessTokenFromRequest(req), mySessionData, requiredScope)
	if err != nil {
		// ...
	}
	
	// if now error occurred the token + scope is valid and you have access to:
	// ar.GetClient().GetID(), ar.GetGrantedScopes(), ar.GetScopes(), mySessionData.UserID, ar.GetRequestedAt(), ...
}
Code Examples

Fosite provides integration tests as well as a http server example:

  • Fosite ships with an example app that runs in your browser: Example app.
  • If you want to check out how to enable specific handlers, check out the integration tests.

If you have working examples yourself, please share them with us!

Exemplary Storage Implementation

Fosite does not ship a storage implementation. This is intended, because requirements vary with every environment. You can find a reference implementation at fosite-example/internal/store.go. This storage fulfills requirements from all OAuth2 and OpenID Conenct handlers.

Extensible handlers

OAuth2 is a framework. Fosite mimics this behaviour by enabling you to replace existing or create new OAuth2 handlers. Of course, fosite ships handlers for all OAuth2 and OpenID Connect flows.

This section is missing documentation and we welcome any contributions in that direction.

Contribute

You need git and golang installed on your system.

go get -d github.com/ory-am/fosite
cd $GOPATH/src/github.com/ory-am/fosite
git status
git remote add myfork <url-to-your-fork>
go test ./...

Simple, right? Now you are ready to go! Make sure to run go test ./... often, detecting problems with your code rather sooner than later. Please read [CONTRIBUTE.md] before creating pull requests and issues.

Refresh mock objects

Run ./generate-mocks.sh in fosite's root directory or run the contents of [generate-mocks.sh] in a shell.

Hall of Fame

This place is reserved for the fearless bug hunters, reviewers and contributors (alphabetical order).

Find out more about the author of Fosite and Hydra, and the Ory Company.

Documentation

Index

Constants

View Source
const MinParameterEntropy = 8

Variables

View Source
var (
	ErrRequestUnauthorized     = errors.New("The request could not be authorized")
	ErrRequestForbidden        = errors.New("The request is not allowed")
	ErrInvalidRequest          = errors.New("The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed")
	ErrUnauthorizedClient      = errors.New("The client is not authorized to request a token using this method")
	ErrAccessDenied            = errors.New("The resource owner or authorization server denied the request")
	ErrUnsupportedResponseType = errors.New("The authorization server does not support obtaining a token using this method")
	ErrInvalidScope            = errors.New("The requested scope is invalid, unknown, or malformed")
	ErrServerError             = errors.New("The authorization server encountered an unexpected condition that prevented it from fulfilling the request")
	ErrTemporarilyUnavailable  = errors.New("The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server")
	ErrUnsupportedGrantType    = errors.New("The authorization grant type is not supported by the authorization server")
	ErrInvalidGrant            = errors.New("The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client")
	ErrInvalidClient           = errors.New("Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)")
	ErrInvalidState            = errors.Errorf("The state is missing or has less than %d characters and is therefore considered too weak", MinParameterEntropy)
	ErrInsufficientEntropy     = errors.Errorf("The request used a security parameter (e.g., anti-replay, anti-csrf) with insufficient entropy (minimum of %d characters)", MinParameterEntropy)
	ErrMisconfiguration        = errors.New("The request failed because of an internal error that is probably caused by misconfiguration")
	ErrNotFound                = errors.New("Could not find the requested resource(s)")
)
View Source
var ErrUnknownRequest = errors.New("The handler is not responsible for this request.")

Functions

func AccessTokenFromRequest added in v0.2.0

func AccessTokenFromRequest(req *http.Request) string

func GetRedirectURIFromRequestValues

func GetRedirectURIFromRequestValues(values url.Values) (string, error)

GetRedirectURIFromRequestValues extracts the redirect_uri from values but does not do any sort of validation.

Considered specifications

  • https://tools.ietf.org/html/rfc6749#section-3.1 The endpoint URI MAY include an "application/x-www-form-urlencoded" formatted (per Appendix B) query component ([RFC3986] Section 3.4), which MUST be retained when adding additional query parameters.

func HierarchicScopeStrategy added in v0.2.0

func HierarchicScopeStrategy(haystack []string, needle string) bool

func IsRedirectURISecure

func IsRedirectURISecure(redirectURI *url.URL) bool

func IsValidRedirectURI

func IsValidRedirectURI(redirectURI *url.URL) bool

IsValidRedirectURI validates a redirect_uri as specified in:

* https://tools.ietf.org/html/rfc6749#section-3.1.2

  • The redirection endpoint URI MUST be an absolute URI as defined by [RFC3986] Section 4.3.
  • The endpoint URI MUST NOT include a fragment component.

func MatchRedirectURIWithClientRedirectURIs

func MatchRedirectURIWithClientRedirectURIs(rawurl string, client Client) (*url.URL, error)

MatchRedirectURIWithClientRedirectURIs if the given uri is a registered redirect uri. Does not perform uri validation.

Considered specifications

  • https://tools.ietf.org/html/rfc6749#section-3.1.2.3 If multiple redirection URIs have been registered, if only part of the redirection URI has been registered, or if no redirection URI has been registered, the client MUST include a redirection URI with the authorization request using the "redirect_uri" request parameter.

    When a redirection URI is included in an authorization request, the authorization server MUST compare and match the value received against at least one of the registered redirection URIs (or URI components) as defined in [RFC3986] Section 6, if any redirection URIs were registered. If the client registration included the full redirection URI, the authorization server MUST compare the two URIs using simple string comparison as defined in [RFC3986] Section 6.2.1.

* https://tools.ietf.org/html/rfc6819#section-4.4.1.7

  • The authorization server may also enforce the usage and validation of pre-registered redirect URIs (see Section 5.2.3.5). This will allow for early recognition of authorization "code" disclosure to counterfeit clients.
  • The attacker will need to use another redirect URI for its authorization process rather than the target web site because it needs to intercept the flow. So, if the authorization server associates the authorization "code" with the redirect URI of a particular end-user authorization and validates this redirect URI with the redirect URI passed to the token's endpoint, such an attack is detected (see Section 5.2.4.5).

func NewContext

func NewContext() context.Context

func StringInSlice

func StringInSlice(needle string, haystack []string) bool

StringInSlice returns true if needle exists in haystack

Types

type AccessRequest

type AccessRequest struct {
	GrantTypes       Arguments `json:"grantTypes" gorethink:"grantTypes"`
	HandledGrantType Arguments `json:"handledGrantType" gorethink:"handledGrantType"`

	Request
}

func NewAccessRequest

func NewAccessRequest(session interface{}) *AccessRequest

func (*AccessRequest) GetGrantTypes

func (a *AccessRequest) GetGrantTypes() Arguments

type AccessRequester

type AccessRequester interface {
	// GetGrantType returns the requests grant type.
	GetGrantTypes() (grantTypes Arguments)

	Requester
}

AccessRequester is a token endpoint's request context.

type AccessResponder

type AccessResponder interface {
	// SetExtra sets a key value pair for the access response.
	SetExtra(key string, value interface{})

	// GetExtra returns a key's value.
	GetExtra(key string) interface{}

	SetExpiresIn(time.Duration)

	SetScopes(scopes Arguments)

	// SetAccessToken sets the responses mandatory access token.
	SetAccessToken(token string)

	// SetTokenType set's the responses mandatory token type
	SetTokenType(tokenType string)

	// SetAccessToken returns the responses access token.
	GetAccessToken() (token string)

	// GetTokenType returns the responses token type.
	GetTokenType() (token string)

	// ToMap converts the response to a map.
	ToMap() map[string]interface{}
}

AccessResponder is a token endpoint's response.

func NewAccessResponse

func NewAccessResponse() AccessResponder

type AccessResponse

type AccessResponse struct {
	Extra       map[string]interface{}
	AccessToken string
	TokenType   string
}

func (*AccessResponse) GetAccessToken

func (a *AccessResponse) GetAccessToken() string

func (*AccessResponse) GetExtra

func (a *AccessResponse) GetExtra(key string) interface{}

func (*AccessResponse) GetTokenType

func (a *AccessResponse) GetTokenType() string

func (*AccessResponse) SetAccessToken

func (a *AccessResponse) SetAccessToken(token string)

func (*AccessResponse) SetExpiresIn

func (a *AccessResponse) SetExpiresIn(expiresIn time.Duration)

func (*AccessResponse) SetExtra

func (a *AccessResponse) SetExtra(key string, value interface{})

func (*AccessResponse) SetScopes

func (a *AccessResponse) SetScopes(scopes Arguments)

func (*AccessResponse) SetTokenType

func (a *AccessResponse) SetTokenType(name string)

func (*AccessResponse) ToMap

func (a *AccessResponse) ToMap() map[string]interface{}

type Arguments

type Arguments []string

func (Arguments) Exact

func (r Arguments) Exact(name string) bool

func (Arguments) Has

func (r Arguments) Has(items ...string) bool

func (Arguments) Matches

func (r Arguments) Matches(items ...string) bool

type AuthorizeEndpointHandler

type AuthorizeEndpointHandler interface {
	// HandleAuthorizeRequest handles an authorize endpoint request. To extend the handler's capabilities, the http request
	// is passed along, if further information retrieval is required. If the handler feels that he is not responsible for
	// the authorize request, he must return nil and NOT modify session nor responder neither requester.
	//
	// The following spec is a good example of what HandleAuthorizeRequest should do.
	// * https://tools.ietf.org/html/rfc6749#section-3.1.1
	//   response_type REQUIRED.
	//   The value MUST be one of "code" for requesting an
	//   authorization code as described by Section 4.1.1, "token" for
	//   requesting an access token (implicit grant) as described by
	//   Section 4.2.1, or a registered extension value as described by Section 8.4.
	HandleAuthorizeEndpointRequest(ctx context.Context, req *http.Request, requester AuthorizeRequester, responder AuthorizeResponder) error
}

type AuthorizeEndpointHandlers

type AuthorizeEndpointHandlers []AuthorizeEndpointHandler

AuthorizeEndpointHandlers is a list of AuthorizeEndpointHandler

func (*AuthorizeEndpointHandlers) Append

Add adds an AuthorizeEndpointHandler to this list. Ignores duplicates based on reflect.TypeOf.

type AuthorizeRequest

type AuthorizeRequest struct {
	ResponseTypes        Arguments `json:"responseTypes" gorethink:"responseTypes"`
	RedirectURI          *url.URL  `json:"redirectUri" gorethink:"redirectUri"`
	State                string    `json:"state" gorethink:"state"`
	HandledResponseTypes Arguments `json:"handledResponseTypes" gorethink:"handledResponseTypes"`

	Request
}

AuthorizeRequest is an implementation of AuthorizeRequester

func NewAuthorizeRequest

func NewAuthorizeRequest() *AuthorizeRequest

func (*AuthorizeRequest) DidHandleAllResponseTypes

func (d *AuthorizeRequest) DidHandleAllResponseTypes() bool

func (*AuthorizeRequest) GetRedirectURI

func (d *AuthorizeRequest) GetRedirectURI() *url.URL

func (*AuthorizeRequest) GetResponseTypes

func (d *AuthorizeRequest) GetResponseTypes() Arguments

func (*AuthorizeRequest) GetState

func (d *AuthorizeRequest) GetState() string

func (*AuthorizeRequest) IsRedirectURIValid

func (d *AuthorizeRequest) IsRedirectURIValid() bool

func (*AuthorizeRequest) SetResponseTypeHandled

func (d *AuthorizeRequest) SetResponseTypeHandled(name string)

type AuthorizeRequester

type AuthorizeRequester interface {
	// GetResponseTypes returns the requested response types
	GetResponseTypes() (responseTypes Arguments)

	// SetResponseTypeHandled marks a response_type (e.g. token or code) as handled indicating that the response type
	// is supported.
	SetResponseTypeHandled(responseType string)

	// DidHandleAllResponseTypes returns if all requested response types have been handled correctly
	DidHandleAllResponseTypes() (didHandle bool)

	// GetRedirectURI returns the requested redirect URI
	GetRedirectURI() (redirectURL *url.URL)

	// IsRedirectURIValid returns false if the redirect is not rfc-conform (i.e. missing client, not on white list,
	// or malformed)
	IsRedirectURIValid() (isValid bool)

	// GetState returns the request's state.
	GetState() (state string)

	Requester
}

AuthorizeRequester is an authorize endpoint's request context.

type AuthorizeResponder

type AuthorizeResponder interface {
	// GetCode returns the response's authorize code if set.
	GetCode() string

	// GetHeader returns the response's header
	GetHeader() (header http.Header)

	// AddHeader adds an header key value pair to the response
	AddHeader(key, value string)

	// GetQuery returns the response's query
	GetQuery() (query url.Values)

	// AddQuery adds an url query key value pair to the response
	AddQuery(key, value string)

	// GetHeader returns the response's url fragments
	GetFragment() (fragment url.Values)

	// AddHeader adds a key value pair to the response's url fragment
	AddFragment(key, value string)
}

AuthorizeResponder is an authorization endpoint's response.

type AuthorizeResponse

type AuthorizeResponse struct {
	Header   http.Header
	Query    url.Values
	Fragment url.Values
	// contains filtered or unexported fields
}

AuthorizeResponse is an implementation of AuthorizeResponder

func NewAuthorizeResponse

func NewAuthorizeResponse() *AuthorizeResponse

func (*AuthorizeResponse) AddFragment

func (a *AuthorizeResponse) AddFragment(key, value string)

func (*AuthorizeResponse) AddHeader

func (a *AuthorizeResponse) AddHeader(key, value string)

func (*AuthorizeResponse) AddQuery

func (a *AuthorizeResponse) AddQuery(key, value string)

func (*AuthorizeResponse) GetCode

func (a *AuthorizeResponse) GetCode() string

func (*AuthorizeResponse) GetFragment

func (a *AuthorizeResponse) GetFragment() url.Values

func (*AuthorizeResponse) GetHeader

func (a *AuthorizeResponse) GetHeader() http.Header

func (*AuthorizeResponse) GetQuery

func (a *AuthorizeResponse) GetQuery() url.Values

type Client

type Client interface {
	// GetID returns the client ID.
	GetID() string

	// GetHashedSecret returns the hashed secret as it is stored in the store.
	GetHashedSecret() []byte

	// Returns the client's allowed redirect URIs.
	GetRedirectURIs() []string

	// Returns the client's allowed grant types.
	GetGrantTypes() Arguments

	// Returns the client's allowed response types.
	GetResponseTypes() Arguments

	// Returns the client's owner.
	GetOwner() string

	// Returns the scopes this client is allowed to request.
	GetScopes() Arguments
}

Client represents a client or an app.

type ClientManager

type ClientManager interface {
	// GetClient loads the client by its ID or returns an error
	// if the client does not exist or another error occurred.
	GetClient(id string) (Client, error)
}

ClientManager defines the (persistent) manager interface for clients.

type DefaultClient

type DefaultClient struct {
	ID                string   `json:"id" gorethink:"id"`
	Name              string   `json:"client_name" gorethink:"client_name"`
	Secret            []byte   `json:"client_secret,omitempty" gorethink:"client_secret"`
	RedirectURIs      []string `json:"redirect_uris" gorethink:"redirect_uris"`
	GrantTypes        []string `json:"grant_types" gorethink:"grant_types"`
	ResponseTypes     []string `json:"response_types" gorethink:"response_types"`
	Scopes            []string `json:"scopes" gorethink:"scopes"`
	Owner             string   `json:"owner" gorethink:"owner"`
	PolicyURI         string   `json:"policy_uri" gorethink:"policy_uri"`
	TermsOfServiceURI string   `json:"tos_uri" gorethink:"tos_uri"`
	ClientURI         string   `json:"client_uri" gorethink:"client_uri"`
	LogoURI           string   `json:"logo_uri" gorethink:"logo_uri"`
	Contacts          []string `json:"contacts" gorethink:"contacts"`
}

DefaultClient is a simple default implementation of the Client interface.

func (*DefaultClient) GetGrantTypes

func (c *DefaultClient) GetGrantTypes() Arguments

func (*DefaultClient) GetHashedSecret

func (c *DefaultClient) GetHashedSecret() []byte

func (*DefaultClient) GetID

func (c *DefaultClient) GetID() string

func (*DefaultClient) GetOwner

func (c *DefaultClient) GetOwner() string

func (*DefaultClient) GetRedirectURIs

func (c *DefaultClient) GetRedirectURIs() []string

func (*DefaultClient) GetResponseTypes

func (c *DefaultClient) GetResponseTypes() Arguments

func (*DefaultClient) GetScopes added in v0.2.0

func (c *DefaultClient) GetScopes() Arguments

type Fosite

type Fosite struct {
	Store                     Storage
	AuthorizeEndpointHandlers AuthorizeEndpointHandlers
	TokenEndpointHandlers     TokenEndpointHandlers
	TokenValidators           TokenValidators
	Hasher                    hash.Hasher
	Logger                    logrus.StdLogger
	ScopeStrategy             ScopeStrategy
}

Fosite implements OAuth2Provider.

func (*Fosite) NewAccessRequest

func (f *Fosite) NewAccessRequest(ctx context.Context, r *http.Request, session interface{}) (AccessRequester, error)

Implements

  • https://tools.ietf.org/html/rfc6749#section-2.3.1 Clients in possession of a client password MAY use the HTTP Basic authentication scheme as defined in [RFC2617] to authenticate with the authorization server. The client identifier is encoded using the "application/x-www-form-urlencoded" encoding algorithm per Appendix B, and the encoded value is used as the username; the client password is encoded using the same algorithm and used as the password. The authorization server MUST support the HTTP Basic authentication scheme for authenticating clients that were issued a client password. Including the client credentials in the request-body using the two parameters is NOT RECOMMENDED and SHOULD be limited to clients unable to directly utilize the HTTP Basic authentication scheme (or other password-based HTTP authentication schemes). The parameters can only be transmitted in the request-body and MUST NOT be included in the request URI.
  • https://tools.ietf.org/html/rfc6749#section-3.2.1
  • Confidential clients or other clients issued client credentials MUST authenticate with the authorization server as described in Section 2.3 when making requests to the token endpoint.
  • If the client type is confidential or the client was issued client credentials (or assigned other authentication requirements), the client MUST authenticate with the authorization server as described in Section 3.2.1.

func (*Fosite) NewAccessResponse

func (f *Fosite) NewAccessResponse(ctx context.Context, req *http.Request, requester AccessRequester) (AccessResponder, error)

func (*Fosite) NewAuthorizeRequest

func (c *Fosite) NewAuthorizeRequest(ctx context.Context, r *http.Request) (AuthorizeRequester, error)

func (*Fosite) NewAuthorizeResponse

func (o *Fosite) NewAuthorizeResponse(ctx context.Context, r *http.Request, ar AuthorizeRequester, session interface{}) (AuthorizeResponder, error)

func (*Fosite) ValidateToken added in v0.2.0

func (f *Fosite) ValidateToken(ctx context.Context, token string, tokenType TokenType, session interface{}, scopes ...string) (AccessRequester, error)

func (*Fosite) WriteAccessError

func (c *Fosite) WriteAccessError(rw http.ResponseWriter, _ AccessRequester, err error)

func (*Fosite) WriteAccessResponse

func (c *Fosite) WriteAccessResponse(rw http.ResponseWriter, requester AccessRequester, responder AccessResponder)

func (*Fosite) WriteAuthorizeError

func (c *Fosite) WriteAuthorizeError(rw http.ResponseWriter, ar AuthorizeRequester, err error)

func (*Fosite) WriteAuthorizeResponse

func (c *Fosite) WriteAuthorizeResponse(rw http.ResponseWriter, ar AuthorizeRequester, resp AuthorizeResponder)

type OAuth2Provider

type OAuth2Provider interface {
	// NewAuthorizeRequest returns an AuthorizeRequest.
	//
	// The following specs must be considered in any implementation of this method:
	// * https://tools.ietf.org/html/rfc6749#section-3.1
	//	 Extension response types MAY contain a space-delimited (%x20) list of
	//	 values, where the order of values does not matter (e.g., response
	//	 type "a b" is the same as "b a").  The meaning of such composite
	//	 response types is defined by their respective specifications.
	// * https://tools.ietf.org/html/rfc6749#section-3.1.2
	//   The redirection endpoint URI MUST be an absolute URI as defined by
	//   [RFC3986] Section 4.3.  The endpoint URI MAY include an
	//   "application/x-www-form-urlencoded" formatted (per Appendix B) query
	//   component ([RFC3986] Section 3.4), which MUST be retained when adding
	//   additional query parameters.  The endpoint URI MUST NOT include a
	//   fragment component.
	// * https://tools.ietf.org/html/rfc6749#section-3.1.2.2 (everything MUST be implemented)
	NewAuthorizeRequest(ctx context.Context, req *http.Request) (AuthorizeRequester, error)

	// NewAuthorizeResponse iterates through all response type handlers and returns their result or
	// ErrUnsupportedResponseType if none of the handler's were able to handle it.
	//
	// The following specs must be considered in any implementation of this method:
	// * https://tools.ietf.org/html/rfc6749#section-3.1.1
	//	 Extension response types MAY contain a space-delimited (%x20) list of
	//	 values, where the order of values does not matter (e.g., response
	//	 type "a b" is the same as "b a").  The meaning of such composite
	//	 response types is defined by their respective specifications.
	//	 If an authorization request is missing the "response_type" parameter,
	//	 or if the response type is not understood, the authorization server
	//	 MUST return an error response as described in Section 4.1.2.1.
	NewAuthorizeResponse(ctx context.Context, req *http.Request, requester AuthorizeRequester, session interface{}) (AuthorizeResponder, error)

	// WriteAuthorizeError returns the error codes to the redirection endpoint or shows the error to the user, if no valid
	// redirect uri was given. Implements rfc6749#section-4.1.2.1
	//
	// The following specs must be considered in any implementation of this method:
	// * https://tools.ietf.org/html/rfc6749#section-3.1.2
	//   The redirection endpoint URI MUST be an absolute URI as defined by
	//   [RFC3986] Section 4.3.  The endpoint URI MAY include an
	//   "application/x-www-form-urlencoded" formatted (per Appendix B) query
	//   component ([RFC3986] Section 3.4), which MUST be retained when adding
	//   additional query parameters.  The endpoint URI MUST NOT include a
	//   fragment component.
	// * https://tools.ietf.org/html/rfc6749#section-4.1.2.1 (everything)
	// * https://tools.ietf.org/html/rfc6749#section-3.1.2.2 (everything MUST be implemented)
	WriteAuthorizeError(rw http.ResponseWriter, requester AuthorizeRequester, err error)

	// WriteAuthorizeResponse persists the AuthorizeSession in the store and redirects the user agent to the provided
	// redirect url or returns an error if storage failed.
	//
	// The following specs must be considered in any implementation of this method:
	// * https://tools.ietf.org/html/rfc6749#rfc6749#section-4.1.2.1
	//   After completing its interaction with the resource owner, the
	//   authorization server directs the resource owner's user-agent back to
	//   the client.  The authorization server redirects the user-agent to the
	//   client's redirection endpoint previously established with the
	//   authorization server during the client registration process or when
	//   making the authorization request.
	// * https://tools.ietf.org/html/rfc6749#section-3.1.2.2 (everything MUST be implemented)
	WriteAuthorizeResponse(rw http.ResponseWriter, requester AuthorizeRequester, responder AuthorizeResponder)

	// NewAccessRequest creates a new access request object and validates
	// various parameters.
	//
	// The following specs must be considered in any implementation of this method:
	// * https://tools.ietf.org/html/rfc6749#section-3.2 (everything)
	// * https://tools.ietf.org/html/rfc6749#section-3.2.1 (everything)
	//
	// Furthermore the registered handlers should implement their specs accordingly.
	NewAccessRequest(ctx context.Context, req *http.Request, session interface{}) (AccessRequester, error)

	// NewAccessResponse creates a new access response and validates that access_token and token_type are set.
	//
	// The following specs must be considered in any implementation of this method:
	// https://tools.ietf.org/html/rfc6749#section-5.1
	NewAccessResponse(ctx context.Context, req *http.Request, requester AccessRequester) (AccessResponder, error)

	// WriteAccessError writes an access request error response.
	//
	// The following specs must be considered in any implementation of this method:
	// * https://tools.ietf.org/html/rfc6749#section-5.2 (everything)
	WriteAccessError(rw http.ResponseWriter, requester AccessRequester, err error)

	// WriteAccessResponse writes the access response.
	//
	// The following specs must be considered in any implementation of this method:
	// https://tools.ietf.org/html/rfc6749#section-5.1
	WriteAccessResponse(rw http.ResponseWriter, requester AccessRequester, responder AccessResponder)

	// ValidateToken validates a token. Popular validators include authorize code, id token, access token and refresh token.
	// Returns an error if validation failed.
	ValidateToken(ctx context.Context, token string, tokenType TokenType, session interface{}, scope ...string) (AccessRequester, error)
}

OAuth2Provider is an interface that enables you to write OAuth2 handlers with only a few lines of code. Check fosite.Fosite for an implementation of this interface.

type RFC6749Error

type RFC6749Error struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Hint        string `json:"-"`
	StatusCode  int    `json:"statusCode"`
	Debug       string `json:"-"`
}

func ErrorToRFC6749Error

func ErrorToRFC6749Error(err error) *RFC6749Error

type Request

type Request struct {
	RequestedAt   time.Time   `json:"requestedAt" gorethink:"requestedAt"`
	Client        Client      `json:"client" gorethink:"client"`
	Scopes        Arguments   `json:"scopes" gorethink:"scopes"`
	GrantedScopes Arguments   `json:"grantedScopes" gorethink:"grantedScopes"`
	Form          url.Values  `json:"form" gorethink:"form"`
	Session       interface{} `json:"session" gorethink:"session"`
}

Request is an implementation of Requester

func NewRequest

func NewRequest() *Request

func (*Request) AppendRequestedScope added in v0.2.0

func (a *Request) AppendRequestedScope(scope string)

func (*Request) GetClient

func (a *Request) GetClient() Client

func (*Request) GetGrantedScopes

func (a *Request) GetGrantedScopes() Arguments

func (*Request) GetRequestForm

func (a *Request) GetRequestForm() url.Values

func (*Request) GetRequestedAt

func (a *Request) GetRequestedAt() time.Time

func (*Request) GetRequestedScopes added in v0.2.0

func (a *Request) GetRequestedScopes() Arguments

func (*Request) GetSession

func (a *Request) GetSession() interface{}

func (*Request) GrantScope

func (a *Request) GrantScope(scope string)

func (*Request) Merge

func (a *Request) Merge(request Requester)

func (*Request) SetRequestedScopes added in v0.2.0

func (a *Request) SetRequestedScopes(s Arguments)

func (*Request) SetSession

func (a *Request) SetSession(session interface{})

type Requester

type Requester interface {
	// GetRequestedAt returns the time the request was created.
	GetRequestedAt() (requestedAt time.Time)

	// GetClient returns the requests client.
	GetClient() (client Client)

	// GetRequestedScopes returns the request's scopes.
	GetRequestedScopes() (scopes Arguments)

	// SetRequestedScopes sets the request's scopes.
	SetRequestedScopes(scopes Arguments)

	// AppendRequestedScope appends a scope to the request.
	AppendRequestedScope(scope string)

	// GetGrantScopes returns all granted scopes.
	GetGrantedScopes() (grantedScopes Arguments)

	// GrantScope marks a request's scope as granted.
	GrantScope(scope string)

	// GetSession returns a pointer to the request's session or nil if none is set.
	GetSession() (session interface{})

	// GetSession sets the request's session pointer.
	SetSession(session interface{})

	// GetRequestForm returns the request's form input.
	GetRequestForm() url.Values

	Merge(requester Requester)
}

Requester is an abstract interface for handling requests in Fosite.

type ScopeStrategy added in v0.2.0

type ScopeStrategy func(haystack []string, needle string) bool

ScopeStrategy is a strategy for matching scopes.

type Storage

type Storage interface {
	ClientManager
}

Storage defines fosite's minimal storage interface.

type TokenEndpointHandler

type TokenEndpointHandler interface {
	// PopulateTokenEndpointResponse is responsible for setting return values and should only be executed if
	// the handler's HandleTokenEndpointRequest did not return ErrUnknownRequest.
	PopulateTokenEndpointResponse(ctx context.Context, req *http.Request, requester AccessRequester, responder AccessResponder) error

	// HandleTokenEndpointRequest handles an authorize request. If the handler is not responsible for handling
	// the request, this method should return ErrUnknownRequest and otherwise handle the request.
	HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester AccessRequester) error
}

type TokenEndpointHandlers

type TokenEndpointHandlers []TokenEndpointHandler

TokenEndpointHandlers is a list of TokenEndpointHandler

func (*TokenEndpointHandlers) Append

Add adds an TokenEndpointHandler to this list. Ignores duplicates based on reflect.TypeOf.

type TokenType added in v0.2.0

type TokenType string
const (
	AccessToken   TokenType = "access_token"
	RefreshToken  TokenType = "refresh_token"
	AuthorizeCode TokenType = "authorize_code"
	IDToken       TokenType = "id_token"
)

type TokenValidator added in v0.2.0

type TokenValidator interface {
	ValidateToken(ctx context.Context, token string, tokenType TokenType, accessRequest AccessRequester, scopes []string) error
}

type TokenValidators added in v0.2.0

type TokenValidators []TokenValidator

TokenValidators is a list of TokenValidator

func (*TokenValidators) Append added in v0.2.0

func (t *TokenValidators) Append(h TokenValidator)

Add adds an AccessTokenValidator to this list. Ignores duplicates based on reflect.TypeOf.

Directories

Path Synopsis
pkg
handler
token
hmac
Package hmac is the default implementation for generating and validating challenges.
Package hmac is the default implementation for generating and validating challenges.
jwt
Package JWT is able to generate and validate json web tokens.
Package JWT is able to generate and validate json web tokens.

Jump to

Keyboard shortcuts

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