GoTrue

Endpoints

Every route gt.Mount registers, with its request and response types.

gt.Mount(app, "/auth") registers three groups of routes. All paths below assume the /auth prefix.

Typed JSON endpoints

These bind and validate a request struct, forward it, and pass GoTrue's body back unchanged. The response type is used to document the endpoint.

Method and pathRequest typeResponse typeRate limited
GET /healthnoneHealthResponseno
GET /settingsnoneSettingsResponseno
POST /signupSignupRequestSessionResponseyes
POST /tokenTokenRequestSessionResponseyes
POST /otpOTPRequestMessageResponseyes
POST /verifyVerifyRequestSessionResponseno
POST /recoverRecoverRequestMessageResponseyes
POST /resendResendRequestMessageResponseyes
POST /logoutnoneMessageResponseno
GET /usernoneUserObjectno
PUT /userUpdateUserRequestUserObjectno
POST /reauthenticateReauthenticateRequestMessageResponseno

Sign up

type SignupRequest struct {
    Email    string         `json:"email,omitempty" validate:"omitempty,email"`
    Phone    string         `json:"phone,omitempty"`
    Password string         `json:"password" validate:"required,min=6"`
    Data     map[string]any `json:"data,omitempty"`
}
Terminal
curl -s localhost:8080/auth/signup \
  -H 'Content-Type: application/json' \
  -d '{"email":"ada@example.com","password":"password123","data":{"name":"Ada"}}'

Get a token

The grant is selected with the grant_type query parameter, which is forwarded to GoTrue along with the rest of the query string.

type TokenRequest struct {
    Email        string `json:"email,omitempty" validate:"omitempty,email"`
    Phone        string `json:"phone,omitempty"`
    Password     string `json:"password,omitempty"`
    RefreshToken string `json:"refresh_token,omitempty"`
}
Terminal
# password grant
curl -s 'localhost:8080/auth/token?grant_type=password' \
  -H 'Content-Type: application/json' \
  -d '{"email":"ada@example.com","password":"password123"}'

# refresh grant
curl -s 'localhost:8080/auth/token?grant_type=refresh_token' \
  -H 'Content-Type: application/json' \
  -d '{"refresh_token":"..."}'

A successful response is a SessionResponse with access_token, token_type, expires_in, expires_at, refresh_token, the user object, and weak_password.

Read and update the current user

Both require the caller's bearer token, which the proxy forwards as-is.

Terminal
curl -s localhost:8080/auth/user -H "Authorization: Bearer $TOKEN"

curl -s -X PUT localhost:8080/auth/user \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"data":{"name":"Ada Lovelace"}}'

Redirect endpoints

Some GoTrue endpoints answer with redirects or HTML rather than JSON, so they are proxied byte for byte through a reverse proxy instead of a typed handler.

Method and pathPurpose
GET /authorizeStart an external OAuth flow
GET /callbackExternal provider callback
GET /verifyEmail link verification
GET /oauth/authorizeOAuth2 server authorization

GET /verify and POST /verify are separate routes: the GET form is the link a user clicks in an email and redirects, while the POST form takes a JSON body and returns a session.

Point browsers at these directly:

<a href="/auth/authorize?provider=github">Sign in with GitHub</a>

If the proxy cannot reach GoTrue, these routes return 502 with the standard JSON error body.

OAuth2 server

Mounted unconditionally. GoTrue decides whether the feature is on, and returns its own error when it is not.

Method and pathRequest typeResponse typeRate limited
GET /.well-known/oauth-authorization-servernoneOAuthServerMetadatano
GET /.well-known/openid-configurationnoneOAuthServerMetadatano
GET /.well-known/jwks.jsonnoneJWKSResponseno
GET /oauth/authorizeredirect passthroughno
POST /oauth/tokenOAuthTokenRequestOAuthTokenResponseyes
POST /oauth/clients/registerClientRegistrationRequestClientRegistrationResponseyes
GET /oauth/userinfononeOAuthUserInfono
With GOTRUE_OAUTH_SERVER_ALLOW_DYNAMIC_REGISTRATION=true, anyone who can reach /auth/oauth/clients/register can create an OAuth client. ezz rate limits the route, but if you do not need public registration, turn the setting off in GoTrue.

Passkeys and WebAuthn

Also mounted unconditionally.

Method and pathRequest typeResponse typeRate limited
GET /passkeysnonePasskeyListResponseno
POST /passkeys/registration/optionsPasskeyRegistrationOptionsRequestPasskeyRegistrationOptionsResponseno
POST /passkeys/registration/verifyPasskeyRegistrationVerifyRequestPasskeyRegistrationVerifyResponseno
POST /passkeys/authentication/optionsPasskeyAuthenticationOptionsRequestPasskeyAuthenticationOptionsResponseyes
POST /passkeys/authentication/verifyPasskeyAuthenticationVerifyRequestSessionResponseyes
PATCH /passkeys/{id}PasskeyUpdateRequestPasskeyRegistrationVerifyResponseno
DELETE /passkeys/{id}noneMessageResponseno

The two {id} routes forward the inbound path rather than a fixed one, so the identifier is validated first. It must be a single non-empty segment with no slashes and no dot-dot components; anything else is rejected with 400 before the request leaves your process. Both share one rate-limit bucket per client, keyed by the route template rather than the concrete ID.

Browsers must perform WebAuthn against the public origin, which means GOTRUE_WEBAUTHN_RP_ORIGINS has to include your ezz origin. See Deployment.

Route metadata

Mounted routes carry tags so the docs group them sensibly: Auth for the core endpoints, OAuth for the OAuth2 server, and Passkey for WebAuthn. They are all registered Public() at the ezz layer, since GoTrue performs its own bearer-token checks. They appear in /openapi.json and the Scalar UI next to your own routes.

Next steps

Copyright © 2026