Skip to content

The module needs Go 1.22 or later and has no dependencies.

Terminal window
go get github.com/Keyd-Solutions/nexafon/packages/server-go
main.go
func nexafonToken(w http.ResponseWriter, r *http.Request) {
u := signedInUser(r)
if u == nil {
w.WriteHeader(http.StatusUnauthorized)
return
}
if !mayUseNexafon(u) {
w.WriteHeader(http.StatusForbidden)
return
}
// "" signs with NEXAFON_SECRET_KEY (sk_test_… or sk_live_…), which never leaves this server.
// Nexafon links a first sign-in to the member with this email only when it is verified: never send true for
// an address your app has not confirmed, or whoever sets a member's email on your app acts as that member.
token, err := nexafon.CreateUserToken("", nexafon.User{
ID: u.ID, Name: u.Name, Role: "agent", Email: u.Email, EmailVerified: u.EmailVerified,
})
if err != nil {
http.Error(w, "", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/plain")
w.Header().Set("Cache-Control", "no-store")
_, _ = io.WriteString(w, token)
}

CreateUserToken(secretKey, user, opts...): an empty secretKey reads NEXAFON_SECRET_KEY; nexafon.ExpiresIn(d) sets the lifetime (at most 10 minutes).

main.go
func nexafonWebhook(w http.ResponseWriter, r *http.Request) {
// The raw body, exactly as received: parsing it first changes the bytes the signature covers.
body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 1<<20))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}
ev, err := nexafon.VerifyWebhook(r.Header, body, os.Getenv("NEXAFON_WEBHOOK_SECRET"))
if errors.Is(err, nexafon.ErrInvalidWebhook) {
w.WriteHeader(http.StatusBadRequest)
return
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
// Deliveries can repeat: skip an ev.ID you have already handled.
log.Println(ev.Type, ev.ID)
w.WriteHeader(http.StatusNoContent)
}

Pass nexafon.AlsoSecret(previous) while you rotate the secret. The sample is a complete net/http server (go run .).