ohauth

package module
v0.0.0-...-2180e78 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2015 License: MIT Imports: 19 Imported by: 0

README

OhAuth

GoDoc

OAuth 2 provider library for Go as defined in RFC 6749.

This library intends to define a stricter type of OAuth providers that follow recommendations from various researchers (see references)

Example Usage

The following example shows how to setup a provider with some test clients and install the handlers into a gin app. The implementation of the Authenticator (created by NewDefaultAuthenticator) is left as an exercise for the reader.

package main

import (
    "fmt"
    "net/http"

    "github.com/disintegrator/ohauth"
    "github.com/gin-gonic/gin"
)

func main() {
    authz := ohauth.MustParseURL("https://authz.saas.dev:3000/oauth")
    authn := ohauth.MustParseURL("https://authn.saas.dev:3000/")
    s, err := ohauth.NewTestingStore()
    if err != nil {
        panic(err)
    }

    ac := createClient(s, ohauth.AuthorizationCode)
    ic := createClient(s, ohauth.Implicit)
    pc := createClient(s, ohauth.Password)
    cc := createClient(s, ohauth.ClientCredentials)

    a, err := NewDefaultAuthenticator(authn)
    if err != nil {
        panic(err)
    }
    p := ohauth.NewProvider(authz, a, s)

    e := gin.Default()
    e.Group("/oauth").Any("*action", gin.WrapH(p.Handler()))
    e.GET("/_health", func(c *gin.Context) {
        c.String(http.StatusOK, "ok")
    })

    fmt.Printf("Authorization code client registered with id: %s - %s\n", ac.ID, ac.Secret)
    fmt.Printf("Implicit client registered with id: %s - %s\n", ic.ID, ic.Secret)
    fmt.Printf("Password client registered with id: %s - %s\n", pc.ID, pc.Secret)
    fmt.Printf("Client credentials client registered with id: %s - %s\n", cc.ID, cc.Secret)
    e.Run(":3000")
}

References

TODO

  • A ton of tests

Contributions are more than welcome!

Documentation

Overview

Package ohauth allows the creation of OAuth 2.0 providers.

The package defines several interfaces that must be implemented and used to configure a Provider. The provider can then be incorporated into a web service that provides OAuth 2.0 authorisation functionality.

As a starting point, it is best to look at the Provider documentation and proceeding from there to discover the rest of this library.

Index

Constants

View Source
const (
	ClientActive  = "active"
	ClientRevoked = "revoked"
)

possible values for client status

View Source
const (
	AccessDenied            = "access_denied"
	InvalidClient           = "invalid_client"
	InvalidGrant            = "invalid_grant"
	InvalidRequest          = "invalid_request"
	InvalidScope            = "invalid_scope"
	ServerError             = "server_error"
	TemporarilyUnavailable  = "temporarily_unavailable"
	UnauthorizedClient      = "unauthorized_client"
	UnsupportedGrantType    = "unsupported_grant_type"
	UnsupportedResponseType = "unsupported_response_type"
)

Error codes as specified throughout rfc6749

View Source
const (
	AuthorizationCode = "authorization_code"
	Implicit          = "implicit"
	Password          = "password"
	ClientCredentials = "client_credentials"
	RefreshToken      = "refresh_token"
)

Grant type defined in rfc6749

View Source
const (
	RoleIdentity     = "identity"
	RoleCode         = "code"
	RoleAccessToken  = "access_token"
	RoleRefreshToken = "refresh_token"
)

Role identifies the role of a JWT token

Variables

View Source
var (
	ErrClientNotFound        = NewError(InvalidClient, "client not found")
	ErrScopeNotAllowed       = NewError(InvalidScope, "client cannot offer requested scope")
	ErrWrongGrant            = NewError(InvalidRequest, "client cannot use specified grant type")
	ErrInvalidGrant          = NewError(InvalidRequest, "invalid grant type")
	ErrUnexpected            = NewError(ServerError, "unexpected error occured")
	ErrUnsupportResponseType = NewError(UnsupportedResponseType, "unsupported response type")
	ErrBadRedirect           = NewError(InvalidRequest, "invalid redirect uri")
	ErrAccessDenied          = NewError(AccessDenied, "access denied")
	ErrUnauthorized          = NewError(UnauthorizedClient, "unauthorized client")
	ErrCodeUsed              = NewError(InvalidRequest, "authorization code has already been used")
)

Common errors that can occur while processing authorization and token requests

View Source
var ErrNotAbsoluteURL = errors.New("absolute urls with host are required")

ErrNotAbsoluteURL is returned when a parsed URL is not absolute i.e. does not have scheme or host

Functions

This section is empty.

Types

type Authenticator

type Authenticator interface {
	Verify(sig string, client *Client) (*TokenClaims, error)
	AuthenticateCredentials(username, password string, client *Client) (*TokenClaims, error)
	AuthenticateRequest(r *http.Request, client *Client) (*TokenClaims, error)
}

