Commit: Bulk unfinished work

This commit is contained in:
2026-01-22 17:39:04 +01:00
parent 6c46b4efcc
commit 3a9acc42a2
68 changed files with 5047 additions and 1064 deletions

116
interceptors/auth.go Normal file
View File

@@ -0,0 +1,116 @@
package interceptors
import (
"context"
"errors"
"log/slog"
"strings"
"connectrpc.com/connect"
"git.kocoder.xyz/kocoded/vt/cmd/mailingest/query"
"git.kocoder.xyz/kocoded/vt/fx/interfaces/stores"
"git.kocoder.xyz/kocoded/vt/model"
"github.com/coreos/go-oidc/v3/oidc"
"gorm.io/gen/field"
"gorm.io/gorm"
)
const tokenHeader = "authentication"
const alternativeTokenHeader = "Authorization"
type AuthenticationInterceptor struct {
verifier *oidc.IDTokenVerifier
sr stores.SessionStore
logger *slog.Logger
db *gorm.DB
}
// WrapStreamingClient implements connect.Interceptor.
func (a *AuthenticationInterceptor) WrapStreamingClient(connect.StreamingClientFunc) connect.StreamingClientFunc {
panic("unimplemented")
}
// WrapStreamingHandler implements connect.Interceptor.
func (a *AuthenticationInterceptor) WrapStreamingHandler(connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
panic("unimplemented")
}
// WrapUnary implements connect.Interceptor.
func (a *AuthenticationInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
return func(
ctx context.Context,
req connect.AnyRequest,
) (connect.AnyResponse, error) {
if req.Spec().IsClient {
panic(errors.New(""))
} else {
authToken := req.Header().Get(tokenHeader)
if authToken == "" {
authToken = req.Header().Get(alternativeTokenHeader)
}
if ok := strings.HasPrefix(authToken, "Bearer "); !ok {
a.logger.Info("No Authtoken with Bearer Prefix provided")
return nil, connect.NewError(
connect.CodeUnauthenticated,
errors.New("No valid token provided"),
)
}
authToken = strings.TrimPrefix(authToken, "Bearer ")
IDToken, err := a.verifier.Verify(context.TODO(), authToken)
if err != nil {
a.logger.Info("No valid/verifyable Authtoken provided", "err", err)
return nil, err
}
session, err := a.sr.GetSessionFromToken(IDToken.Subject)
if err != nil {
q := query.Use(a.db)
u := q.User
claims := model.User{}
if err := IDToken.Claims(&claims); err != nil {
a.logger.Error("Failed to parse claims from IDToken", "err", err)
panic(err)
}
a.logger.Info("Parsed Claims", "claims", claims)
us, err := u.Where(u.Sub.Eq(IDToken.Subject)).Assign(field.Attrs(&claims)).FirstOrCreate()
if err != nil {
a.logger.Error("Failed to get or create user from DB", "err", err, "sub", IDToken.Subject)
return nil, connect.NewError(connect.CodeInternal, errors.New("Failed to create user in DB"))
}
session = &stores.Session{Token: IDToken.Subject, UserID: us.ID, MandantId: 1}
a.sr.AddSession(session)
}
ctx = NewSessionContext(ctx, session)
}
return next(ctx, req)
}
}
func NewOIDCInterceptor(verifier *oidc.IDTokenVerifier, sr stores.SessionStore, logger *slog.Logger, db *gorm.DB) *AuthenticationInterceptor {
return &AuthenticationInterceptor{verifier: verifier, sr: sr, logger: logger, db: db}
}
type key int
var sessionKey key
func NewSessionContext(ctx context.Context, u *stores.Session) context.Context {
return context.WithValue(ctx, sessionKey, u)
}
// FromContext returns the User value stored in ctx, if any.
func SessionFromContext(ctx context.Context) (*stores.Session, bool) {
u, ok := ctx.Value(sessionKey).(*stores.Session)
return u, ok
}