fosite

package module
v0.0.0-...-745a4df Latest Latest
Warning

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

Go to latest
Published: Jan 15, 2016 License: Apache-2.0 Imports: 12 Imported by: 0

README

Fosite security first OAuth2 framework

The security first OAuth2 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.

Build Status Coverage Status

Be aware that go get github.com/ory-am/fosite will give you the master branch, which is and always will be nightly. Once releases roll out, you will be able to fetch a specific fosite API version through gopkg.in. As of now, no stable v1 version exists.

During development, we reviewed the following open specifications:

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.

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/ory-am/fosite/fosite-example
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:

Not implemented yet:

Additionally, we added these safeguards:

  • Enforcing random states: Without a random-looking state the request will fail.
  • Advanced Token Validation: Tokens are layouted as <key>.<signature> where <signature> is created using HMAC-SHA256, a global secret and the client's secret. Read more about this workflow in the proposal. This is what a token can look like: /tgBeUhWlAT8tM8Bhmnx+Amf8rOYOUhrDi3pGzmjP7c=.BiV/Yhma+5moTP46anxMT6cWW8gz5R5vpC9RbpwSDdM=
  • Enforging scopes: By default, you always need to include the fosite scope or fosite will not execute the request properly. Obviously, you can change the scope to basic or core but be aware that you should use scopes if you use OAuth2.

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.

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. ;)

Usage

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

Installation

You will need Go installed on your machine and it is required that you have set up your GOPATH environment variable. Fosite is being shipped through gopkg.in so new updates don't break your code. To see a full list of available versions check gopkg.in/ory-am/fosite.v0.

Right now, there is only an unstable release versioned as the v0 branch:

go get gopkg.in/ory-am/fosite.v0/...
Exemplary Authorization Endpoint
package main

import(
    "github.com/ory-am/fosite"
    "github.com/ory-am/handler/core/explicit"
	"golang.org/x/net/context"
)

