Respond with unauthenticated on compliance page missing membership

Signed-off-by: Émile Ré <emile@getprobo.com>
This commit is contained in:
Émile Ré
2026-03-04 20:25:58 +04:00
parent 3bf6e67729
commit 1af887e4d1
6 changed files with 57 additions and 31 deletions

View File

@@ -673,6 +673,15 @@ func (s AuthService) SendMagicLink(ctx context.Context, req *SendMagicLinkReques
)
}
func (s AuthService) GetMagincLinkEmail(ctx context.Context, tokenString string) (mail.Addr, error) {
payload, err := statelesstoken.ValidateToken[MagicLinkData](s.tokenSecret, TokenTypeMagicLink, tokenString)
if err != nil {
return mail.Nil, NewInvalidTokenError()
}
return payload.Data.Email, nil
}
func (s AuthService) OpenSessionWithMagicLink(ctx context.Context, tokenString string) (*coredata.Identity, *coredata.Session, *string, error) {
var (
now = time.Now()

View File

@@ -40,7 +40,7 @@ func NewGraphQLHandler(iamSvc *iam.Service, trustSvc *trust.Service, esignSvc *e
sessionCookie: authn.NewCookie(&cookieConfig),
},
Directives: schema.DirectiveRoot{
Nda: newNDADirectiveFunc(logger, trustSvc, esignSvc),
Nda: newNDADirective(logger, esignSvc),
Session: session.Directive,
},
}

View File

@@ -16,7 +16,6 @@ package trust_v1
import (
"context"
"errors"
"github.com/99designs/gqlgen/graphql"
"go.gearno.de/kit/log"
@@ -25,12 +24,10 @@ import (
"go.probo.inc/probo/pkg/server/api/authn"
"go.probo.inc/probo/pkg/server/api/compliancepage"
"go.probo.inc/probo/pkg/server/gqlutils"
"go.probo.inc/probo/pkg/trust"
)
func newNDADirectiveFunc(
func newNDADirective(
logger *log.Logger,
trustSvc *trust.Service,
esignSvc *esign.Service,
) func(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
return func(ctx context.Context, obj any, next graphql.Resolver) (any, error) {
@@ -41,24 +38,7 @@ func newNDADirectiveFunc(
membership := compliancepage.ComplianceMembershipFromContext(ctx)
if membership == nil {
// Connected user should always have a membership
logger.ErrorCtx(ctx, "cannot get membership from context")
return nil, gqlutils.Internal(ctx)
}
compliancePage := compliancepage.CompliancePageFromContext(ctx)
if compliancePage == nil {
logger.ErrorCtx(ctx, "cannot get compliance page from context")
return nil, gqlutils.Internal(ctx)
}
if _, err := trustSvc.GetNDAFile(ctx, compliancePage.ID); err != nil {
if errors.Is(err, trust.ErrNDAFileNotFound) {
return next(ctx)
}
logger.ErrorCtx(ctx, "cannot get NDA file", log.Error(err))
return nil, gqlutils.Internal(ctx)
return nil, gqlutils.Unauthenticatedf(ctx, "authentication needed")
}
if membership.ElectronicSignatureID == nil {

View File

@@ -824,9 +824,9 @@ type Query {
type Mutation {
sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload
@session(required: NONE)
@session(required: OPTIONAL)
verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload
@session(required: NONE)
@session(required: OPTIONAL)
requestAllAccesses: RequestAccessesPayload! @session(required: PRESENT) @nda

View File

@@ -2246,9 +2246,9 @@ type Query {
type Mutation {
sendMagicLink(input: SendMagicLinkInput!): SendMagicLinkPayload
@session(required: NONE)
@session(required: OPTIONAL)
verifyMagicLink(input: VerifyMagicLinkInput!): VerifyMagicLinkPayload
@session(required: NONE)
@session(required: OPTIONAL)
requestAllAccesses: RequestAccessesPayload! @session(required: PRESENT) @nda
@@ -4278,7 +4278,7 @@ func (ec *executionContext) _Mutation_sendMagicLink(ctx context.Context, field g
directive0 := next
directive1 := func(ctx context.Context) (any, error) {
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE")
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "OPTIONAL")
if err != nil {
var zeroVal *types.SendMagicLinkPayload
return zeroVal, err
@@ -4341,7 +4341,7 @@ func (ec *executionContext) _Mutation_verifyMagicLink(ctx context.Context, field
directive0 := next
directive1 := func(ctx context.Context) (any, error) {
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "NONE")
required, err := ec.unmarshalNSessionRequirement2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋgqlutilsᚋdirectivesᚋsessionᚐSessionRequirement(ctx, "OPTIONAL")
if err != nil {
var zeroVal *types.VerifyMagicLinkPayload
return zeroVal, err

View File

@@ -210,17 +210,54 @@ func (r *mutationResolver) SendMagicLink(ctx context.Context, input types.SendMa
// VerifyMagicLink is the resolver for the verifyMagicLink field.
func (r *mutationResolver) VerifyMagicLink(ctx context.Context, input types.VerifyMagicLinkInput) (*types.VerifyMagicLinkPayload, error) {
identity, session, continueURL, err := r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
session := authn.SessionFromContext(ctx)
identity := authn.IdentityFromContext(ctx)
email, err := r.iam.AuthService.GetMagincLinkEmail(ctx, input.Token)
if err != nil {
var errInvalidToken *iam.ErrInvalidToken
if errors.As(err, &errInvalidToken) {
return nil, gqlutils.Invalid(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err))
r.logger.ErrorCtx(ctx, "cannot get magic link email", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
var continueURL *string
switch {
case session == nil:
var err error
identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
if err != nil {
var errInvalidToken *iam.ErrInvalidToken
if errors.As(err, &errInvalidToken) {
return nil, gqlutils.Invalid(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
case identity.EmailAddress != email:
if err := r.iam.SessionService.CloseSession(ctx, session.ID); err != nil {
r.logger.ErrorCtx(ctx, "cannot close session", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
var err error
identity, session, continueURL, err = r.iam.AuthService.OpenSessionWithMagicLink(ctx, input.Token)
if err != nil {
var errInvalidToken *iam.ErrInvalidToken
if errors.As(err, &errInvalidToken) {
return nil, gqlutils.Invalid(ctx, err)
}
r.logger.ErrorCtx(ctx, "cannot open session with magic link", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
}
trustCenter := compliancepage.CompliancePageFromContext(ctx)
trustService := r.TrustService(ctx, trustCenter.ID.TenantID())