Skip to content

Commit

Permalink
Merge pull request #31 from edwinvautier/feat_authenticator-bundle
Browse files Browse the repository at this point in the history
Authenticator bundle V1
  • Loading branch information
Edwin Vautier authored Apr 3, 2021
2 parents 70fe6ed + fc57b9c commit 432e530
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 48 deletions.
62 changes: 62 additions & 0 deletions bundles/authenticator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Authenticator bundle

This bundle is used to authenticate users on your API.
It uses [Json Web Token](https://en.wikipedia.org/wiki/JSON_Web_Token).

## Setup

In order to work, the bundle needs the following variables to be set in your `.env` file :

| variable | description |
| -------------------- | -------------------------------------- |
| TOKEN_VALID_DURATION | valid duration of the token in minutes |
| RSA_PUBLIC_PATH | the path to the public.pem file |
| RSA_PRIVATE_PATH | the path to the private.pem file |
| RSA_PASSWORD | password of the encryption key |
| DOMAIN | the domain for the cookies |

As the bundle uses RSA keys, you need to generate them :

```sh
# Generate private.pem
openssl genrsa -des3 -out private.pem 2048

# Generate public.pem
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
```

> 💡 You need to use the same password for both commands, this password is the one you set as **RSA_PASSWORD**
By default if you put your public.pem and private.pem in project root, then the path to them should be `../public.pem`.

## Available methods

The following methods are available :

| name | description |
| ------------- | ------------------------------------------------ |
| GenerateToken | generates a JWT from email |
| DecodeToken | decode a jwt and returns the token and its claim |
| HashPassword | hash password with MD5 method |

## Use

When installed, a middleware is automatically created for authentication.
You can use it inside the `api/routes/router.go` file :

```go
func Init(r *gin.Engine) {
r.POST("/register", controllers.CreateCustomer)
r.POST("/login", controllers.Login)

api := r.Group("/api")
api.Use(middlewares.CheckAuthorization)
{
api.GET("/", controllers.TestController)
}
}
```

`login controller` is also created, feel free to modify it to your needs.

Your customer entity should have a password, you can use the authenticator.HashPassword to do so.
26 changes: 0 additions & 26 deletions bundles/authenticator/authenticator.go

This file was deleted.

2 changes: 1 addition & 1 deletion bundles/authenticator/password_hasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

// HashPassword takes a string in parameter and returns the same string hashed with sha512
func (auth Authenticator) HashPassword(password string) string {
func HashPassword(password string) string {
h := sha512.New()
h.Write([]byte(password))
bytesHash := h.Sum(nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"{{.GoPackageFullPath}}/shared/env"
"{{.GoPackageFullPath}}/api/models"
"{{.GoPackageFullPath}}/api/repositories"
"{{.GoPackageFullPath}}/shared/services"
"github.com/edwinvautier/go-cli/bundles/authenticator"
"github.com/gin-gonic/gin"
)

Expand All @@ -29,14 +29,14 @@ func Login(c *gin.Context) {
}

// Verify password
hashedPwd := services.HashPassword(customerForm.Password)
hashedPwd := authenticator.HashPassword(customerForm.Password)
if hashedPwd != customer.HashedPassword {
c.JSON(http.StatusUnauthorized, "incorrect email or password.")
return
}

// Generate connection token
token, err := services.GenerateToken(customer.Email)
token, err := authenticator.GenerateToken(customer.Email)
if err != nil {
c.JSON(http.StatusInternalServerError, "Couldn't create your authorization")
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"{{.GoPackageFullPath}}/api/models"
"{{.GoPackageFullPath}}/api/repositories"

"{{.GoPackageFullPath}}/shared/services"
"github.com/edwinvautier/go-cli/bundles/authenticator"
"github.com/gin-gonic/gin"
)

Expand All @@ -23,7 +23,7 @@ func CheckAuthorization(c *gin.Context) {
token := strings.TrimPrefix(bearer, "Bearer ")

// validate and decode token to get its informations
_, claims, err := services.DecodeToken(token)
_, claims, err := authenticator.DecodeToken(token)
if err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, "Unauthorized")
return
Expand Down
10 changes: 8 additions & 2 deletions bundles/authenticator/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ type Rsa struct {
PrivateKey interface{}
}

// Claim is the struct for the jwt claim
type Claim struct {
Email string
jwt.StandardClaims
}

var rsa Rsa

func initRsaKeys() error {
Expand Down Expand Up @@ -58,7 +64,7 @@ func initRsaKeys() error {
}

// GenerateToken creates a JWT with email and expiration time in the payload
func (auth Authenticator) GenerateToken(email string) (string, error) {
func GenerateToken(email string) (string, error) {
err := initRsaKeys()
if err != nil {
return "", errors.New("Couldn't init rsa keys")
Expand Down Expand Up @@ -88,7 +94,7 @@ func (auth Authenticator) GenerateToken(email string) (string, error) {
}

// DecodeToken decode and validates a token
func (auth Authenticator) DecodeToken(tokenString string) (*jwt.Token, *Claim, error) {
func DecodeToken(tokenString string) (*jwt.Token, *Claim, error) {
err := initRsaKeys()
if err != nil {
return nil, &Claim{}, errors.New("Couldn't init rsa keys")
Expand Down
14 changes: 0 additions & 14 deletions templates/tests/customers_test.go.template
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ func TestValidateCustomer(t *testing.T) {
args: args{
&models.CustomerForm{
Name: "",
Password: "tesT12345",
Email: "bob@gmail.com",
},
},
Expand All @@ -30,29 +29,16 @@ func TestValidateCustomer(t *testing.T) {
args: args{
&models.CustomerForm{
Name: "Bob",
Password: "tesT12345",
Email: "bobgmail.com",
},
},
wantErr: true,
},
{
name: "wrong password",
args: args{
&models.CustomerForm{
Name: "Bob",
Password: "test",
Email: "bob@gmail.com",
},
},
wantErr: true,
},
{
name: "correct customer",
args: args{
&models.CustomerForm{
Name: "Bob",
Password: "tesT12345",
Email: "bob@gmail.com",
},
},
Expand Down

0 comments on commit 432e530

Please sign in to comment.