oauth

package module
v0.5.6 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2023 License: MIT Imports: 7 Imported by: 0

README

oauth middleware

OAuth 2.0 Authorization Server & Authorization Middleware for Gin-Gonic

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

This repository is forked from maxzerbini/oauth, making it Go Module compatable while adding built-in JWT to it.

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 Gin-Gonic middleware BearerAuthentication intercepts the resource server calls and authorizes only resource requests containing a valid bearer token.

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() {
	router := gin.New()
	router.Use(gin.Recovery())
	router.Use(gin.Logger())

    s := oauth.NewOAuthBearerServer(
		"mySecretKey-10101",
		time.Hour*24*14,
		time.Second*120,
		&TestUserVerifier{},
		nil)
	router.POST("/token", s.UserCredentials)
	router.POST("/auth", s.ClientCredentials)
	
	router.Run(":9090")
}

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

Authorization Middleware usage example

This snippet shows how to use the middleware

    authorized := router.Group("/")
	// use the Bearer Athentication middleware
	authorized.Use(oauth.Authorize("mySecretKey-10101", nil))

	authorized.GET("/customers", GetCustomers)
	authorized.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 signingMethod and the same secret key for encryption/decryption.

Note

This master branch introduces breaking changes in the interface CredentialsVerifier methods ValidateUser, ValidateClient and AddClaims. Refer to v1 branch for the previous implementation. Updated server implementation in v3 due to go.uuid library change.

Reference

License

MIT

Documentation

Index

Constants

View Source
const (
	TokenType = "Bearer"
)

Variables

View Source
var (
	ErrInvalidRefreshTokenSigningMethod = OauthErrorResponse{
		ErrorName:        "invalid_request",
		ErrorDescription: "token used an invalid signing method",
		Header:           "Bearer",
		Status:           http.StatusBadRequest,
		NoCache:          true,
	}

	ErrNotRefreshToken = OauthErrorResponse{
		ErrorName:        "invalid_request",
		ErrorDescription: "token provided was not a refresh token",
		Header:           "Bearer",
		Status:           http.StatusBadRequest,
		NoCache:          true,
	}

	ErrRefreshTokenValidationError = OauthErrorResponse{
		ErrorName:        "invalid_request",
		ErrorDescription: "token provided was not a valid refresh token",
		Header:           "Bearer",
		Status:           http.StatusBadRequest,
		NoCache:          true,
	}

	ErrInvalidGrant = OauthErrorResponse{
		ErrorName:        "invalid_grant",
		ErrorDescription: "invalid password or client_secret",
		Header:           "Bearer",
		Status:           http.StatusBadRequest,
		NoCache:          true,
	}

	ErrInvalidAuthenticationScheme = OauthErrorResponse{
		ErrorName:        "invalid_request",
		ErrorDescription: "invalid authentication scheme, bearer wanted",
		Header:           "Bearer",
		Status:           http.StatusBadRequest,
		NoCache:          true,
	}

	ErrUnsupportedGrantType = OauthErrorResponse{
		ErrorName:        "unsupported_grant_type",
		ErrorDescription: "grant type required not available on this server",
		Header:           "Bearer",
		Status:           http.StatusBadRequest,
		NoCache:          true,
	}

	ErrInvalidClient = OauthErrorResponse{
		ErrorName:        "invalid_client",
		ErrorDescription: "invalid client id or secret",
		Header:           "Bearer",
		Status:           http.StatusUnauthorized,
		NoCache:          true,
	}

	ErrInvalidRevocationRequest = OauthErrorResponse{
		ErrorName:        "invalid_request",
		ErrorDescription: "invalid revocation request",
		Status:           http.StatusBadRequest,
	}

	ErrUnsupportedTokenType = OauthErrorResponse{
		ErrorName:        "unsupported_token_type",
		ErrorDescription: "unsupported token type",
		Status:           http.StatusServiceUnavailable,
		NoCache:          true,
	}

	ErrInvalidAccessTokenSigningMethod = OauthErrorResponse{
		ErrorName:        "invalid_token",
		ErrorDescription: "token used an invalid signing method",
		Header:           "Bearer",
		Status:           http.StatusUnauthorized,
	}

	ErrNotAccessToken = OauthErrorResponse{
		ErrorName:        "invalid_token",
		ErrorDescription: "token provided was not a access token",
		Header:           "Bearer",
		Status:           http.StatusUnauthorized,
	}

	ErrTokenInvalid = OauthErrorResponse{
		ErrorName:        "invalid_token",
		ErrorDescription: "the token is invalid",
		Header:           "Bearer",
		Status:           http.StatusUnauthorized,
	}

	ErrTokenUsedBeforeValid = OauthErrorResponse{
		ErrorName:        "invalid_token",
		ErrorDescription: "the token is not valid yet",
		Header:           "Bearer",
		Status:           http.StatusUnauthorized,
	}

	ErrTokenExpire = OauthErrorResponse{
		ErrorName:        "invalid_token",
		ErrorDescription: "the token has expired",
		Header:           "Bearer",
		Status:           http.StatusUnauthorized,
	}

	ErrTokenValidationError = OauthErrorResponse{
		ErrorName:        "invalid_token",
		ErrorDescription: "the token is invalid",
		Header:           "Bearer",
		Status:           http.StatusUnauthorized,
	}

	ErrInsufficientScope = OauthErrorResponse{
		ErrorName:        "insufficient_scope",
		ErrorDescription: "the request requires higher privileges than provided by the access token",
		Header:           "Bearer",
		Status:           http.StatusForbidden,
	}

	ErrTokenUsedBeforeIssued = OauthErrorResponse{
		ErrorName:        "invalid_token",
		ErrorDescription: "the token is not yet issued",
		Header:           "Bearer",
		Status:           http.StatusUnauthorized,
	}

	ErrInvalidScope = OauthErrorResponse{
		ErrorName:        "invalid_scope",
		ErrorDescription: "you requested for an invalid scope",
		Header:           "Bearer",
		Status:           http.StatusBadRequest,
		NoCache:          true,
	}
)
View Source
var (
	AvailableGrantTypes               = []string{"password", "client_credentials", "authorization_code", "refresh_token"}
	PasswordWithRefreshToken          = []string{"password", "refresh_token"}
	ClientCredentialsWithRefreshToken = []string{"client_credentials", "refresh_token"}
	AuthorizationCodeOnly             = []string{"authorization_code"}
)

