daddy/internal/route/login.go

98 lines
2.2 KiB
Go

package route
import (
"fmt"
"net/http"
"forge.cadoles.com/Cadoles/daddy/internal/auth"
"forge.cadoles.com/Cadoles/daddy/internal/model"
"forge.cadoles.com/Cadoles/daddy/internal/orm"
"forge.cadoles.com/Cadoles/daddy/internal/session"
"github.com/pkg/errors"
"forge.cadoles.com/Cadoles/daddy/internal/config"
oidc "forge.cadoles.com/wpetit/goweb-oidc"
"gitlab.com/wpetit/goweb/logger"
"gitlab.com/wpetit/goweb/middleware/container"
)
func handleLogin(w http.ResponseWriter, r *http.Request) {
ctn := container.Must(r.Context())
client := oidc.Must(ctn)
client.Login(w, r)
}
type emailClaims struct {
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
}
func handleLoginCallback(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
ctn := container.Must(ctx)
conf := config.Must(ctn)
auth := auth.Must(ctn)
idToken, err := oidc.IDToken(w, r)
if err != nil {
logger.Error(ctx, "could not retrieve idToken", logger.E(err))
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
logger.Info(ctx, "user logged in", logger.F("sub", idToken.Subject))
claims := &emailClaims{}
if err := idToken.Claims(claims); err != nil {
panic(errors.WithStack(err))
}
// TODO implements better UX in case of errors
if claims.Email == "" {
http.Error(w, "an email is expected to access this app", http.StatusForbidden)
return
}
if !claims.EmailVerified {
http.Error(w, "your email must be verified to access this app", http.StatusForbidden)
return
}
db := orm.Must(ctn).DB()
repo := model.NewUserRepository(db)
user, err := repo.CreateOrConnectUser(ctx, claims.Email)
if err != nil {
panic(errors.Wrap(err, "could not upsert user"))
}
authorized, err := auth.Authorize(user)
if err != nil {
panic(errors.WithStack(err))
}
if !authorized {
message := fmt.Sprintf(
"You are not authorized to access this application. Disconnect by navigating to %s.",
"http://"+r.Host+"/logout",
)
http.Error(w, message, http.StatusForbidden)
return
}
if err := session.SaveUserEmail(w, r, claims.Email); err != nil {
panic(errors.WithStack(err))
}
http.Redirect(w, r, conf.HTTP.FrontendURL, http.StatusSeeOther)
}