osin

package module
v0.0.0-...-b13af21 Latest Latest
Warning

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

Go to latest
Published: Mar 26, 2024 License: BSD-3-Clause Imports: 14 Imported by: 0

README

OSIN

GoDoc

Golang OAuth2 server library

OSIN is an OAuth2 server library for the Go language, as specified at http://tools.ietf.org/html/rfc6749 and http://tools.ietf.org/html/draft-ietf-oauth-v2-10.

It also includes support for PKCE, as specified at https://tools.ietf.org/html/rfc7636, which increases security for code-exchange flows for public OAuth clients.

Using it, you can build your own OAuth2 authentication service.

The library implements the majority of the specification, like authorization and token endpoints, and authorization code, implicit, resource owner and client credentials grant types.

Example Server
import (
	"github.com/RangelReale/osin"
	ex "github.com/RangelReale/osin/example" 
)

// ex.NewTestStorage implements the "osin.Storage" interface
server := osin.NewServer(osin.NewServerConfig(), ex.NewTestStorage())

// Authorization code endpoint
http.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
	resp := server.NewResponse()
	defer resp.Close()

	if ar := server.HandleAuthorizeRequest(resp, r); ar != nil {

		// HANDLE LOGIN PAGE HERE

		ar.Authorized = true
		server.FinishAuthorizeRequest(resp, r, ar)
	}
	osin.OutputJSON(resp, w, r)
})

// Access token endpoint
http.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
	resp := server.NewResponse()
	defer resp.Close()

	if ar := server.HandleAccessRequest(resp, r); ar != nil {
		ar.Authorized = true
		server.FinishAccessRequest(resp, r, ar)
	}
	osin.OutputJSON(resp, w, r)
})

http.ListenAndServe(":14000", nil)
Example Access

Open in your web browser:

http://localhost:14000/authorize?response_type=code&client_id=1234&redirect_uri=http%3A%2F%2Flocalhost%3A14000%2Fappauth%2Fcode
Storage backends

There is a mock available at example/teststorage.go which you can use as a guide for writing your own.

You might want to check out other implementations for common database management systems as well:

License

The code is licensed using "New BSD" license.

Author

Rangel Reale rangelreale@gmail.com

Changes

2014-06-25

  • BREAKING CHANGES:
    • Storage interface has 2 new methods, Clone and Close, to better support storages that need to clone / close in each connection (mgo)

    • Client was changed to be an interface instead of an struct. Because of that, the Storage interface also had to change, as interface is already a pointer.

    • HOW TO FIX YOUR CODE:

      • In your Storage, add a Clone function returning itself, and a do nothing Close.

      • In your Storage, replace all *osin.Client with osin.Client (remove the pointer reference)

      • If you used the osin.Client struct directly in your code, change it to osin.DefaultClient, which is a struct with the same fields that implements the interface.

      • Change all accesses using osin.Client to use the methods instead of the fields directly.

      • You MUST defer Response.Close in all your http handlers, otherwise some Storages may not clean correctly.

          resp := server.NewResponse()
          defer resp.Close()
        

Documentation

Index

Constants

View Source
const (
	CODE  AuthorizeRequestType = "code"
	TOKEN AuthorizeRequestType = "token"

	PKCE_PLAIN = "plain"
	PKCE_S256  = "S256"
)
View Source
const (
	E_INVALID_REQUEST           string = "invalid_request"
	E_UNAUTHORIZED_CLIENT              = "unauthorized_client"
	E_ACCESS_DENIED                    = "access_denied"
	E_UNSUPPORTED_RESPONSE_TYPE        = "unsupported_response_type"
	E_INVALID_SCOPE                    = "invalid_scope"
	E_SERVER_ERROR                     = "server_error"
	E_TEMPORARILY_UNAVAILABLE          = "temporarily_unavailable"
	E_UNSUPPORTED_GRANT_TYPE           = "unsupported_grant_type"
	E_INVALID_GRANT                    = "invalid_grant"
	E_INVALID_CLIENT                   = "invalid_client"
	E_RATE_LIMIT_EXCEEDED              = "rate_limit_exceeded"
)

Variables

View Source
var (
	// ErrNotFound is the error returned by Storage Get<...> and Load<...> functions in case
	// no entity is found in the storage. E.g. Storage.GetClient() returns ErrNotFound when
	// client is not found. All other returned errors must be treated as storage-specific errors,
	// like "connection lost", "connection refused", etc.
	ErrNotFound = errors.New("Entity not found")
)

Functions

func AddTokenInCookie

func AddTokenInCookie(response *Response, token string, tokenType string, tokenExpiration int64, cookieDomain string)

