oauth

package module
v0.0.0-...-974118f Latest Latest
Warning

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

Go to latest
Published: Jun 21, 2022 License: MIT Imports: 11 Imported by: 0

README

oauth middleware

OAuth 2.0 Authorization Server & Authorization Middleware for go-chi

This library was ported to go-chi from https://github.com/maxzerbini/oauth by jeffreydwalter.

This library offers an OAuth 2.0 Authorization Server based on go-chi and an Authorization Middleware usable in Resource Servers developed with go-chi.

Build status

Build Status

Authorization Server

The Authorization Server is implemented by the struct OAuthBearerServer that manages two grant types of authorizations (password and client_credentials). This Authorization Server is made to provide an authorization token usable for consuming resources API.

Password grant type

OAuthBearerServer supports the password grant type, allowing the token generation for username / password credentials.

Client Credentials grant type

OAuthBearerServer supports the client_credentials grant type, allowing the token generation for client_id / client_secret credentials.

Authorization Code and Implicit grant type

These grant types are currently partially supported implementing AuthorizationCodeVerifier interface. The method ValidateCode is called during the phase two of the authorization_code grant type evalutations.

Refresh token grant type

If authorization token will expire, the client can regenerate the token calling the authorization server and using the refresh_token grant type.

Authorization Middleware

The go-chi middleware BearerAuthentication intercepts the resource server calls and authorizes only resource requests containing a valid bearer token.

Token Formatter

Authorization Server crypts the token using the Token Formatter and Authorization Middleware decrypts the token using the same Token Formatter. This library contains a default implementation of the formatter interface called SHA256RC4TokenSecureFormatter based on the algorithms SHA256 and RC4. Programmers can develop their Token Formatter implementing the interface TokenSecureFormatter and this is really recommended before publishing the API in a production environment.

Credentials Verifier

The interface CredentialsVerifier defines the hooks called during the token generation process. The methods are called in this order:

  • ValidateUser() or ValidateClient() called first for credentials verification
  • AddClaims() used for add information to the token that will be encrypted
  • StoreTokenID() called after the token generation but before the response, programmers can use this method for storing the generated IDs
  • AddProperties() used for add clear information to the response

There is another method in the CredentialsVerifier interface that is involved during the refresh token process. In this case the methods are called in this order:

  • ValidateTokenID() called first for TokenID verification, the method receives the TokenID related to the token associated to the refresh token
  • AddClaims() used for add information to the token that will be encrypted
  • StoreTokenID() called after the token regeneration but before the response, programmers can use this method for storing the generated IDs
  • AddProperties() used for add clear information to the response

Authorization Server usage example

This snippet shows how to create an authorization server

func main() {
    r := chi.NewRouter()
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)

    s := oauth.NewOAuthBearerServer(
        "mySecretKey-10101",
	time.Second*120,
	&TestUserVerifier{},
	nil)
	
    r.Post("/token", s.UserCredentials)
    r.Post("/auth", s.ClientCredentials)
    http.ListenAndServe(":8080", r)
}

See /test/authserver/main.go for the full example.

Authorization Middleware usage example

