# Go

> Sign user tokens and verify webhooks in Go with the server-go module.

Source: https://docs.nexafone.so/v1/server/go/

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

:::note[Not published yet]
The Nexafon packages are not on npm, Packagist or PyPI yet, and `go get` finds the Go module only once the Nexafon
repository is public and tagged, so installing them by name fails for now. Until then,
[install them from a checkout of the repo](/v1/quickstart/#install-from-a-checkout). The install commands shown here
are the ones to use after release.
:::

```sh
go get github.com/Keyd-Solutions/nexafon/packages/server-go
```

## Token endpoint

```go title="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)
}
```

:::caution[Send `EmailVerified` as true only for a confirmed address]
Nexafon links a user’s first sign-in to the workspace member with the token’s email only when it is verified. If your
app lets users set an email it has not confirmed, someone could register with a member’s address and act as that
member. Send `true` only when your app has confirmed the address, from your own user record, never as a constant.
:::

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

## Webhook receiver

```go title="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 .`).