func fositeFactory() fosite.OAuth2Provider {
    // NewMyStorageImplementation should implement all storage interfaces.
    var store = newMyStorageImplementation()

    f := fosite.NewFosite(store)
    accessTokenLifespan := time.Hour

    // Let's enable the explicit authorize code grant!
    explicitHandler := &explicit.AuthorizeExplicitGrantTypeHandler struct {
        Enigma:           &enigma.HMACSHAEnigma{GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows")},
        Store:            store,
        AuthCodeLifespan: time.Minute * 10,
    }
    f.AuthorizeEndpointHandlers.Add("code", explicitHandler)
    f.TokenEndpointHandlers.Add("code", explicitHandler)

    // Next let's enable the implicit one!
    implicitHandler := &implicit.AuthorizeImplicitGrantTypeHandler struct {
        Enigma:              &enigma.HMACSHAEnigma{GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows")},
        Store:               store,
        AccessTokenLifespan: accessTokenLifespan,
    }
    f.AuthorizeEndpointHandlers.Add("implicit", implicitHandler)

    return f
}

// Let's assume that we're in a http handler
func handleAuth(rw http.ResponseWriter, r *http.Request) {
    ctx := fosite.NewContext()

    // Let's create an AuthorizeRequest object!
    // It will analyze the request and extract important information like scopes, response type and others.
    authorizeRequest, err := oauth2.NewAuthorizeRequest(ctx, r)
    if err != nil {
       oauth2.WriteAuthorizeError(rw, req, err)
       return
    }

    // you have now access to authorizeRequest, Code ResponseTypes, Scopes ...
    // and can show the user agent a login or consent page
    //
    // or, for example:
    // if authorizeRequest.GetScopes().Has("admin") {
    //     http.Error(rw, "you're not allowed to do that", http.StatusForbidden)
    //     return
    // }

    // it would also be possible to redirect the user to an identity provider (google, microsoft live, ...) here
    // and do fancy stuff like OpenID Connect amongst others

    // Once you have confirmed the users identity and consent that he indeed wants to give app XYZ authorization,
    // you will use the user's id to create an authorize session
    user := "12345"

    // mySessionData is going to be persisted alongside the other data. Note that mySessionData is arbitrary.
    // You will however absolutely need the user id later on, so at least store that!
    mySessionData := struct {
        User string
        UsingIdentityProvider string
        Foo string
    } {
        User: user,
        UsingIdentityProvider: "google",
        Foo: "bar",
    }

    // if you want to support OpenID Connect, this would be a good place to do stuff like
    // user := getUserFromCookie()
    // mySessionData := NewImplementsOpenIDSession()
    // if authorizeRequest.GetScopes().Has("openid") {
    //     if authorizeRequest.GetScopes().Has("email") {
    //         mySessionData.AddField("email", user.Email)
    //     }
    //     mySessionData.AddField("id", user.ID)
    // }
    //

    // Now is the time to handle the response types
    // You can use a custom list of response type handlers by setting
    // oauth2.AuthorizeEndpointHandlers = []fosite.AuthorizeEndpointHandler{}
    //
    // Each AuthorizeEndpointHandler is responsible for managing his own state data. For example, the code response type
    // handler stores the access token and the session data in a database backend and retrieves it later on
    // when handling a grant type.
    //
    // If you use advanced AuthorizeEndpointHandlers it is a good idea to read the README first and check if your
    // session object needs to implement any interface. Think of the session as a persistent context
    // for the handlers.
    response, err := oauth2.NewAuthorizeResponse(ctx, req, authorizeRequest, &mySessionData)
    if err != nil {
       oauth2.WriteAuthorizeError(rw, req, err)
       return
    }

    // The next step is going to redirect the user by either using implicit or explicit grant or both (for OpenID connect)
    oauth2.WriteAuthorizeResponse(rw, authorizeRequest, response)

    // Done! The client should now have a valid authorize code!
}

// ...
Exemplary Token Endpoint
// ...

func handleToken(rw http.ResponseWriter, req *http.Request) {
    ctx := NewContext()

    // First we need to define a session object. Some handlers might require the session to implement
    // a specific interface, so keep that in mind when using them.
    var mySessionData struct {
        User string
        UsingIdentityProvider string
        Foo string
    }

    // This will create an access request object and iterate through the registered TokenEndpointHandlers.
    // These might populate mySessionData so do not pass nils.
    accessRequest, err := oauth2.NewAccessRequest(ctx, req, &mySessionData)
    if err != nil {
       oauth2.WriteAccessError(rw, accessRequest, err)
       return
    }

    // Now we have access to mySessionData's populated values and can do crazy things.

    // 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, &mySessionData)
    if err != nil {
       oauth2.WriteAccessError(rw, accessRequest, err)
       return
    }

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

You can replace the Token and Authorize endpoint logic by modifying Fosite.TokenEndpointHandlers and Fosite.AuthorizeEndpointHandlers.

Let's take the explicit authorize handler. He is responsible for handling the authorize code workflow.

If you want to enable the handler able to handle this workflow, you can do this:

handler := &explicit.AuthorizeExplicitGrantTypeHandler{
	Generator: &enigma.HMACSHAEnigma{GlobalSecret: []byte("some-super-cool-secret-that-nobody-knows")},
	Store:     myCodeStore, // Needs to implement CodeResponseTypeStorage
}
oauth2 := &fosite.Fosite{
	AuthorizeEndpointHandlers: fosite.AuthorizeEndpointHandlers{
		handler,
	},
	TokenEndpointHandlers: fosite.TokenEndpointHandlers{
		handler,
	},
}

As you probably noticed, there are two types of handlers, one for the authorization /auth endpoint and one for the token /token endpoint. The AuthorizeExplicitEndpointHandler implements API requirements for both endpoints, while, for example, the AuthorizeImplicitEndpointHandler only implements the AuthorizeEndpointHandler API.

You can find a complete list of handlers inside the handler directory. A short list is documented here:

Replaceable storage

Fosite does not ship a storage implementation yet. To get fosite running, you need to implement github.com/ory-am/fosite.Storage. Additionally, most of the token / authorize endpoint handlers require a store as well. It is probably smart to implement all of those interfaces in one struct.

Develop fosite

You need git and golang installed on your system.

go get github.com/ory-am/fosite/... -d
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.

Useful commands

Create storage mocks

mockgen -destination internal/storage.go github.com/ory-am/fosite Storage
mockgen -destination internal/core_client_storage.go github.com/ory-am/fosite/handler/core/client ClientCredentialsGrantStorage
mockgen -destination internal/core_explicit_storage.go github.com/ory-am/fosite/handler/core/explicit AuthorizeCodeGrantStorage
mockgen -destination internal/core_implicit_storage.go github.com/ory-am/fosite/handler/core/implicit ImplicitGrantStorage
mockgen -destination internal/core_owner_storage.go github.com/ory-am/fosite/handler/core/owner ResourceOwnerPasswordCredentialsGrantStorage
mockgen -destination internal/core_refresh_storage.go github.com/ory-am/fosite/handler/core/refresh RefreshTokenGrantStorage

Create handler mocks

mockgen -destination internal/authorize_handler.go github.com/ory-am/fosite AuthorizeEndpointHandler
mockgen -destination internal/token_handler.go github.com/ory-am/fosite TokenEndpointHandler

Create stateful "context" mocks

mockgen -destination internal/client.go github.com/ory-am/fosite/client Client
mockgen -destination internal/access_request.go github.com/ory-am/fosite AccessRequester
mockgen -destination internal/access_response.go github.com/ory-am/fosite AccessResponder
mockgen -destination internal/authorize_request.go github.com/ory-am/fosite AuthorizeRequester
mockgen -destination internal/authorize_response.go github.com/ory-am/fosite AuthorizeResponder

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

This section is empty.

Variables

View Source
var (
	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", minStateLength)
)
View Source
var DefaultRequiredScopeName = "fosite"

Functions

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 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 {
	GrantType        string
	HandledGrantType []string
	RequestedAt      time.Time
	Client           client.Client
	Scopes           Arguments
	GrantedScopes    []string
}

func NewAccessRequest

func NewAccessRequest() *AccessRequest

func (*AccessRequest) DidHandleGrantType

func (a *AccessRequest) DidHandleGrantType() bool

func (*AccessRequest) GetClient

func (a *AccessRequest) GetClient() client.Client

func (*AccessRequest) GetGrantType

func (a *AccessRequest) GetGrantType() string

func (*AccessRequest) GetGrantedScopes

func (a *AccessRequest) GetGrantedScopes() Arguments

func (*AccessRequest) GetRequestedAt

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

func (*AccessRequest) GetScopes

func (a *AccessRequest) GetScopes() Arguments

func (*AccessRequest) GrantScope

func (a *AccessRequest) GrantScope(scope string)

func (*AccessRequest) SetGrantTypeHandled

func (a *AccessRequest) SetGrantTypeHandled(name string)

func (*AccessRequest) SetScopes

func (a *AccessRequest) SetScopes(s Arguments)

type AccessRequester

type AccessRequester interface {
	// GetGrantType returns the requests grant type.
	GetGrantType() string

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

	// GetRequestedAt returns the time the request was created.
	GetRequestedAt() time.Time

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

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

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

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

	// SetGrantTypeHandled marks a grant type as handled indicating that the response type is supported.
	SetGrantTypeHandled(string)

	// DidHandleGrantType returns if the requested grant type has been handled correctly.
	DidHandleGrantType() bool
}

type AccessResponder

type AccessResponder interface {
	SetExtra(key string, value interface{})

	GetExtra(key string) interface{}

	SetAccessToken(string)

	SetTokenType(string)

	GetAccessToken() string

	GetTokenType() string

	ToMap() map[string]interface{}
}

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) SetExtra

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

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) Has

func (r Arguments) Has(item 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, session interface{}) error
}