This snippet shows how to use the middleware

    r.Route("/", func(r chi.Router) {
	// use the Bearer Authentication middleware
	r.Use(oauth.Authorize("mySecretKey-10101", nil))

	r.Get("/customers", GetCustomers)
	r.Get("/customers/{id}/orders", GetOrders)
    }

See /test/resourceserver/main.go for the full example.

Note that the authorization server and the authorization middleware are both using the same token formatter and the same secret key for encryption/decryption.

Reference

License

MIT

Documentation

Index

Constants

View Source
const (
	CredentialContext  contextKey = "oauth.credential"
	ClaimsContext      contextKey = "oauth.claims"
	ScopeContext       contextKey = "oauth.scope"
	TokenTypeContext   contextKey = "oauth.tokentype"
	AccessTokenContext contextKey = "oauth.accesstoken"
)

Variables

This section is empty.

Functions

func Authorize

func Authorize(secretKey string, formatter TokenSecureFormatter) func(next http.Handler) http.Handler

Authorize is the OAuth 2.0 middleware for go-chi resource server. Authorize creates a BearerAuthentication middleware and return the Authorize method.

func CheckBasicAuthentication

func CheckBasicAuthentication(username, password string, r *http.Request) error

CheckBasicAuthentication checks Basic Authorization header credentials

func GetBasicAuthentication

func GetBasicAuthentication(r *http.Request) (username, password string, err error)

GetBasicAuthentication get username and password from Authorization header

Types

type AuthorizationCodeVerifier

type AuthorizationCodeVerifier interface {
	// ValidateCode checks the authorization code and returns the user credential
	ValidateCode(clientID, clientSecret, code, redirectURI string, r *http.Request) (string, error)
}

AuthorizationCodeVerifier defines the interface of the Authorization Code verifier

type BearerAuthentication

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

BearerAuthentication middleware for go-chi

func NewBearerAuthentication

func NewBearerAuthentication(secretKey string, formatter TokenSecureFormatter) *BearerAuthentication

NewBearerAuthentication create a BearerAuthentication middleware

func (*BearerAuthentication) Authorize

func (ba *BearerAuthentication) Authorize(next http.Handler) http.Handler

Authorize verifies the bearer token authorizing or not the request. Token is retrieved from the Authorization HTTP header that respects the format Authorization: Bearer {access_token}

type BearerServer

type BearerServer struct {
	TokenTTL        time.Duration
	RefreshTokenTTL time.Duration
	// contains filtered or unexported fields
}

BearerServer is the OAuth 2 bearer server implementation.

func NewBearerServer

func NewBearerServer(secretKey string, ttl, refreshTTL time.Duration, verifier CredentialsVerifier, formatter TokenSecureFormatter) *BearerServer

NewBearerServer creates new OAuth 2 bearer server

func (*BearerServer) AuthorizationCode

func (bs *BearerServer) AuthorizationCode(w http.ResponseWriter, r *http.Request)

AuthorizationCode manages authorization code grant type requests for the phase two of the authorization process

func (*BearerServer) ClientCredentials

func (bs *BearerServer) ClientCredentials(w http.ResponseWriter, r *http.Request)

ClientCredentials manages client credentials grant type requests

func (*BearerServer) UserCredentials

func (bs *BearerServer) UserCredentials(w http.ResponseWriter, r *http.Request)

UserCredentials manages password grant type requests

type Claims

type Claims map[string]interface{}

type CredentialsVerifier

type CredentialsVerifier interface {
	// ValidateUser validates username and password returning an error if the user credentials are wrong
	ValidateUser(username, password, scope string, r *http.Request) error
	// ValidateClient validates clientID and secret returning an error if the client credentials are wrong
	ValidateClient(clientID, clientSecret, scope string, r *http.Request) error
	// AddClaims provides additional claims to the token
	AddClaims(tokenType TokenType, credential, tokenID, scope string, r *http.Request) (Claims, error)
	// AddProperties provides additional information to the authorization server response
	AddProperties(tokenType TokenType, credential, tokenID, scope string, r *http.Request) (Properties, error)
	// ValidateTokenID optionally validates previously stored tokenID during refresh request
	ValidateTokenID(tokenType TokenType, credential, tokenID, refreshTokenID string) error
	// StoreTokenID optionally stores the tokenID generated for the user
	StoreTokenID(tokenType TokenType, credential, tokenID, refreshTokenID string) error
}

CredentialsVerifier defines the interface of the user and client credentials verifier.

type ErrorResponse

type ErrorResponse struct {
	Error       ErrorResponseType `json:"error"`
	Description string            `json:"error_description"`
	URI         string            `json:"error_uri,omitempty"`
	State       string            `json:"state,omitempty"`
}

type ErrorResponseType

type ErrorResponseType string

ErrorResponseType ...

const (
	// AuthorizationCodeGrantInvalidRequest The request is missing a required parameter, includes an invalid parameter
	// value, includes a parameter more than once, or is otherwise malformed.
	AuthorizationCodeGrantInvalidRequest ErrorResponseType = "invalid_request"
	// AuthorizationCodeGrantUnauthorizedClient This client is not authorized to use the requested grant type.
	// For example, if you restrict which applications can use the Implicit grant, you would return this error for the
	// other apps.
	AuthorizationCodeGrantUnauthorizedClient ErrorResponseType = "unauthorized_client"
	// AuthorizationCodeGrantAccessDenied The resource owner or authorization server denied the request.
	AuthorizationCodeGrantAccessDenied ErrorResponseType = "access_denied"
	// AuthorizationCodeGrantUnsupportedResponseType The authorization server does not support obtaining an
	// authorization code using this method.
	AuthorizationCodeGrantUnsupportedResponseType ErrorResponseType = "unsupported_response_type"
	// AuthorizationCodeGrantInvalidScope The requested scope is invalid, unknown, or malformed.
	AuthorizationCodeGrantInvalidScope ErrorResponseType = "invalid_scope"
	// AuthorizationCodeGrantServerError The authorization server encountered an unexpected condition that prevented it
	// from fulfilling the request. (This error code is needed because a 500 Internal Server Error HTTP status code
	// cannot be returned to the client via an HTTP redirect.)
	AuthorizationCodeGrantServerError ErrorResponseType = "server_error"
	// AuthorizationCodeGrantTemporarilyUnavailable The authorization server is currently unable to handle the request
	// due to a temporary overloading or maintenance of the server. (This error code is needed because a 503 Service
	// Unavailable HTTP status code cannot be returned to the client via an HTTP redirect.)
	AuthorizationCodeGrantTemporarilyUnavailable ErrorResponseType = "temporarily_unavailable"

	// ImplicitGrantInvalidRequest The request is missing a required parameter, includes an invalid parameter value,
	// includes a parameter more than once, or is otherwise malformed.
	ImplicitGrantInvalidRequest ErrorResponseType = "invalid_request"
	// ImplicitGrantUnauthorizedClient The client is not authorized to request an access token using this method.
	ImplicitGrantUnauthorizedClient ErrorResponseType = "unauthorized_client"
	// ImplicitGrantAccessDenied The resource owner or authorization server denied the request.
	ImplicitGrantAccessDenied ErrorResponseType = "access_denied"
	// ImplicitGrantUnsupportedResponseType The authorization server does not support obtaining an
	// authorization code using this method.
	ImplicitGrantUnsupportedResponseType ErrorResponseType = "unsupported_response_type"
	// ImplicitGrantInvalidScope The requested scope is invalid, unknown, or malformed.
	ImplicitGrantInvalidScope ErrorResponseType = "invalid_scope"

	// TokenInvalidRequest The request is missing a required parameter, includes an
	// unsupported parameter value (other than grant type), repeats a parameter, includes multiple credentials,
	// utilizes more than one mechanism for authenticating the client, or is otherwise malformed.
	TokenInvalidRequest ErrorResponseType = "invalid_request"
	// TokenInvalidClient Client authentication failed (e.g., unknown client, no
	// client authentication included, or unsupported authentication method). The authorization server MAY
	// return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported.
	// If the client attempted to authenticate via the "Authorization" request header field, the authorization server
	// MUST respond with an HTTP 401 (Unauthorized) status code and include the "WWW-Authenticate" response header field
	// matching the authentication scheme used by the client.
	TokenInvalidClient ErrorResponseType = "invalid_client"
	// TokenInvalidGrant 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.
	TokenInvalidGrant ErrorResponseType = "invalid_grant"
	// TokenUnauthorizedClient The authenticated client is not authorized to use this authorization grant type.
	TokenUnauthorizedClient ErrorResponseType = "unauthorized_client"
	// TokenUnsupportedGrantType The authorization grant type is not supported by the
	// authorization server.
	TokenUnsupportedGrantType ErrorResponseType = "unsupported_grant_type"
	// TokenInvalidScope The requested scope is invalid, unknown, malformed, or
	// exceeds the scope granted by the resource owner.
	TokenInvalidScope ErrorResponseType = "invalid_scope"
	// TokenServerError The authorization server encountered an unexpected
	// condition that prevented it from fulfilling the request. (This error code is needed because a 500 Internal Server
	// Error HTTP status code cannot be returned to the client via an HTTP redirect.)
	TokenServerError ErrorResponseType = "server_error"
	// TokenTemporarilyUnavailable The authorization server is currently unable to handle
	// the request due to a temporary overloading or maintenance of the server.  (This error code is needed because a 503
	// Service Unavailable HTTP status code cannot be returned to the client via an HTTP redirect.)
	TokenTemporarilyUnavailable ErrorResponseType = "temporarily_unavailable"
)

See: https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1 See: https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2.1 See: https://datatracker.ietf.org/doc/html/rfc6749#section-5.2

type ExpirableToken

type ExpirableToken interface {
	IsExpired() bool
}

ExpirableToken is an interface for a token that has an expiration.

type GrantType

type GrantType string
const (
	PasswordGrant          GrantType = "password"
	ClientCredentialsGrant GrantType = "client_credentials"
	AuthCodeGrant          GrantType = "authorization_code"
	RefreshTokenGrant      GrantType = "refresh_token"
)

type Properties

type Properties map[string]string

type RC4TokenSecureFormatter

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

func NewRC4TokenSecurityProvider

func NewRC4TokenSecurityProvider(key []byte) *RC4TokenSecureFormatter

func (*RC4TokenSecureFormatter) CryptToken

func (sc *RC4TokenSecureFormatter) CryptToken(source []byte) ([]byte, error)

func (*RC4TokenSecureFormatter) DecryptToken

func (sc *RC4TokenSecureFormatter) DecryptToken(source []byte) ([]byte, error)

type RefreshToken

type RefreshToken struct {
	ID           string        `json:"refresh_token_id"`
	TokenID      string        `json:"token_id"`
	CreationDate time.Time     `json:"date"`
	ExpiresIn    time.Duration `json:"expires_in"` // secs
	Credential   string        `json:"credential"`
	TokenType    TokenType     `json:"type"`
	Scope        string        `json:"scope"`
	Claims       Claims        `json:"claims"`
}

RefreshToken structure included in the authorization server response

func (*RefreshToken) IsExpired

func (t *RefreshToken) IsExpired() bool

IsExpired checks the creation date to the expiry, if it's greater than 0, and returns true if the token is expired.

type SHA256RC4TokenSecureFormatter

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

func NewSHA256RC4TokenSecurityProvider

func NewSHA256RC4TokenSecurityProvider(key []byte) *SHA256RC4TokenSecureFormatter

func (*SHA256RC4TokenSecureFormatter) CryptToken

func (sc *SHA256RC4TokenSecureFormatter) CryptToken(source []byte) ([]byte, error)

func (*SHA256RC4TokenSecureFormatter) DecryptToken

func (sc *SHA256RC4TokenSecureFormatter) DecryptToken(source []byte) ([]byte, error)

type Token

type Token struct {
	ID           string        `json:"token_id"`
	CreationDate time.Time     `json:"date"`
	ExpiresIn    time.Duration `json:"expires_in"` // secs
	Credential   string        `json:"credential"`
	TokenType    TokenType     `json:"type"`
	Scope        string        `json:"scope"`
	Claims       Claims        `json:"claims"`
}

Token structure generated by the authorization server

func (*Token) IsExpired

func (t *Token) IsExpired() bool

IsExpired checks the creation date to the expiry, if it's greater than 0, and returns true if the token is expired.

type TokenProvider

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

func NewTokenProvider

func NewTokenProvider(formatter TokenSecureFormatter) *TokenProvider

func (*TokenProvider) CryptRefreshToken

func (tp *TokenProvider) CryptRefreshToken(t *RefreshToken) (token string, err error)

func (*TokenProvider) CryptToken

func (tp *TokenProvider) CryptToken(t *Token) (token string, err error)

func (*TokenProvider) DecryptRefreshTokens

func (tp *TokenProvider) DecryptRefreshTokens(refreshToken string) (refresh *RefreshToken, err error)

func (*TokenProvider) DecryptToken

func (tp *TokenProvider) DecryptToken(token string) (t *Token, err error)

type TokenResponse

type TokenResponse struct {
	Token                 string     `json:"access_token"`
	RefreshToken          string     `json:"refresh_token"`
	TokenType             TokenType  `json:"token_type"`               // bearer
	ExpiresIn             int64      `json:"expires_in"`               // secs
	RefreshTokenExpiresIn int64      `json:"refresh_token_expires_in"` // secs
	Properties            Properties `json:"properties"`
}

TokenResponse is the authorization server response

type TokenSecureFormatter

type TokenSecureFormatter interface {
	CryptToken(source []byte) ([]byte, error)
	DecryptToken(source []byte) ([]byte, error)
}

type TokenType

type TokenType string
const (
	BearerToken TokenType = "Bearer"
	AuthToken   TokenType = "A"
	UserToken   TokenType = "U"
	ClientToken TokenType = "C"
)

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

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