Commit: Bulk unfinished work
This commit is contained in:
116
interceptors/auth.go
Normal file
116
interceptors/auth.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user