Functions

func Authorize

func Authorize(secretKey string, signingMethod jwt.SigningMethod, customValidator AccessTokenValidator) gin.HandlerFunc

Authorize is the OAuth 2.0 middleware for Gin-Gonic resource server. Authorize creates a BearerAuthentication middlever and return the Authorize method.

Types

type AccessTokenValidator

type AccessTokenValidator interface {
	ValidateAccessToken(token *Token, ctx *gin.Context) error
}

type AuthorizationCodeVerifier

type AuthorizationCodeVerifier interface {
	// ValidateCode checks the authorization code and returns the user credential
	ValidateCode(clientID, clientSecret, code, redirectURI string, req *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 Gin-Gonic

func NewBearerAuthentication

func NewBearerAuthentication(secretKey string, signingMethod jwt.SigningMethod, customValidator AccessTokenValidator) *BearerAuthentication

NewBearerAuthentication create a BearerAuthentication middleware

func (*BearerAuthentication) Authorize

func (ba *BearerAuthentication) Authorize(ctx *gin.Context)

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

type CredentialsVerifier

type CredentialsVerifier interface {
	// Validate username and password returning an error if the user credentials are wrong
	ValidateUser(username, password string, scope []string, req *http.Request) error
	// Validate clientId and secret returning an error if the client credentials are wrong
	ValidateClient(clientID, clientSecret string, scope []string, req *http.Request) error
	// Provide additional claims to the token
	AddClaims(credential, tokenID, tokenType string, scope []string) (map[string]string, error)
	// Optionally store the tokenID generated for the user
	StoreTokenId(credential, tokenID, tokenType string) error
	// Provide additional information to the authorization server response
	AddProperties(credential, tokenID, tokenType string, scope []string) (map[string]string, error)
	// Optionally validate previously stored tokenID during refresh request
	ValidateTokenId(credential, tokenID, tokenType string) error
	// Optionally revoke a token by it's ID
	RevokeToken(tokenID string) error
}

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

type OAuthBearerServer

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

OAuthBearerServer is the OAuth 2 Bearer Server implementation.

func NewOAuthBearerServer

func NewOAuthBearerServer(
	secretKey string,
	ttl time.Duration,
	refreshTtl time.Duration,
	verifier CredentialsVerifier,
	signingMethod jwt.SigningMethod,
) *OAuthBearerServer

NewOAuthBearerServer creates new OAuth 2 Bearer Server

func (*OAuthBearerServer) GetOAuthServer added in v0.5.0

func (s *OAuthBearerServer) GetOAuthServer(allowedGrantTypes []string) gin.HandlerFunc

GetOAuthServer returns the OAuth 2.0 server handler

func (*OAuthBearerServer) TokenRevocationServer added in v0.5.0

func (s *OAuthBearerServer) TokenRevocationServer(ctx *gin.Context)

TokenRevocationServer manages token revocation requests, it revokes a valid token, along with all tokens share the same id with it. We broke the standard to make a 4xx response when the client intend to revoke an invalid token.

type OauthErrorResponse added in v0.3.0

type OauthErrorResponse struct {
	ErrorName        string `json:"error"`
	ErrorDescription string `json:"error_description,omitempty"`
	ErrorUrl         string `json:"error_url,omitempty"`
	Header           string `json:"-"` // WWW-Authenticate header, used to indicate available authentication scheme(s), in our case it's Bearer
	Status           int    `json:"-"` // HTTP status code
	NoCache          bool   `json:"-"` // Set Cache-Control: no-store
}

OauthErrorResponse is the error response structure. Can be serialized to a valid OAuth2 error response. ErrorNames were error codes defined in RFC6749(https://datatracker.ietf.org/doc/html/rfc6749) at 4.1.2.1, 4.2.2.1 and 5.2. When error occours in parts of this lib, it would be pushed into gin.Context.Errors. Handle them with a custom middleware.

func (OauthErrorResponse) Error added in v0.3.0

func (e OauthErrorResponse) Error() string

type Token

type Token struct {
	Audience   string            `json:"aud"`
	ExpiresAt  int64             `json:"exp"`
	Id         string            `json:"jti"`
	IssuedAt   int64             `json:"iat"`
	NotBefore  int64             `json:"nbf"`
	TokenType  string            `json:"type"` // "user" for user, "client" for client
	ForRefresh bool              `json:"for_refresh"`
	Scope      []string          `json:"scope"`
	Claims     map[string]string `json:"claims"`
}

Token structure generated by the authorization server

func (Token) Valid

func (t Token) Valid() error

type TokenResponse

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

TokenResponse is the authorization server response

Directories

Path Synopsis
test

Jump to

Keyboard shortcuts

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