AddTokenInCookie adds token cookie in the response header

func CheckClientID

func CheckClientID(client Client, id string) bool

CheckClientID determines whether the given id matches a client ID.

func CheckClientSecret

func CheckClientSecret(client Client, secret string) bool

CheckClientSecret determines whether the given secret matches a secret held by the client. Public clients return true for a secret of ""

func FirstUri

func FirstUri(baseUriList string, separator string) string

FirstUri Returns the first uri from an uri list

func OutputJSON

func OutputJSON(rs *Response, w http.ResponseWriter, r *http.Request) error

OutputJSON encodes the Response to JSON and writes to the http.ResponseWriter

func ValidateUri

func ValidateUri(baseUri string, redirectUri string) error

ValidateUri validates that redirectUri is contained in baseUri

func ValidateUriList

func ValidateUriList(baseUriList string, redirectUri string, separator string) error

ValidateUriList validates that redirectUri is contained in baseUriList. baseUriList may be a string separated by separator. If separator is blank, validate only 1 URI.

Types

type AccessData

type AccessData struct {
	// Client information
	Client Client

	// Authorize data, for authorization code
	AuthorizeData *AuthorizeData

	// Previous access data, for refresh token
	AccessData *AccessData

	// Access token
	AccessToken string

	// Refresh Token. Can be blank
	RefreshToken string

	// ID Token. Can be blank
	IDToken string

	// Token expiration in seconds
	ExpiresIn int32

	// Refresh Token expiration in seconds
	RefreshExpireIn int32

	// Requested scope
	Scope string

	// Redirect Uri from request
	RedirectUri string

	// Date created
	CreatedAt time.Time

	// Data to be passed to storage. Not used by the library.
	UserData interface{}
}

AccessData represents an access grant (tokens, expiration, client, etc)

func (*AccessData) ExpireAt

func (d *AccessData) ExpireAt() time.Time

ExpireAt returns the expiration date

func (*AccessData) IsExpired

func (d *AccessData) IsExpired() bool

IsExpired returns true if access expired

func (*AccessData) IsExpiredAt

func (d *AccessData) IsExpiredAt(t time.Time) bool

IsExpiredAt returns true if access expires at time 't'

type AccessRequest

type AccessRequest struct {
	Type          AccessRequestType
	Code          string
	Client        Client
	AuthorizeData *AuthorizeData
	AccessData    *AccessData

	// Force finish to use this access data, to allow access data reuse
	ForceAccessData *AccessData
	RedirectUri     string
	Scope           string
	Username        string
	Password        string
	AssertionType   string
	Assertion       string

	// Set if request is authorized
	Authorized bool

	// Token expiration in seconds. Change if different from default
	Expiration int32

	// Refresh Token expiration in seconds. Change if different from default
	RefreshExpiration int32

	// Set if a refresh token should be generated
	GenerateRefresh bool

	// Data to be passed to storage. Not used by the library.
	UserData interface{}

	// HttpRequest *http.Request for special use
	HttpRequest *http.Request

	// Optional code_verifier as described in rfc7636
	CodeVerifier string

	// Skip set access_token and refresh_token cookies
	SkipSetCookie bool
}

AccessRequest is a request for access tokens

type AccessRequestType

type AccessRequestType string

AccessRequestType is the type for OAuth param `grant_type`

const (
	AUTHORIZATION_CODE        AccessRequestType = "authorization_code"
	REFRESH_TOKEN             AccessRequestType = "refresh_token"
	PASSWORD                  AccessRequestType = "password"
	CLIENT_CREDENTIALS        AccessRequestType = "client_credentials"
	ASSERTION                 AccessRequestType = "assertion"
	ANONYMOUS                 AccessRequestType = "anonymous"
	DEVICE                    AccessRequestType = "device"
	PLATFORM                  AccessRequestType = "platform"
	IMPLICIT                  AccessRequestType = "__implicit"
	EXTEND_LOGIN_QUEUE_TICKET AccessRequestType = "urn:ietf:params:oauth:grant-type:login_queue_ticket"
)

type AccessTokenGen

type AccessTokenGen interface {
	GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error)
	GenerateToken(data *AccessData, generaterefresh bool) (accesstoken, refreshtoken, idToken string, err error)
}

AccessTokenGen generates access tokens

type AccessTokenGenDefault

type AccessTokenGenDefault struct {
}

AccessTokenGenDefault is the default authorization token generator

func (*AccessTokenGenDefault) GenerateAccessToken

func (a *AccessTokenGenDefault) GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error)

GenerateAccessToken generates base64-encoded UUID access and refresh tokens

