From 431dec55f92b9eae0906f03e4ecd4112c2da0897 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Tue, 13 Feb 2024 23:23:01 -0500 Subject: [PATCH] feat: otp api support --- account/totp.go | 19 +++++++++++ account/web.go | 7 ++++ api/account/account.go | 8 +++-- api/account/http.go | 74 +++++++++++++++++++++++++++++++++++++++++ api/account/messages.go | 15 +++++++++ go.mod | 2 ++ go.sum | 5 +++ 7 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 account/totp.go create mode 100644 account/web.go diff --git a/account/totp.go b/account/totp.go new file mode 100644 index 0000000..c16dbac --- /dev/null +++ b/account/totp.go @@ -0,0 +1,19 @@ +package account + +import "github.com/pquerna/otp/totp" + +func TOTPGenerate(domain string, email string) (string, error) { + key, err := totp.Generate(totp.GenerateOpts{ + Issuer: domain, + AccountName: email, + }) + if err != nil { + return "", err + } + + return key.Secret(), nil +} + +func TOTPValidate(secret string, code string) bool { + return totp.Validate(code, secret) +} diff --git a/account/web.go b/account/web.go new file mode 100644 index 0000000..eb47974 --- /dev/null +++ b/account/web.go @@ -0,0 +1,7 @@ +package account + +import "go.sia.tech/jape" + +func SendJWT(jc jape.Context, jwt string) { + jc.ResponseWriter.Header().Set("Authorization", "Bearer "+jwt) +} diff --git a/api/account/account.go b/api/account/account.go index 1cb4f56..9f14144 100644 --- a/api/account/account.go +++ b/api/account/account.go @@ -74,7 +74,11 @@ func (a AccountAPI) Stop(ctx context.Context) error { func getRoutes(a *AccountAPI) map[string]jape.Handler { return map[string]jape.Handler{ - "/api/login": a.httpHandler.login, - "api/register": a.httpHandler.register, + "/api/auth/login": a.httpHandler.login, + "/api/auth/register": a.httpHandler.register, + "/api/auth/otp/generate": a.httpHandler.otpGenerate, + "/api/auth/otp/verify": a.httpHandler.otpVerify, + "/api/auth/otp/validate": a.httpHandler.otpValidate, + "/api/auth/otp/disable": a.httpHandler.otpDisable, } } diff --git a/api/account/http.go b/api/account/http.go index 4178acc..4753b15 100644 --- a/api/account/http.go +++ b/api/account/http.go @@ -3,6 +3,7 @@ package account import ( "errors" "git.lumeweb.com/LumeWeb/portal/account" + "git.lumeweb.com/LumeWeb/portal/api/middleware" "go.sia.tech/jape" "go.uber.org/fx" "go.uber.org/zap" @@ -78,3 +79,76 @@ func (h *HttpHandler) register(jc jape.Context) { return } } + +func (h *HttpHandler) otpGenerate(jc jape.Context) { + user := middleware.GetUserFromContext(jc.Request.Context()) + + otp, err := h.accounts.OTPGenerate(user) + if jc.Check("failed to generate otp", err) != nil { + return + } + + jc.Encode(&OTPGenerateResponse{ + OTP: otp, + }) +} + +func (h *HttpHandler) otpVerify(jc jape.Context) { + user := middleware.GetUserFromContext(jc.Request.Context()) + + var request OTPVerifyRequest + + if jc.Decode(&request) != nil { + return + } + + err := h.accounts.OTPEnable(user, request.OTP) + + if jc.Check("failed to verify otp", err) != nil { + return + } +} + +func (h *HttpHandler) otpValidate(jc jape.Context) { + user := middleware.GetUserFromContext(jc.Request.Context()) + + var request OTPValidateRequest + + if jc.Decode(&request) != nil { + return + } + + jwt, err := h.accounts.LoginOTP(user, request.OTP) + if jc.Check("failed to validate otp", err) != nil { + return + } + + account.SendJWT(jc, jwt) +} + +func (h *HttpHandler) otpDisable(jc jape.Context) { + user := middleware.GetUserFromContext(jc.Request.Context()) + + var request OTPDisableRequest + + if jc.Decode(&request) != nil { + return + } + + valid, _, err := h.accounts.ValidLoginByUserID(user, request.Password) + + if !valid { + if err != nil { + err = errors.Join(errInvalidLogin, err) + } + + if jc.Check("failed to validate password", err) != nil { + return + } + } + + err = h.accounts.OTPDisable(user) + if jc.Check("failed to disable otp", err) != nil { + return + } +} diff --git a/api/account/messages.go b/api/account/messages.go index b511720..eb76785 100644 --- a/api/account/messages.go +++ b/api/account/messages.go @@ -11,3 +11,18 @@ type RegisterRequest struct { Email string `json:"email"` Password string `json:"password"` } + +type OTPGenerateResponse struct { + OTP string `json:"otp"` +} + +type OTPVerifyRequest struct { + OTP string `json:"otp"` +} + +type OTPValidateRequest struct { + OTP string `json:"otp"` +} +type OTPDisableRequest struct { + Password string `json:"password"` +} diff --git a/go.mod b/go.mod index f05be2f..fd2dd0e 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/google/uuid v1.5.0 github.com/hashicorp/go-plugin v1.6.0 github.com/julienschmidt/httprouter v1.3.0 + github.com/pquerna/otp v1.4.0 github.com/rs/cors v1.10.1 github.com/samber/lo v1.39.0 github.com/spf13/pflag v1.0.5 @@ -58,6 +59,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.26.6 // indirect github.com/aws/smithy-go v1.19.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/casbin/govaluate v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dchest/threefish v0.0.0-20120919164726-3ecf4c494abf // indirect diff --git a/go.sum b/go.sum index 070f88b..a160f43 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +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/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/casbin/casbin/v2 v2.81.0 h1:vNwJXK7a+TJZElZ5saP+SFJvweZNtJ3MlVP6P4IuRqE= @@ -260,6 +262,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= @@ -320,6 +324,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +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.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=