Authenticator is responsible for determining how to authenticate users

type Authorization

type Authorization struct {
	CID     string    `json:"cid"`
	UID     string    `json:"uid"`
	Scope   Scope     `json:"scope"`
	Active  bool      `json:"active"`
	Created time.Time `json:"created"`
}

Authorization is used to record a resource owner's approval of a client's authorization request when using the Authorization Code and Implicit grant types

func NewAuthorization

func NewAuthorization(cid, uid string, scope Scope) *Authorization

NewAuthorization initialises an authorization with a specified client id, resource owner id and scope that may be saved to a store.

type Client

type Client struct {
	ID          string `json:"id"`
	DisplayName string `json:"displayName"`
	Secret      string `json:"secret"`

	// GrantType defines the allowed flow the client may use
	GrantType   string     `json:"grantType"`
	RedirectURI *StrictURL `json:"redirectURI"`
	Scope       Scope      `json:"scope"`
	Status      string     `json:"status"`
	Created     time.Time  `json:"created"`

	// Keys are used with a Tokenizer to sign and verify codes and tokens
	Keys *ClientKeys `json:"keys"`
}

Client defines an OAuth 2.0 client

func NewClient

func NewClient(displayName string, grantType string) *Client

NewClient creates a default client with randomly generated id, secret and keys. The default client's scope is empty initially as well.

type ClientKeys

type ClientKeys struct {
	Sign   []byte `json:"sign"`
	Verify []byte `json:"verify"`
}

ClientKeys are used in conjuction with Tokenizers to sign and verify codes and tokens

func NewClientKeys

func NewClientKeys() *ClientKeys

NewClientKeys creates random pair of private/public keys using RSA 2048

type Error

type Error struct {
	Code        string `json:"error"`
	Description string `json:"error_description,omitempty"`
}

Error defines an OAuth error with fields specified in rfc6749

func NewError

func NewError(ec, desc string) *Error

NewError creates a populated error instance

func (*Error) Error

func (e *Error) Error() string

func (*Error) Values

func (e *Error) Values() url.Values

Values returns a representation of an Error that can be used as query parameters in a redirect uri

type Issuer

type Issuer interface {
	// ExpiryForToken returns the expiry duration for token issued under a
	// specified grant type
	ExpiryForToken(grantType string) time.Duration
	// ExpiryForCode returns the expiry duration for codes issued with the
	// Authorization Code grant type
	ExpiryForCode() time.Duration
	// ScopePermitted determines if a scope can be issued under a certain grant
	// type
	ScopePermitted(scope Scope, grantType string) bool
}

Issuer defines parameters for tokens and scopes

type Provider

type Provider struct {
	// Authorization and Authentication endpoints
	URL *StrictURL
	// Authenticator is used for to parse sessions and authenticate via password grants
	Authenticator Authenticator
	// Data store for clients and tokens
	Store Store
	// Tokenizer is required to generate code, id, access and refresh tokens
	Tokenizer Tokenizer
	// Issuer is used to determine claim values when issuing tokens
	Issuer Issuer
}

Provider configures an OAuth 2.0 provider that can authorize clients by issuing signed access tokens

func NewProvider

func NewProvider(u *StrictURL, authn Authenticator, store Store) *Provider

NewProvider creates a provider configured with the default tokenizer and issuer.

func (*Provider) Handler

func (p *Provider) Handler() http.Handler

Handler returns an http.Handler that can be integrated into web applications

type Scope

type Scope map[string]bool

Scope is a set of actions defined on resources that clients may request from resource owners

func ParseScope

func ParseScope(raw string) Scope

ParseScope takes raw comma-separated string and parses into a scope object

func (Scope) Add

func (s Scope) Add(actions ...string)

Add adds a list of actions to a scope object

func (Scope) Contains

func (s Scope) Contains(s2 Scope) bool

Contains determines if a scope is a subset of another

func (Scope) Equals

func (s Scope) Equals(s2 Scope) bool

Equals determines if two scopes are the same by comparing the actions they define

func (Scope) MarshalJSON

func (s Scope) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface

func (Scope) String

func (s Scope) String() string

func (*Scope) UnmarshalJSON

func (s *Scope) UnmarshalJSON(inp []byte) error

UnmarshalJSON implements the json.Unmarshaler interface for converting JSON string of comma-separated scope actions and uses to populate a Scope object

func (Scope) Values

func (s Scope) Values() []string

Values returns a list of actions held by a scope object

type Store

type Store interface {
	// CreateClient stores a client
	CreateClient(*Client) error
	// FetchClient retrieves a client by its id
	FetchClient(cid string) (*Client, error)
	// DeleteClient deletes a client by its id
	DeleteClient(cid string) error

	// BlacklistToken invalidate codes and tokens using a token ID
	BlacklistToken(id string) error
	// TokenBlacklisted is used to check if a code or token is invalidated
	TokenBlacklisted(id string) (bool, error)

	// StoreAuthorization records a resource owner's authorisation of a client
	StoreAuthorization(a *Authorization) error
	// FetchAuthorization retrieves an Authorization record
	FetchAuthorization(cid string, sub string) (*Authorization, error)
}

