opengist/internal/web/handlers/auth/webauthn.go
Thomas Miceli c2899ef41b wip
2025-01-11 20:17:01 +01:00

151 lines
4.1 KiB
Go

package auth
import (
"bytes"
gojson "encoding/json"
"github.com/thomiceli/opengist/internal/auth/webauthn"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/web/context"
"io"
)
func BeginWebAuthnBinding(ctx *context.Context) error {
credsCreation, jsonWaSession, err := webauthn.BeginBinding(ctx.User)
if err != nil {
return ctx.ErrorRes(500, "Cannot begin WebAuthn registration", err)
}
sess := ctx.GetSession()
sess.Values["webauthn_registration_session"] = jsonWaSession
sess.Options.MaxAge = 5 * 60 // 5 minutes
ctx.SaveSession(sess)
return ctx.JSON(200, credsCreation)
}
func FinishWebAuthnBinding(ctx *context.Context) error {
sess := ctx.GetSession()
jsonWaSession, ok := sess.Values["webauthn_registration_session"].([]byte)
if !ok {
return ctx.ErrorRes(401, "Cannot get WebAuthn registration session", nil)
}
user := ctx.User
// extract passkey name from request
body, err := io.ReadAll(ctx.Request().Body)
if err != nil {
return ctx.ErrorRes(400, "Failed to read request body", err)
}
ctx.Request().Body.Close()
ctx.Request().Body = io.NopCloser(bytes.NewBuffer(body))
dto := new(db.CrendentialDTO)
_ = gojson.Unmarshal(body, &dto)
if err = ctx.Validate(dto); err != nil {
return ctx.ErrorRes(400, "Invalid request", err)
}
passkeyName := dto.PasskeyName
if passkeyName == "" {
passkeyName = "WebAuthn"
}
waCredential, err := webauthn.FinishBinding(user, jsonWaSession, ctx.Request())
if err != nil {
return ctx.ErrorRes(403, "Failed binding attempt for passkey", err)
}
if _, err = db.CreateFromCrendential(user.ID, passkeyName, waCredential); err != nil {
return ctx.ErrorRes(500, "Cannot create WebAuthn credential on database", err)
}
delete(sess.Values, "webauthn_registration_session")
ctx.SaveSession(sess)
ctx.AddFlash(ctx.Tr("flash.auth.passkey-registred", passkeyName), "success")
return ctx.JSON_([]string{"OK"})
}
func BeginWebAuthnLogin(ctx *context.Context) error {
credsCreation, jsonWaSession, err := webauthn.BeginDiscoverableLogin()
if err != nil {
return ctx.ErrorRes(401, "Cannot begin WebAuthn login", err)
}
sess := ctx.GetSession()
sess.Values["webauthn_login_session"] = jsonWaSession
sess.Options.MaxAge = 5 * 60 // 5 minutes
ctx.SaveSession(sess)
return ctx.JSON_(credsCreation)
}
func FinishWebAuthnLogin(ctx *context.Context) error {
sess := ctx.GetSession()
sessionData, ok := sess.Values["webauthn_login_session"].([]byte)
if !ok {
return ctx.ErrorRes(401, "Cannot get WebAuthn login session", nil)
}
userID, err := webauthn.FinishDiscoverableLogin(sessionData, ctx.Request())
if err != nil {
return ctx.ErrorRes(403, "Failed authentication attempt for passkey", err)
}
sess.Values["user"] = userID
sess.Options.MaxAge = 60 * 60 * 24 * 365 // 1 year
delete(sess.Values, "webauthn_login_session")
ctx.SaveSession(sess)
return ctx.JSON_([]string{"OK"})
}
func BeginWebAuthnAssertion(ctx *context.Context) error {
sess := ctx.GetSession()
ogUser, err := db.GetUserById(sess.Values["mfaID"].(uint))
if err != nil {
return ctx.ErrorRes(500, "Cannot get user", err)
}
credsCreation, jsonWaSession, err := webauthn.BeginLogin(ogUser)
if err != nil {
return ctx.ErrorRes(401, "Cannot begin WebAuthn login", err)
}
sess.Values["webauthn_assertion_session"] = jsonWaSession
sess.Options.MaxAge = 5 * 60 // 5 minutes
ctx.SaveSession(sess)
return ctx.JSON_(credsCreation)
}
func FinishWebAuthnAssertion(ctx *context.Context) error {
sess := ctx.GetSession()
sessionData, ok := sess.Values["webauthn_assertion_session"].([]byte)
if !ok {
return ctx.ErrorRes(401, "Cannot get WebAuthn assertion session", nil)
}
userId := sess.Values["mfaID"].(uint)
ogUser, err := db.GetUserById(userId)
if err != nil {
return ctx.ErrorRes(500, "Cannot get user", err)
}
if err = webauthn.FinishLogin(ogUser, sessionData, ctx.Request()); err != nil {
return ctx.ErrorRes(403, "Failed authentication attempt for passkey", err)
}
sess.Values["user"] = userId
sess.Options.MaxAge = 60 * 60 * 24 * 365 // 1 year
delete(sess.Values, "webauthn_assertion_session")
delete(sess.Values, "mfaID")
ctx.SaveSession(sess)
return ctx.JSON_([]string{"OK"})
}