func (*AccessTokenGenDefault) GenerateToken

func (a *AccessTokenGenDefault) GenerateToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, idToken string, err error)

GenerateToken generates base64-encoded UUID access and refresh tokens & empty idToken

type AllowedAccessType

type AllowedAccessType []AccessRequestType

AllowedAccessType is a collection of allowed access request types

func (AllowedAccessType) Exists

Exists returns true if the access type exists in the list

type AllowedAuthorizeType

type AllowedAuthorizeType []AuthorizeRequestType

AllowedAuthorizeType is a collection of allowed auth request types

func (AllowedAuthorizeType) Exists

Exists returns true if the auth type exists in the list

type AuthorizeData

type AuthorizeData struct {
	// Client information
	Client Client

	// Authorization code
	Code string

	// Token expiration in seconds
	ExpiresIn int32

	// Requested scope
	Scope string

	// Redirect Uri from request
	RedirectUri string

	// State data from request
	State string

	// Date created
	CreatedAt time.Time

	// Data to be passed to storage. Not used by the library.
	UserData interface{}

	// Optional code_challenge as described in rfc7636
	CodeChallenge string
	// Optional code_challenge_method as described in rfc7636
	CodeChallengeMethod string
}

Authorization data

func (*AuthorizeData) ExpireAt

func (d *AuthorizeData) ExpireAt() time.Time

ExpireAt returns the expiration date

func (*AuthorizeData) IsExpired

func (d *AuthorizeData) IsExpired() bool

IsExpired is true if authorization expired

func (*AuthorizeData) IsExpiredAt

func (d *AuthorizeData) IsExpiredAt(t time.Time) bool

IsExpiredAt is true if authorization expires at time 't'

type AuthorizeRequest

type AuthorizeRequest struct {
	Type        AuthorizeRequestType
	Client      Client
	Scope       string
	RedirectUri string
	State       string

	// Set if request is authorized
	Authorized bool

	// Token expiration in seconds. Change if different from default.
	// If type = TOKEN, this expiration will be for the ACCESS token.
	Expiration int32

	// Data to be passed to storage. Not used by the library.
	UserData interface{}

	// HttpRequest *http.Request for special use
	HttpRequest *http.Request

	// Optional code_challenge as described in rfc7636
	CodeChallenge string
	// Optional code_challenge_method as described in rfc7636
	CodeChallengeMethod string
}

Authorize request information

type AuthorizeRequestType

type AuthorizeRequestType string

AuthorizeRequestType is the type for OAuth param `response_type`

type AuthorizeTokenGen

type AuthorizeTokenGen interface {
	GenerateAuthorizeToken(data *AuthorizeData) (string, error)
}

AuthorizeTokenGen is the token generator interface

type AuthorizeTokenGenDefault

type AuthorizeTokenGenDefault struct {
}

AuthorizeTokenGenDefault is the default authorization token generator

func (*AuthorizeTokenGenDefault) GenerateAuthorizeToken

func (a *AuthorizeTokenGenDefault) GenerateAuthorizeToken(data *AuthorizeData) (ret string, err error)

GenerateAuthorizeToken generates a base64-encoded UUID code

type BasicAuth

type BasicAuth struct {
	Username string
	Password string
}

Parse basic authentication header

func CheckBasicAuth

func CheckBasicAuth(r *http.Request) (*BasicAuth, error)

Return authorization header data

func GetClientAuth

func GetClientAuth(w *Response, r *http.Request, allowQueryParams bool) *BasicAuth

GetClientAuth checks client basic authentication in params if allowed, otherwise gets it from the header. Sets an error on the response if no auth is present or a server error occurs.

type BearerAuth

type BearerAuth struct {
	Code string
}

Parse bearer authentication header

func CheckBearerAuth

func CheckBearerAuth(r *http.Request) *BearerAuth

Return "Bearer" token from request. The header has precedence over query string.

type Client

type Client interface {
	// Client id
	GetID() string

	// Client secret
	GetSecret() string

	// Base client uri
	GetRedirectURI() string

	// Data to be passed to storage. Not used by the library.
	GetUserData() interface{}
}

Client information

type ClientIDMatcher

type ClientIDMatcher interface {
	// ClientIDMatches returns true if the given ID matches
	ClientIDMatches(id string) bool
}

type ClientSecretMatcher

type ClientSecretMatcher interface {
	// SecretMatches returns true if the given secret matches
	ClientSecretMatches(secret string) bool
}

ClientSecretMatcher is an optional interface clients can implement which allows them to be the one to determine if a secret matches. If a Client implements ClientSecretMatcher, the framework will never call GetSecret

