Skip to content

Commit

Permalink
Merge pull request #3 from serveba/master
Browse files Browse the repository at this point in the history
feat(tokens): Access token with encrypted v2.local paseto interface w…
  • Loading branch information
mapreal19 authored Jan 15, 2020
2 parents cec8bf3 + ce29df1 commit d7dc61b
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: go

go:
- 1.12.x
- 1.13.x

env:
- GO111MODULE=on
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/mapreal19/beemiel

go 1.12
go 1.13

require (
github.com/astaxie/beego v1.11.1
Expand Down
25 changes: 25 additions & 0 deletions tokens/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Tokens

### Access Token

We use PASETO under the hood: https://paseto.io/

Set your env values first: `PASETO_SYMMETRIC_KEY` (32 bytes key)

You could generate those using `tokens.GenerateSymmetricKey()` function:

```go
fmt.Println("generated symmetric key: ", tokens.GenerateSymmetricKey())
```

Generate new reset token:
```go
passwords.NewAccessToken(payload string, expiration time.Time)
```

Get Payload from reset token:
```go
passwords.GetPayloadFromToken(token string)
```

See tests in order to set and get a given JSON payload.
59 changes: 59 additions & 0 deletions tokens/tokens.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package tokens

import (
"crypto/rand"
"encoding/hex"
"os"
"time"

"github.com/o1egl/paseto"
)

func NewAccessToken(payload string, expiration time.Time) string {
now := time.Now()

jsonToken := paseto.JSONToken{
Subject: payload,
IssuedAt: now,
NotBefore: now,
Expiration: expiration,
}

v2 := paseto.NewV2()
token, err := v2.Encrypt(symmetricKey(), jsonToken, nil)

if err != nil {
panic(err)
}

return token
}

func GetPayloadFromToken(token string) (payload string, err error) {
v2 := paseto.NewV2()
jsonToken := paseto.JSONToken{}

v2.Decrypt(token, symmetricKey(), &jsonToken, nil)
if err != nil {
return
}

err = jsonToken.Validate()
if err != nil {
return
}

payload = jsonToken.Subject
return
}

func symmetricKey() (key []byte) {
key, _ = hex.DecodeString(os.Getenv("PASETO_SYMMETRIC_KEY"))
return
}

func GenerateSymmetricKey() string {
key := make([]byte, 32)
_, _ = rand.Read(key)
return hex.EncodeToString(key)
}
13 changes: 13 additions & 0 deletions tokens/tokens_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package tokens_test

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestTokens(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Tokens Suite")
}
77 changes: 77 additions & 0 deletions tokens/tokens_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package tokens_test

import (
"encoding/json"
"os"
"time"

"github.com/mapreal19/beemiel/tokens"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("tokens", func() {
Describe("NewAccessToken", func() {
It("returns PASETO token", func() {
_ = os.Setenv("PASETO_SYMMETRIC_KEY", tokens.GenerateSymmetricKey())
userID := "1"
expiration := time.Now().Add(2 * time.Hour)

result := tokens.NewAccessToken(userID, expiration)

Expect(result).To(HavePrefix("v2.local."))
})

Context("when having a wrong PASETO key", func() {
It("panics", func() {
_ = os.Setenv("PASETO_SYMMETRIC_KEY", "wannabekey")
userID := "1"
expiration := time.Now().Add(2 * time.Hour)

result := func() {
tokens.NewAccessToken(userID, expiration)
}

Expect(result).Should(Panic())
})
})
})

Describe("GetPayloadFromToken", func() {
It("returns json payload", func() {
_ = os.Setenv("PASETO_SYMMETRIC_KEY", tokens.GenerateSymmetricKey())
payload := `{"base":"BTC","currency":"USD","amount":12334.87}`

expiration := time.Now().Add(2 * time.Hour)
token := tokens.NewAccessToken(payload, expiration)

result, err := tokens.GetPayloadFromToken(token)

type money struct {
Base string `json:"base"`
Currency string `json:"currency"`
Amount float32 `json:"amount"`
}
var response money

err = json.Unmarshal([]byte(result), &response)

Expect(err).To(BeNil())
Expect("BTC").To(Equal(response.Base))
Expect("USD").To(Equal(response.Currency))
})

Context("when token is expired", func() {
It("returns error", func() {
_ = os.Setenv("PASETO_SYMMETRIC_KEY", tokens.GenerateSymmetricKey())
userID := "1"
expiration := time.Now().Add(-2 * time.Hour)
token := tokens.NewAccessToken(userID, expiration)

_, err := tokens.GetPayloadFromToken(token)

Expect(err.Error()).To(Equal("token has expired: token validation error"))
})
})
})
})

0 comments on commit d7dc61b

Please sign in to comment.