WIP: Enrollement MFA avec TOTP
This commit is contained in:
parent
274a30480e
commit
53c94a490c
10 changed files with 144 additions and 1 deletions
80
controllers/mfa.go
Normal file
80
controllers/mfa.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"image/png"
|
||||
|
||||
"git.readonly.ch/bouzoure/popvaud-people/helpers"
|
||||
"git.readonly.ch/bouzoure/popvaud-people/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
func TotpEnrollPage(c *fiber.Ctx) error {
|
||||
db, err := helpers.GetDatabase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userid, err := helpers.GetSessionUserId(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var user models.User
|
||||
result := db.First(&user, "id = ?", userid)
|
||||
|
||||
if result.Error != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.TotpSercet.Valid {
|
||||
return fiber.NewError(fiber.StatusForbidden, "Forbidden")
|
||||
}
|
||||
|
||||
sess, err := helpers.GetSessionStore(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options := totp.GenerateOpts{
|
||||
Issuer: "POP Vaud",
|
||||
AccountName: user.Email,
|
||||
}
|
||||
|
||||
key, err := totp.Generate(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
img, err := key.Image(200, 200)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = png.Encode(&buf, img)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
imgBase64 := fmt.Sprintf(
|
||||
"data:image/png;base64,%s",
|
||||
base64.StdEncoding.EncodeToString(buf.Bytes()),
|
||||
)
|
||||
fmt.Println(imgBase64)
|
||||
|
||||
sess.Set("totp-enroll-secret", key.Secret())
|
||||
err = sess.Save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Render("totp_enroll", fiber.Map{
|
||||
"PageTitle": "Enregistrement multifacteur",
|
||||
"QrCode": imgBase64,
|
||||
"Secret": key.Secret(),
|
||||
}, "layouts/main")
|
||||
}
|
||||
2
go.mod
2
go.mod
|
|
@ -15,6 +15,7 @@ require (
|
|||
github.com/Joker/hpp v1.0.0 // indirect
|
||||
github.com/Joker/jade v1.1.3 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
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/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
||||
|
|
@ -29,6 +30,7 @@ require (
|
|||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/pquerna/otp v1.4.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
|
|
|
|||
5
go.sum
5
go.sum
|
|
@ -4,6 +4,8 @@ github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
|
|||
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -55,6 +57,8 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
|
|
@ -64,6 +68,7 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f
|
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ package helpers
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.readonly.ch/bouzoure/popvaud-people/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
|
@ -73,3 +75,23 @@ func UserExistsAndIsActive(id uint) (bool, error) {
|
|||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func GetSessionUserId(c *fiber.Ctx) (uint, error) {
|
||||
sess, err := GetSessionStore(c)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
userid := sess.Get("userid")
|
||||
if userid == nil {
|
||||
return 0, fmt.Errorf("no value for key userid in session")
|
||||
}
|
||||
|
||||
switch userid.(type) {
|
||||
case uint:
|
||||
default:
|
||||
return 0, fmt.Errorf("userid value is not of type uint as expected")
|
||||
}
|
||||
|
||||
return userid.(uint), nil
|
||||
}
|
||||
|
|
|
|||
1
main.go
1
main.go
|
|
@ -88,6 +88,7 @@ func main() {
|
|||
app.Get("/login", controllers.LoginForm)
|
||||
app.Post("/login", controllers.LoginProcess)
|
||||
app.Get("/logout", controllers.LogoutProcess)
|
||||
app.Get("/mfa/totp/enroll", controllers.TotpEnrollPage)
|
||||
|
||||
listenAddr := fmt.Sprintf(
|
||||
"%s:%d",
|
||||
|
|
|
|||
1
middlewares/mfa.go
Normal file
1
middlewares/mfa.go
Normal file
|
|
@ -0,0 +1 @@
|
|||
package middlewares
|
||||
4
static/events.js
Normal file
4
static/events.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
$(document).ready(function() {
|
||||
var elem = $("#totp-enroll-image img");
|
||||
$(elem).attr("src", $(elem).data("image"));
|
||||
});
|
||||
2
static/jquery/jquery.min.js
vendored
Normal file
2
static/jquery/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -13,6 +13,8 @@ html(lang="fr", data-bs-theme="dark")
|
|||
body
|
||||
| {{embed}}
|
||||
|
||||
script(src="/static/jquery/jquery.min.js")
|
||||
script(src="/static/bootstrap/js/bootstrap.bundle.min.js")
|
||||
script(src="/static/feather/dist/feather.min.js")
|
||||
script feather.replace();
|
||||
script(src="/static/events.js")
|
||||
|
|
|
|||
24
views/totp_enroll.pug
Normal file
24
views/totp_enroll.pug
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
include partials/header.pug
|
||||
|
||||
.container
|
||||
#login-card.my-5
|
||||
.card
|
||||
.card-header
|
||||
| Vérification multifacteur (TOTP)
|
||||
.card-body
|
||||
if .MfaError
|
||||
.alert.alert-danger
|
||||
| #{.MfaError}
|
||||
#totp-enroll-image.text-center.my-3
|
||||
img(data-image=.QrCode)
|
||||
form#login(method="post")
|
||||
.mb-3
|
||||
label.form-label(for="secret") Secret (si pas possible de scanner le code QR)
|
||||
input#secret.form-control(type="text", value!=.Secret, disabled)
|
||||
.mb-3
|
||||
label.form-label(for="otp") Code temporaire
|
||||
input#otp.form-control(type="text", required, name="otp", placeholder="000000" pattern="[0-9]{6}")
|
||||
.mt-3.text-end
|
||||
button.btn.btn-primary(type="submit")
|
||||
i.me-2(data-feather="check-circle")
|
||||
| Vérifier
|
||||
Loading…
Add table
Add a link
Reference in a new issue