type AuthorizeEndpointHandlers

type AuthorizeEndpointHandlers map[string]AuthorizeEndpointHandler

AuthorizeEndpointHandlers is a list of AuthorizeEndpointHandler

func (AuthorizeEndpointHandlers) Add

Add adds an AuthorizeEndpointHandler to this list

type AuthorizeRequest

type AuthorizeRequest struct {
	ResponseTypes        Arguments
	Client               Client
	Scopes               Arguments
	RedirectURI          *url.URL
	State                string
	RequestedAt          time.Time
	HandledResponseTypes Arguments
	GrantedScopes        []string
}

AuthorizeRequest is an implementation of AuthorizeRequester

func NewAuthorizeRequest

func NewAuthorizeRequest() *AuthorizeRequest

func (*AuthorizeRequest) DidHandleAllResponseTypes

func (d *AuthorizeRequest) DidHandleAllResponseTypes() bool

func (*AuthorizeRequest) GetClient

func (d *AuthorizeRequest) GetClient() Client

func (*AuthorizeRequest) GetGrantedScopes

func (a *AuthorizeRequest) GetGrantedScopes() Arguments

func (*AuthorizeRequest) GetRedirectURI

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

func (*AuthorizeRequest) GetRequestedAt

func (d *AuthorizeRequest) GetRequestedAt() time.Time

