handle-auth.go raw

   1  package app
   2  
   3  import (
   4  	"next.orly.dev/pkg/lol/chk"
   5  	"next.orly.dev/pkg/lol/log"
   6  	"next.orly.dev/pkg/nostr/encoders/envelopes/authenvelope"
   7  	"next.orly.dev/pkg/nostr/encoders/envelopes/okenvelope"
   8  	"next.orly.dev/pkg/nostr/encoders/reason"
   9  	"next.orly.dev/pkg/nostr/protocol/auth"
  10  )
  11  
  12  // zeroEventID is used for OK responses when we cannot parse the event ID
  13  var zeroEventID = make([]byte, 32)
  14  
  15  func (l *Listener) HandleAuth(b []byte) (err error) {
  16  	var rem []byte
  17  	env := authenvelope.NewResponse()
  18  	if rem, err = env.Unmarshal(b); chk.E(err) {
  19  		// NIP-42: AUTH messages MUST be answered with an OK message
  20  		// For parse failures, use zero event ID
  21  		log.E.F("%s AUTH unmarshal failed: %v", l.remote, err)
  22  		if writeErr := okenvelope.NewFrom(
  23  			zeroEventID, false, reason.Error.F("failed to parse auth event: %s", err),
  24  		).Write(l); chk.E(writeErr) {
  25  			return writeErr
  26  		}
  27  		return
  28  	}
  29  	defer func() {
  30  		if env != nil && env.Event != nil {
  31  			env.Event.Free()
  32  		}
  33  	}()
  34  	if len(rem) > 0 {
  35  		log.T.F("extra '%s'", rem)
  36  	}
  37  	var valid bool
  38  	if valid, err = auth.Validate(
  39  		env.Event, l.challenge.Load(),
  40  		l.WebSocketURL(l.req),
  41  	); err != nil {
  42  		e := err.Error()
  43  		if err = Ok.Error(l, env, e); chk.E(err) {
  44  			return
  45  		}
  46  		return
  47  	} else if !valid {
  48  		if err = Ok.Error(
  49  			l, env, "auth response event is invalid",
  50  		); chk.E(err) {
  51  			return
  52  		}
  53  		return
  54  	} else {
  55  		if err = okenvelope.NewFrom(
  56  			env.Event.ID, true,
  57  		).Write(l); chk.E(err) {
  58  			return
  59  		}
  60  		log.D.F(
  61  			"%s authed to pubkey %0x", l.remote,
  62  			env.Event.Pubkey,
  63  		)
  64  		l.authedPubkey.Store(env.Event.Pubkey)
  65  
  66  		// Check if this is a first-time user and create welcome note
  67  		go l.handleFirstTimeUser(env.Event.Pubkey)
  68  	}
  69  	return
  70  }
  71  
  72  // handleFirstTimeUser checks if user is logging in for first time and creates welcome note
  73  func (l *Listener) handleFirstTimeUser(pubkey []byte) {
  74  	// Check if this is a first-time user
  75  	isFirstTime, err := l.Server.DB.IsFirstTimeUser(pubkey)
  76  	if err != nil {
  77  		log.E.F("failed to check first-time user status: %v", err)
  78  		return
  79  	}
  80  
  81  	if !isFirstTime {
  82  		return // Not a first-time user
  83  	}
  84  
  85  	// Get payment processor to create welcome note
  86  	if l.Server.paymentProcessor != nil {
  87  		// Set the dashboard URL based on the current HTTP request
  88  		dashboardURL := l.Server.DashboardURL(l.req)
  89  		l.Server.paymentProcessor.SetDashboardURL(dashboardURL)
  90  
  91  		if err := l.Server.paymentProcessor.CreateWelcomeNote(pubkey); err != nil {
  92  			log.E.F("failed to create welcome note for first-time user: %v", err)
  93  		}
  94  	}
  95  }
  96