An auth operation is defined once and served over HTTP, GraphQL and Go. Every call runs the same steps in the same order, so no entry point can skip a check.
password.sign-in is defined once. That one definition is POST /auth/password/sign-in over HTTP, a mutation in GraphQL, and pw.SignIn(ctx, …) from Go.
1One operation, three ways in
2Rate limit by client address
3Origin check
4The session, resolved once
5The requirement
6Input and the per-account limit
7One transaction
8Hooks, then the operation
9Commit, then effects
10A refused call stops at the first failure
11The same steps from GraphQL and Go
←→ step · space play
All steps as text
One operation, three ways in. password.sign-in is defined once. That one definition is POST /auth/password/sign-in over HTTP, a mutation in GraphQL, and pw.SignIn(ctx, …) from Go.
Rate limit by client address. A sign-in arrives over HTTP. The first step is the rate limit by client address, so a flood costs one counter increment and nothing else.
Origin check. A mutation that carries a session cookie from an origin that is not the base URL or a trusted origin is refused here, before any lookup, so a cross-site request learns nothing.
The session, resolved once. The session is resolved once per request and shared by every operation in it.
The requirement. Every operation has one. Sign-in is Public; most others are SignedIn, and changing a password or an address is FreshSignIn.
Input and the per-account limit. The input is decoded and the per-account rate limit counted, both before any transaction, so a failed call cannot roll its own count back.
One transaction. A mutation opens one transaction for everything after this point. Data hooks and plugin writes all join it.
Hooks, then the operation. Before hooks run, then the operation, then after hooks. An after hook can still fail the call, and its effects are then dropped.
Commit, then effects. Only after the commit do effects reach the client: authhttp turns "issue session" into a Set-Cookie, or a Set-Auth-Token header for an app in bearer mode.
A refused call stops at the first failure. Another run: a flood of sign-ins from one address. The rate limit refuses it at step 1 with 429 auth.rate_limited and a Retry-After header, and no later step runs.
The same steps from GraphQL and Go. A GraphQL resolver and a Go call run the same operation through the same steps. There is no second copy for the Go API that could forget a check.
The quick start from the README, which the diagram follows:
a,err:=auth.New(auth.WithDB(db,sqlb.PostgresDialect{}),auth.WithBaseURL("https://app.example.com"),auth.WithPlugins(email.New(email.Login(),email.Recover(),email.WithMailer(mailer)),password.New(),),)iferr!=nil{log.Fatal(err)// a broken plugin combination fails here, not in production}mux.Handle("/auth/",authhttp.Handler(a,authhttp.Prefix("/auth")))mux.Handle("/me",authhttp.Middleware(a)(authhttp.RequireUser(meHandler)))
The README sums the pipeline up as rate limits, the CSRF origin check, the session, hooks and one transaction. The full order of the eight steps is in the auth guide Plugins and the pipeline, and the refusal is described in Rate limits.