OAuth 2.0 Server & Authorization Middleware for Gin-Gonic

Project OAuth

OAuth is an open source project developed in Go and available on GitHub. The project offers an Authorization Server and an Authorization Middleware that can be used to make secure access to resources API and provide a simple and efficient implementation of the OAuth 2.0 specification.
Authorization Server and Middleware are both based on Gin-Gonic and can interoperate with any other middleware available for this framework. 

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 authorization token usable for consuming resources API.
Developers need only add their own implementation of CredentialsVerifier interface to validate the credentials of users or applications that require a token.

package main

import (
 "errors"
 "time"

 "gopkg.in/gin-gonic/gin.v1"
 "github.com/maxzerbini/oauth"
)


func main() {
 router := gin.New()
 router.Use(gin.Recovery())
 router.Use(gin.Logger())
 gin.SetMode(gin.DebugMode)
 registerAPI(router)
 router.Run(":3000")
}


func registerAPI(router *gin.Engine) {
 s := oauth.NewOAuthBearerServer(
  "mySecretKey-10101",
  time.Second*120,
  &TestUserVerifier{},
  nil)
 router.POST("/token", s.UserCredentials)
 router.POST("/auth", s.ClientCredentials)
}


Grant type Resource Owner Password

This grant type is suitable for clients capable of obtaining the resource owner's credentials username and password, typically using an interactive form.
The middleware method that validates this grant type is UserCredentials. The clients can provide an optional scope parameter that specifies the scope of the access request.



Grant type Client Credentials

If the client will request access to the protected resources under its control, it can  request an access token using only its credentials.
The middleware method that validates this grant type is ClientCredentials.



Credentials Verifier

The interface CredentialsVerifier defines the method hooks called during the token generation process:
  • 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
  • ValidateTokenId() is called when a refresh token in requested
// Provides user credentials verifier for testing.
type TestUserVerifier struct {
}


// Validate username and password returning an error if the user credentials are wrong
func (*TestUserVerifier) ValidateUser(username, password, scope string) error {
 if username == "user01" && password == "12345" {
  return nil
 } else {
  return errors.New("Wrong user")
 }
}


// Validate clientId and secret returning an error if the client credentials are wrong
func (*TestUserVerifier) ValidateClient(clientId, clientSecret, scope string) error {
 if clientId == "abcdef" && clientSecret == "12345" {
  return nil
 } else {
  return errors.New("Wrong client")
 }
}


// Provide additional claims to the token
func (*TestUserVerifier) AddClaims(credential, tokenId, tokenType string) (map[string]string, error) {
 claims := make(map[string]string)
 claims["customerId"] = "1001"
 claims["customerData"] = `{"OrderDate":"2016-12-14","OrderId":"9999"}`
 return claims, nil
}


// Optionally store the token Id generated for the user
func (*TestUserVerifier) StoreTokenId(credential, tokenId, refreshTokenID, tokenType string) error {
 return nil
}


// Provide additional information to the token response
func (*TestUserVerifier) AddProperties(credential, tokenId, tokenType string) (map[string]string, error) {
 props := make(map[string]string)
 props["customerName"] = "Gopher"
 return props, nil
}


// Validate token Id
func (*TestUserVerifier) ValidateTokenId(credential, tokenId, refreshTokenID, tokenType string) error {
 return nil
}


Authorization Middleware

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

package main

import (
 "gopkg.in/gin-gonic/gin.v1"
 "github.com/maxzerbini/oauth"
)


func main() {
 router := gin.New()
 router.Use(gin.Recovery())
 router.Use(gin.Logger())
 gin.SetMode(gin.DebugMode)
 registerAPI(router)
 router.Run(":3200")
}


func registerAPI(router *gin.Engine) {
 authorized := router.Group("/")
 // use the Bearer Athentication middleware
 authorized.Use(oauth.Authorize("mySecretKey-10101", nil))

 authorized.GET("/customers", GetCustomers)
}


func GetCustomers(c *gin.Context) {
 c.JSON(200, gin.H{
  "Status":        "verified",
  "Customer":      "test001",
  "CustomerName":  "Max",
  "CustomerEmail": "test@test.com",
 })
}


Token Formatter

Authorization Server crypts the token using the Token Formatter and Authorization Middleware decrypts the token using the same Token Formatter.
Programmers can develop their Token Formatter implementing the interface TokenSecureFormatter and this is really recommended before publishing the API in a production environment. Otherwise they can use the default implementation SHA256RC4TokenSecureFormatter.

CORS

Cross-Origin Resource Sharing is a common mechanism that allows the execution of requests from different domains. If your secure API are called from a Javascript application running on a browser and you are using different domains for the App site and the API then you can use CORS to overcome the same-origin security policy.
Gin-Gonic has an official CORS middleware that can be used together with the Authorization middleware shown above.




Commenti

Post popolari in questo blog

From the database schema to RESTful API with DinGo

OVO Key/Value Storage