README
¶
OSIN
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
- Variables
- func CheckClientSecret(client Client, secret string) bool
- func FirstUri(baseUriList string, separator string) string
- func OutputJSON(rs *Response, w http.ResponseWriter, r *http.Request) error
- func ParseUrls(baseUrl, redirectUrl string) (retBaseUrl, retRedirectUrl *url.URL, err error)
- func ValidateUri(baseUri string, redirectUri string) (realRedirectUri string, err error)
- func ValidateUriList(baseUriList string, redirectUri string, separator string) (realRedirectUri string, err error)
- type AccessData
- type AccessRequest
- type AccessRequestType
- type AccessTokenGen
- type AccessTokenGenDefault
- type AllowedAccessType
- type AllowedAuthorizeType
- type AuthorizeData
- type AuthorizeRequest
- type AuthorizeRequestType
- type AuthorizeTokenGen
- type AuthorizeTokenGenDefault
- type BasicAuth
- type BearerAuth
- type Client
- type ClientSecretMatcher
- type DefaultClient
- type DefaultErrorId
- type DefaultErrors
- type InfoRequest
- type Logger
- type LoggerDefault
- type Response
- func (r *Response) Close()
- func (r *Response) GetRedirectUrl() (string, error)
- func (r *Response) SetError(id string, description string)
- func (r *Response) SetErrorState(id string, description string, state string)
- func (r *Response) SetErrorUri(id string, description string, uri string, state string)
- func (r *Response) SetRedirect(url string)
- func (r *Response) SetRedirectFragment(f bool)
- type ResponseData
- type ResponseType
- type Server
- func (s *Server) FinishAccessRequest(w *Response, r *http.Request, ar *AccessRequest)
- func (s *Server) FinishAuthorizeRequest(w *Response, r *http.Request, ar *AuthorizeRequest)
- func (s *Server) FinishInfoRequest(w *Response, r *http.Request, ir *InfoRequest)
- func (s *Server) HandleAccessRequest(w *Response, r *http.Request) *AccessRequest
- func (s *Server) HandleAuthorizeRequest(w *Response, r *http.Request) *AuthorizeRequest
- func (s *Server) HandleInfoRequest(w *Response, r *http.Request) *InfoRequest
- func (s *Server) NewResponse() *Response
- type ServerConfig
- type Storage
- type UriValidationError
Constants ¶
const ( CODE AuthorizeRequestType = "code" TOKEN AuthorizeRequestType = "token" PKCE_PLAIN = "plain" PKCE_S256 = "S256" )
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" )
Variables ¶
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 CheckClientSecret ¶
CheckClientSecret determines whether the given secret matches a secret held by the client. Public clients return true for a secret of ""
func OutputJSON ¶
OutputJSON encodes the Response to JSON and writes to the http.ResponseWriter
func ValidateUri ¶
ValidateUri validates that redirectUri is contained in baseUri
func ValidateUriList ¶
func ValidateUriList(baseUriList string, redirectUri string, separator string) (realRedirectUri string, err 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 // Token expiration in seconds ExpiresIn 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 // 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 }
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" IMPLICIT AccessRequestType = "__implicit" )
type AccessTokenGen ¶
type AccessTokenGen interface {
GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken 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
type AllowedAccessType ¶
type AllowedAccessType []AccessRequestType
AllowedAccessType is a collection of allowed access request types
func (AllowedAccessType) Exists ¶
func (t AllowedAccessType) Exists(rt AccessRequestType) bool
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 ¶
func (t AllowedAuthorizeType) Exists(rt AuthorizeRequestType) bool
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
IsExpired 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 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 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 DefaultClient ¶
DefaultClient stores all data in struct variables
func (*DefaultClient) ClientSecretMatches ¶
func (d *DefaultClient) ClientSecretMatches(secret string) bool
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 NewDefaultErrors ¶
func NewDefaultErrors() *DefaultErrors
NewDefaultErrors initializes OAuth2 error codes and descriptions. http://tools.ietf.org/html/rfc6749#section-4.1.2.1 http://tools.ietf.org/html/rfc6749#section-4.2.2.1 http://tools.ietf.org/html/rfc6749#section-5.2 http://tools.ietf.org/html/rfc6749#section-7.2
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 Logger ¶ added in v1.0.1
type Logger interface {
Printf(format string, v ...interface{})
}
Logger creates a formatted log event. NOTE: Log is meant for internal use only and may contain sensitive info.
type LoggerDefault ¶ added in v1.0.1
type LoggerDefault struct { }
func (LoggerDefault) Printf ¶ added in v1.0.1
func (l LoggerDefault) Printf(format string, v ...interface{})
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 (*Response) GetRedirectUrl ¶
GetRedirectUrl returns the redirect url with all query string parameters
func (*Response) SetError ¶
SetError sets an error id and description on the Response state and uri are left blank
func (*Response) SetErrorState ¶
SetErrorState sets an error id, description, and state on the Response uri is left blank
func (*Response) SetErrorUri ¶
SetErrorUri sets an error id, description, state, and uri on the Response
func (*Response) SetRedirect ¶
SetRedirect changes the response to redirect to the given url
func (*Response) SetRedirectFragment ¶
SetRedirectFragment sets redirect values to be passed in fragment instead of as query parameters
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 Logger Logger }
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 ¶
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 // 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
Source Files
¶
Directories
¶
Path | Synopsis |
---|---|
openidconnect
An example of adding OpenID Connect support to osin.
|
An example of adding OpenID Connect support to osin. |