Store defines an interface that is used to store/retrieve/manipulate objects used throughout the OAuth framework (typically a database).

type StrictURL

type StrictURL url.URL

StrictURL is similar to the standard net/url.URL type except that it can be json marshalled and unmarshalled and forces all parsed urls to https protocol

func MustParseURL

func MustParseURL(raw string) *StrictURL

MustParseURL is the same as ParseURL but panic on error instead

func ParseURL

func ParseURL(raw string) (*StrictURL, error)

ParseURL parses a string url and coerces the scheme to https, clears the querystring, sets fragment to '_=_' to create a StrictURL instance. The raw url must be absolute (host and scheme must be set)

func (*StrictURL) Clone

func (u *StrictURL) Clone() *StrictURL

Clone creates a new copy of the StrictURL

func (*StrictURL) Compare

func (u *StrictURL) Compare(u2 *StrictURL) bool

Compare determines if two StrictURL's are the same using simple string comparison. If either instance is nil the result is false.

func (*StrictURL) MarshalJSON

func (u *StrictURL) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface

func (*StrictURL) String

func (u *StrictURL) String() string

func (*StrictURL) StringWithFragment

func (u *StrictURL) StringWithFragment(v url.Values) string

StringWithFragment returns a string representation of a StrictURL with the specified fragment

func (*StrictURL) StringWithParams

func (u *StrictURL) StringWithParams(v url.Values) string

StringWithParams returns a string representation of a StrictURL with the specified query parameters

func (*StrictURL) UnmarshalJSON

func (u *StrictURL) UnmarshalJSON(b []byte) error

UnmarshalJSON implements the json.Unmarshaler that correctly parses a StrictURL

type TestingStore

type TestingStore struct {
	*sync.Mutex
	// contains filtered or unexported fields
}

TestingStore is a Store implementation that may be used for testing and experimenting with OhAuth. It is a simple memory-based store.

func NewTestingStore

func NewTestingStore() (*TestingStore, error)

NewTestingStore creates an instace of a TestingStore

func (*TestingStore) BlacklistToken

func (s *TestingStore) BlacklistToken(id string) error

BlacklistToken invalidate codes and tokens using a token ID

func (*TestingStore) CreateClient

func (s *TestingStore) CreateClient(c *Client) error

CreateClient stores a client

func (*TestingStore) DeleteClient

func (s *TestingStore) DeleteClient(cid string) error

DeleteClient deletes a client by its id

func (*TestingStore) FetchAuthorization

func (s *TestingStore) FetchAuthorization(cid, uid string) (*Authorization, error)

FetchAuthorization retrieves an Authorization record

func (*TestingStore) FetchClient

func (s *TestingStore) FetchClient(cid string) (*Client, error)

FetchClient retrieves a client by its id

func (*TestingStore) StoreAuthorization

func (s *TestingStore) StoreAuthorization(a *Authorization) error

StoreAuthorization records a resource owner's authorisation of a client

func (*TestingStore) TokenBlacklisted

func (s *TestingStore) TokenBlacklisted(id string) (bool, error)

TokenBlacklisted is used to check if a code or token is invalidated

type TokenClaims

type TokenClaims struct {
	ID       string `json:"jti"`
	Role     string `json:"role"`
	Audience string `json:"aud"`
	Expires  int64  `json:"exp"`
	Issued   int64  `json:"iat"`
	Issuer   string `json:"iss"`
	Subject  string `json:"sub"`
	Grant    string `json:"grant"`
	Scope    Scope  `json:"scope,omitempty"`
	Nonce    string `json:"nonce,omitempty"`
}

TokenClaims captures information about a token or code that is issued to clients

func NewTokenClaims

func NewTokenClaims(role string, iat time.Time, exp time.Time) *TokenClaims

NewTokenClaims creates an instance of TokenClaims initialised with some basic claims include an ID, role, issue date and expiry

type Tokenizer

type Tokenizer interface {
	// Tokenize converts TokenClaims into a signed string using a signing key
	Tokenize(tc *TokenClaims, signingKey []byte) (string, error)
	// Parse takes a signed token string, verifies its authenticity and returns
	// the TokenClaims it carries
	Parse(token string, verifyKey []byte) (*TokenClaims, error)
}

Tokenizer defines an interface that can create OAuth token strings (codes, access and refresh tokens) from TokenClaims and parse strings back into TokenClaims.

func NewJWTTokenizer

func NewJWTTokenizer(signingMethod jwt.SigningMethod) Tokenizer

NewJWTTokenizer creates a Tokenizer that creates and parses JWT tokens

Jump to

Keyboard shortcuts

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