type ComboClient

type ComboClient struct {
	Audience jwt.Audience
	Clients  []Client
}

ComboClient implements osin.Client interface This type of client is intended to handle multiple audience in the token

func (*ComboClient) ClientIDMatches

func (client *ComboClient) ClientIDMatches(id string) bool

ClientIDMatches satisfies the ClientIDMatcher interface

func (*ComboClient) ClientSecretMatches

func (client *ComboClient) ClientSecretMatches(secret string) bool

ClientSecretMatches satisfies the ClientSecretMatcher interface

func (*ComboClient) GetID

func (client *ComboClient) GetID() string

GetID satisfies osin.Client interface.

func (*ComboClient) GetRedirectURI

func (client *ComboClient) GetRedirectURI() string

GetRedirectURI satisfies osin.Client interface

func (*ComboClient) GetSecret

func (client *ComboClient) GetSecret() string

GetSecret satisfies osin.Client interface

func (*ComboClient) GetUserData

func (client *ComboClient) GetUserData() interface{}

GetUserData satisfies osin.Client interface

type DefaultClient

type DefaultClient struct {
	Id          string
	Secret      string
	RedirectUri string
	UserData    interface{}
}

DefaultClient stores all data in struct variables

func (*DefaultClient) ClientSecretMatches

func (d *DefaultClient) ClientSecretMatches(secret string) bool

ClientSecretMatches implement the ClientSecretMatcher interface

func (*DefaultClient) CopyFrom

func (d *DefaultClient) CopyFrom(client Client)

func (*DefaultClient) GetID

func (d *DefaultClient) GetID() string

func (*DefaultClient) GetRedirectURI

func (d *DefaultClient) GetRedirectURI() string

func (*DefaultClient) GetSecret

func (d *DefaultClient) GetSecret() string

func (*DefaultClient) GetUserData

func (d *DefaultClient) GetUserData() interface{}

type DefaultErrorId

type DefaultErrorId string

type DefaultErrors

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

Default errors and messages

func (*DefaultErrors) Get

func (e *DefaultErrors) Get(id string) string

type InfoRequest

type InfoRequest struct {
	Code       string      // Code to look up
	AccessData *AccessData // AccessData associated with Code
}

InfoRequest is a request for information about some AccessData

type JWTPayload

type JWTPayload struct {
	Expiration int64 `json:"exp"`
	IssueAt    int64 `json:"iat"`
}

JWTPayload represents JWT payload

type Response

type Response struct {
	Type               ResponseType
	StatusCode         int
	StatusText         string
	ErrorStatusCode    int
	URL                string
	Output             ResponseData
	Headers            http.Header
	IsError            bool
	ErrorId            string
	InternalError      error
	RedirectInFragment bool

	// Storage to use in this response - required
	Storage Storage
}

Server response

func NewResponse

func NewResponse(storage Storage) *Response

func (*Response) Close

func (r *Response) Close()

func (*Response) GetRedirectUrl

func (r *Response) GetRedirectUrl() (string, error)

GetRedirectUrl returns the redirect url with all query string parameters

func (*Response) SetError

func (r *Response) SetError(id string, description string)

SetError sets an error id and description on the Response state and uri are left blank

func (*Response) SetErrorState

func (r *Response) SetErrorState(id string, description string, state string)

SetErrorState sets an error id, description, and state on the Response uri is left blank

func (*Response) SetErrorUri

func (r *Response) SetErrorUri(id string, description string, uri string, state string)

SetErrorUri sets an error id, description, state, and uri on the Response

func (*Response) SetRedirect

func (r *Response) SetRedirect(url string)

SetRedirect changes the response to redirect to the given url

func (*Response) SetRedirectFragment

func (r *Response) SetRedirectFragment(f bool)

SetRedirectFragment sets redirect values to be passed in fragment instead of as query parameters

type ResponseData

type ResponseData map[string]interface{}

Data for response output

type ResponseType

type ResponseType int

Response type enum

const (
	DATA ResponseType = iota
	REDIRECT
)

type Server

type Server struct {
	Config            *ServerConfig
	Storage           Storage
	AuthorizeTokenGen AuthorizeTokenGen
	AccessTokenGen    AccessTokenGen
	Now               func() time.Time
}

Server is an OAuth2 implementation

func NewServer

func NewServer(config *ServerConfig, storage Storage) *Server

NewServer creates a new server instance

func (*Server) FinishAccessRequest

func (s *Server) FinishAccessRequest(w *Response, r *http.Request, ar *AccessRequest)

func (*Server) FinishAuthorizeRequest