func (*AuthorizeRequest) GetResponseTypes

func (d *AuthorizeRequest) GetResponseTypes() Arguments

func (*AuthorizeRequest) GetScopes

func (d *AuthorizeRequest) GetScopes() Arguments

func (*AuthorizeRequest) GetState

func (d *AuthorizeRequest) GetState() string

func (*AuthorizeRequest) GrantScope

func (a *AuthorizeRequest) GrantScope(scope string)

func (*AuthorizeRequest) IsRedirectURIValid

func (d *AuthorizeRequest) IsRedirectURIValid() bool

func (*AuthorizeRequest) SetResponseTypeHandled

func (d *AuthorizeRequest) SetResponseTypeHandled(name string)

func (*AuthorizeRequest) SetScopes

func (a *AuthorizeRequest) SetScopes(s Arguments)

type AuthorizeRequester

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

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

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

	// GetClient returns this request's client or nil
	GetClient() Client

	// GetScopes returns this request's scopes
	GetScopes() Arguments

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

	// GetRequestedAt returns the time the request was issued
	GetRequestedAt() time.Time

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

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

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

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

AuthorizeRequester represents an authorize request

type AuthorizeResponder

type AuthorizeResponder interface {
	GetHeader() http.Header
	AddHeader(key, value string)

	GetQuery() url.Values
	AddQuery(key, value string)

	GetFragment() url.Values
	AddFragment(key, value string)
}

AuthorizeResponder defines fosite's response model

type AuthorizeResponse

type AuthorizeResponse struct {
	Header   *http.Header
	Query    *url.Values
	Fragment *url.Values
}

AuthorizeResponse is an implementation of AuthorizeResponder

func NewAuthorizeResponse

func NewAuthorizeResponse() *AuthorizeResponse

NewAuthorizeResponse creates a new 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) 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 Fosite

type Fosite struct {
	RequiredScope             string
	Store                     Storage
	AuthorizeEndpointHandlers AuthorizeEndpointHandlers
	TokenEndpointHandlers     TokenEndpointHandlers
	Hasher                    hash.Hasher

	// AllowHybridFlow sets if the hybrid flow should be allowed.
	// More: http://openid.net/specs/openid-connect-core-1_0.html#HybridFlowAuth
	AllowHybridFlow bool
}

Fosite implements OAuth2Provider.

func NewFosite

func NewFosite(store Storage) *Fosite

NewFosite returns a new OAuth2Provider implementation

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, session interface{}) (AccessResponder, error)

func (*Fosite) NewAuthorizeRequest

func (c *Fosite) NewAuthorizeRequest(_ 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) WriteAccessError

func (c *Fosite) WriteAccessError(rw http.ResponseWriter, requester 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(_ context.Context, req *http.Request, requester AccessRequester, session interface{}) (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)
}

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:"-"`
}

func ErrorToRFC6749Error

func ErrorToRFC6749Error(err error) *RFC6749Error

type Storage

type Storage 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)
}

Storage defines fosite's minimal storage interface.

type TokenEndpointHandler

type TokenEndpointHandler interface {
	// HandleAuthorizeRequest handles an authorize 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.
	//
	HandleTokenEndpointRequest(ctx context.Context, req *http.Request, requester AccessRequester, responder AccessResponder, session interface{}) error

	// ValidateTokenEndpointRequest
	// 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.
	ValidateTokenEndpointRequest(ctx context.Context, req *http.Request, requester AccessRequester, session interface{}) error
}

type TokenEndpointHandlers

type TokenEndpointHandlers map[string]TokenEndpointHandler

TokenEndpointHandlers is a list of TokenEndpointHandler

func (TokenEndpointHandlers) Add

Add adds an TokenEndpointHandler to this list

Jump to

Keyboard shortcuts

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