The billing service returns invoices on `GET /invoices`. Gin returns 401 unless Authdog authenticates the caller.

Clone [starters/go](https://github.com/authdog/samples/tree/main/starters/go). SDK reference: [Go](/docs/backend/go).

`AttachSession` records context and never fails a request on its own. `RequireAuth` on `GET /invoices` is the identity gate. A signed-in session is not a permission grant. Apply [authorization](/docs/concepts/authorization) after `RequireAuth`.

## What you need

| Requirement | Detail |
| --- | --- |
| Public key | `pk_...` from the [console](https://console.authdog.com) |
| Environment | Copy `.env.example` to `.env` and export `PK_AUTHDOG` |
| Secret key | Never put `sk_...` in the service |
| Package | No tagged versions. `go.mod` in the sample pins a commit. Do not use `@latest` for a reproducible build. Go 1.25+. Gin is the ready adapter. `net/http`, chi, and Echo compose the core functions themselves |
| Request | Hosted sign-in stays in a browser. Call `/invoices` with the `authdog-session` cookie or `Authorization: Bearer` |

## Run

```bash
go mod download
export PK_AUTHDOG=pk_...
go run .
```

Open http://localhost:3000. `curl -i http://localhost:3000/invoices` with no session returns `401`. With a valid session the body includes `inv_90` (Northwind, open) and `inv_91` (Contoso, paid). `GET /logout` expires the local cookie and redirects. It does not revoke a bearer token.

## Invoices

```go
// main.go
ad, err := authdog.New(authdog.Config{PublicKey: publicKey})
if err != nil {
    log.Fatal(err)
}

r := gin.Default()
r.Use(ad.AttachSession())

r.GET("/invoices", ad.RequireAuth(), func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{
        "caller": authdog.FromGin(c).User,
        "invoices": []gin.H{
            {"id": "inv_90", "customer": "Northwind", "total": 1280, "status": "open"},
            {"id": "inv_91", "customer": "Contoso", "total": 640, "status": "paid"},
        },
    })
})
```

`FetchUser: false` makes `RequireAuth` reject every caller. This example does not set that option.