func (s *Server) FinishAuthorizeRequest(w *Response, r *http.Request, ar *AuthorizeRequest)

func (*Server) FinishInfoRequest

func (s *Server) FinishInfoRequest(w *Response, r *http.Request, ir *InfoRequest)

FinishInfoRequest finalizes the request handled by HandleInfoRequest

func (*Server) HandleAccessRequest

func (s *Server) HandleAccessRequest(w *Response, r *http.Request) *AccessRequest

HandleAccessRequest is the http.HandlerFunc for handling access token requests

func (*Server) HandleAuthorizeRequest

func (s *Server) HandleAuthorizeRequest(w *Response, r *http.Request) *AuthorizeRequest

HandleAuthorizeRequest is the main http.HandlerFunc for handling authorization requests

func (*Server) HandleInfoRequest

func (s *Server) HandleInfoRequest(w *Response, r *http.Request) *InfoRequest

HandleInfoRequest is an http.HandlerFunc for server information NOT an RFC specification.

func (*Server) NewResponse

func (s *Server) NewResponse() *Response

NewResponse creates a new response for the server

type ServerConfig

type ServerConfig struct {
	// Authorization token expiration in seconds (default 5 minutes)
	AuthorizationExpiration int32

	// Access token expiration in seconds (default 1 hour)
	AccessExpiration int32

	// Refresh token expiration in seconds (default 1 day)
	RefreshExpiration int32

	// Domain attribute of token cookie
	CookieDomain string

	// Token type to return
	TokenType string

	// List of allowed authorize types (only CODE by default)
	AllowedAuthorizeTypes AllowedAuthorizeType

	// List of allowed access types (only AUTHORIZATION_CODE by default)
	AllowedAccessTypes AllowedAccessType

	// HTTP status code to return for errors - default 200
	// Only used if response was created from server
	ErrorStatusCode int

	// If true allows client secret also in params, else only in
	// Authorization header - default false
	AllowClientSecretInParams bool

	// If true allows access request using GET, else only POST - default false
	AllowGetAccessRequest bool

	// Require PKCE for code flows for public OAuth clients - default false
	RequirePKCEForPublicClients bool

	// Separator to support multiple URIs in Client.GetRedirectURI().
	// If blank (the default), don't allow multiple URIs.
	RedirectUriSeparator string

	// RetainTokenAfter Refresh allows the server to retain the access and
	// refresh token for re-use - default false
	RetainTokenAfterRefresh bool
}

ServerConfig contains server configuration information

func NewServerConfig

func NewServerConfig() *ServerConfig

NewServerConfig returns a new ServerConfig with default configuration

type Storage

type Storage interface {
	// Clone the storage if needed. For example, using mgo, you can clone the session with session.Clone
	// to avoid concurrent access problems.
	// This is to avoid cloning the connection at each method access.
	// Can return itself if not a problem.
	Clone() Storage

	// Close the resources the Storage potentially holds (using Clone for example)
	Close()

	// GetClient loads the client by id (client_id)
	GetClient(id string) (Client, error)

	// SaveAuthorize saves authorize data.
	SaveAuthorize(*AuthorizeData) error

	// LoadAuthorize looks up AuthorizeData by a code.
	// Client information MUST be loaded together.
	// Optionally can return error if expired.
	LoadAuthorize(code string) (*AuthorizeData, error)

	// RemoveAuthorize revokes or deletes the authorization code.
	RemoveAuthorize(code string) error

	// SaveAccess writes AccessData.
	// If RefreshToken is not blank, it must save in a way that can be loaded using LoadRefresh.
	SaveAccess(*AccessData) error

	// LoadAccess retrieves access data by token. Client information MUST be loaded together.
	// AuthorizeData and AccessData DON'T NEED to be loaded if not easily available.
	// Optionally can return error if expired.
	LoadAccess(token string) (*AccessData, error)

	// RemoveAccess revokes or deletes an AccessData.
	RemoveAccess(token string) error

	// LoadRefresh retrieves refresh AccessData. Client information MUST be loaded together.
	// AuthorizeData and AccessData DON'T NEED to be loaded if not easily available.
	// Optionally can return error if expired.
	LoadRefresh(token string) (*AccessData, error)

	// RemoveRefresh revokes or deletes refresh AccessData.
	RemoveRefresh(token string) error
}

Storage interface

type UriValidationError

type UriValidationError string

error returned when validation don't match

func (UriValidationError) Error

func (e UriValidationError) Error() string

Directories

Path Synopsis
openidconnect
An example of adding OpenID Connect support to osin.
An example of adding OpenID Connect support to osin.

Jump to

Keyboard shortcuts

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