Skip to content

Commit

Permalink
Merge pull request #17 from tinh-tinh/feat/ren/3-allow-two2f
Browse files Browse the repository at this point in the history
#3 feat: support two factor
  • Loading branch information
Ren0503 authored Nov 9, 2024
2 parents 91759d8 + 9516f2c commit af034b7
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 0 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ go 1.22.0

require (
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/pquerna/otp v1.4.0
github.com/stretchr/testify v1.9.0
github.com/tinh-tinh/tinhtinh v1.2.2
)

require (
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tinh-tinh/tinhtinh v1.2.2 h1:jSeal9V7xWmtDv3vm5nE5N/W2qU77L5UHhSSMxgtbRg=
Expand Down
54 changes: 54 additions & 0 deletions twofa/twofa.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package twofa

import (
"time"

"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
"github.com/tinh-tinh/tinhtinh/core"
)

type Totp struct{}

func (tp *Totp) GenerateCode(secret string, t time.Time) (string, error) {
return totp.GenerateCode(secret, t)
}

func (tp *Totp) GenerateCodeCustom(secret string, t time.Time, opts totp.ValidateOpts) (string, error) {
return totp.GenerateCodeCustom(secret, t, opts)
}

func (tp *Totp) ValidateCustom(passcode string, secret string, t time.Time, opts totp.ValidateOpts) (bool, error) {
return totp.ValidateCustom(passcode, secret, t, opts)
}

func (tp *Totp) Validate(passcode string, secret string) bool {
return totp.Validate(passcode, secret)
}

func (tp *Totp) Generate(opts totp.GenerateOpts) (*otp.Key, error) {
return totp.Generate(opts)
}

const TOTP core.Provide = "TOTP"

func Register() core.Module {
return func(module *core.DynamicModule) *core.DynamicModule {
twoFAModule := module.New(core.NewModuleOptions{})

twoFAModule.NewProvider(core.ProviderOptions{
Name: TOTP,
Value: &Totp{},
})
twoFAModule.Export(TOTP)
return twoFAModule
}
}

func Inject(module *core.DynamicModule) *Totp {
val, ok := module.Ref(TOTP).(*Totp)
if !ok {
return nil
}
return val
}
96 changes: 96 additions & 0 deletions twofa/twofa_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package twofa_test

import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/pquerna/otp/totp"
"github.com/stretchr/testify/require"
"github.com/tinh-tinh/auth/twofa"
"github.com/tinh-tinh/tinhtinh/core"
)

func TestModule(t *testing.T) {
authController := func(module *core.DynamicModule) *core.DynamicController {
ctrl := module.NewController("test")
totpCode := twofa.Inject(module)

ctrl.Post("", func(ctx core.Ctx) error {
data, err := totpCode.Generate(totp.GenerateOpts{
Issuer: "Snake Oil",
AccountName: "[email protected]",
})
if err != nil {
return err
}
return ctx.JSON(core.Map{
"data": data.Secret(),
})
})

ctrl.Get("", func(ctx core.Ctx) error {
secret := ctx.Query("secret")
code := ctx.Query("code")
valid := totpCode.Validate(code, secret)
return ctx.JSON(core.Map{
"data": valid,
})
})

return ctrl
}

authModule := func(module *core.DynamicModule) *core.DynamicModule {
mod := module.New(core.NewModuleOptions{
Controllers: []core.Controller{authController},
})

return mod
}

appModule := func() *core.DynamicModule {
appMod := core.NewModule(core.NewModuleOptions{
Imports: []core.Module{
twofa.Register(),
authModule,
},
})

return appMod
}

app := core.CreateFactory(appModule)
app.SetGlobalPrefix("api")

testServer := httptest.NewServer(app.PrepareBeforeListen())
defer testServer.Close()

testClient := testServer.Client()

resp, err := testClient.Post(testServer.URL+"/api/test", "application/json", nil)
require.Nil(t, err)
require.Equal(t, 200, resp.StatusCode)

data, err := io.ReadAll(resp.Body)
require.Nil(t, err)

type Response struct {
Data interface{} `json:"data"`
}

var res Response
require.Nil(t, json.Unmarshal(data, &res))

resp, err = testClient.Get(testServer.URL + "/api/test?secret=" + res.Data.(string) + "&code=123456")
require.Nil(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)

data, err = io.ReadAll(resp.Body)
require.Nil(t, err)
require.Nil(t, json.Unmarshal(data, &res))
fmt.Println(res.Data)
}

0 comments on commit af034b7

Please sign in to comment.