core

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 16, 2025 License: MIT Imports: 23 Imported by: 0

README

Error response structure

{
  "status": 400,
  "code": "invalid_input", // Machine-readable 
  "message": "The request contains invalid data.", // Human-readable explanation
  "data?": [ // optional details
    {
      "code": "max_length",        // Machine-readable issue type
      "message": "Password exceeds maximum length of 20 characters", // Human-readable explanation
      "param?": "password",          // The param causing the issue (optional if not field-specific)
      "value?": "mypasswordiswaytoolong123", // Optional: the problematic input
    },
    {
      "code": "required",
      "message": "Username is required"
      "param?": "username",
    }
  ]
}

Data response structure

  • prefer flat. maybe data key. TODO = trying to be mostly equssl toi error

    { "status": 200, "code": "invalid_input", // Machine-readable "message": "The request contains invalid data.", // Human-readable explanation "data?": [ // always array { custom data structure

TODO
  • details>message is the UI message shown. Can be dynamicaly created by server.
  • SDK gets the message and description to show in the UI.
  • param can be used to locate position of the message ex in form validation

Documentation

Index

Constants

View Source
const (
	MimeTypeJSON           = "application/json"
	MimeTypeHTML           = "text/html"
	MimeTypeJavaScript     = "application/javascript"
	MimeTypeJavaScriptText = "text/javascript"
)
View Source
const (
	CodeOkEndpoints            = "ok_endpoints"
	CodeOkEndpointsWithoutAuth = "ok_endpoints_without_auth"
)
View Source
const (
	// oks for non precomputed, dynamic auth responses
	CodeOkAuthentication      = "ok_authentication"        // Standard success code for auth
	CodeOkOAuth2ProvidersList = "ok_oauth2_providers_list" // Success code for OAuth2 providers list
)
View Source
const (
	CodeOkAlreadyVerified        = "ok_already_verified"
	CodeOkEmailVerified          = "ok_email_verified"
	CodeOkVerificationRequested  = "ok_verification_requested"    // Success code for email verification request
	CodeOkPasswordResetRequested = "ok_password_reset_requested"  // Success code for password reset request
	CodeOkEmailChange            = "ok_email_change"              // Success code for completed email change
	CodeOkEmailChangeRequested   = "ok_email_change_requested"    // Success code for email change request
	CodeOkPasswordResetNotNeeded = "ok_password_reset_not_needed" // Success code when password reset is not needed
	CodeOkPasswordNotRequired    = "ok_password_not_required"     // Success code when password is not required for auth

	//errors
	CodeErrorTokenGeneration                   = "err_token_generation"
	CodeErrorClaimsNotFound                    = "err_claims_not_found"
	CodeErrorInvalidRequest                    = "err_invalid_input"
	CodeErrorInvalidCredentials                = "err_invalid_credentials"
	CodeErrorPasswordMismatch                  = "err_password_mismatch"
	CodeErrorMissingFields                     = "err_missing_fields"
	CodeErrorPasswordComplexity                = "err_password_complexity"
	CodeErrorEmailConflict                     = "err_email_conflict"
	CodeErrorNotFound                          = "err_not_found"
	CodeErrorEmailVerificationAlreadyRequested = "err_email_verification_already_requested"
	CodeErrorPasswordResetAlreadyRequested     = "err_password_reset_already_requested"
	CodeErrorEmailChangeAlreadyRequested       = "err_email_change_already_requested"
	CodeErrorPasswordResetFailed               = "err_password_reset_failed"
	CodeOkPasswordReset                        = "ok_password_reset"
	CodeErrorRegistrationFailed                = "err_registration_failed"
	CodeErrorTooManyRequests                   = "err_too_many_requests"
	CodeErrorServiceUnavailable                = "err_service_unavailable"
	CodeErrorNoAuthHeader                      = "err_no_auth_header"
	CodeErrorInvalidTokenFormat                = "err_invalid_token_format"
	CodeErrorJwtInvalidSignMethod              = "err_invalid_sign_method"
	CodeErrorJwtTokenExpired                   = "err_token_expired"
	CodeErrorAlreadyVerified                   = "err_already_verified"
	CodeErrorJwtInvalidToken                   = "err_invalid_token"
	CodeErrorJwtInvalidVerificationToken       = "err_invalid_verification_token"
	CodeErrorInvalidOAuth2Provider             = "err_invalid_oauth2_provider"
	CodeErrorOAuth2TokenExchangeFailed         = "err_oauth2_token_exchange_failed"
	CodeErrorOAuth2UserInfoFailed              = "err_oauth2_user_info_failed"
	CodeErrorOAuth2UserInfoProcessingFailed    = "err_oauth2_user_info_processing_failed"
	CodeErrorOAuth2DatabaseError               = "err_oauth2_database_error"
	CodeErrorAuthDatabaseError                 = "err_auth_database_error"
	CodeErrorIpBlocked                         = "err_ip_blocked"
	CodeErrorInvalidContentType                = "err_invalid_content_type"
	CodeErrorUnverifiedEmail                   = "err_unverified_email"
)

Standard response codes

Variables

View Source
var HeadersFavicon = map[string]string{

	"Cache-Control": "public, max-age=86400",
}

HeadersFavicon defines cache headers for favicon.ico. Favicons are often requested frequently and don't change often.

View Source
var HeadersJson = map[string]string{

	"Content-Type": "application/json; charset=utf-8",

	"X-Content-Type-Options": "nosniff",

	"Cache-Control": "no-store, no-cache, must-revalidate",

	"X-Frame-Options": "DENY",

	"Content-Security-Policy": "default-src 'none'; frame-ancestors 'none'",
}

TODO consiten name

View Source
var HeadersMaintenancePage = map[string]string{

	"Cache-Control": "no-store",

	"Retry-After": "600",
}

HeadersMaintenancePage defines essential headers for a maintenance response (typically 503).

View Source
var HeadersTls = map[string]string{

	"Strict-Transport-Security": "max-age=63072000; includeSubDomains",
}

headersTls defines headers related to enforcing TLS usage. These should typically only be applied when the connection is actually over HTTPS.

Functions

func FaviconHandler

func FaviconHandler(w http.ResponseWriter, r *http.Request)

faviconHandler handles requests for /favicon.ico by returning a 204 No Content. This prevents 404 errors in logs for browsers that automatically request it. It avoids serving an actual icon file, keeping the API server focused. Caching headers are applied by the middleware or router configuration.

func GzipMiddleware

func GzipMiddleware(fsys fs.FS) func(http.Handler) http.Handler

TODO cache control headers for assets w.Header().Set("Cache-Control", "public, max-age=86400, immutable") // 1 day w.Header().Set("ETag", "") // Empty since we don't support If-None-Match For caching, we shoudl rely on: - Cache-Control header with long max-age (set elsewhere) - Content-based ETags (hash of file contents) - The immutable nature of embedded assets TODO versioning

  • Embedded assets are versioned with the application
  • No risk of serving stale content
  • Cache busting can be done through URL versioning

TODO no dependency on app.

func PrecomputeBasicResponse

func PrecomputeBasicResponse(status int, code, message string) jsonResponse

ResponseBasicFormat is used for short ok and error responses PrecomputeBasicResponse() will be executed during initialization (before main() runs), and the JSON body will be precomputed and stored in the response variables. the variables will contain the fully JSON as []byte already It avoids repeated JSON marshaling during request handling Any time we use writeJSONResponse(w, response) in the code, it simply writes the pre-computed bytes to the response writer

func SetHeaders

func SetHeaders(w http.ResponseWriter, headers ...map[string]string)

setHeaders applies one or more sets of headers to the response writer. Headers from later maps will overwrite headers from earlier maps if keys conflict.

func StaticHeadersMiddleware

func StaticHeadersMiddleware(next http.Handler) http.Handler

StaticHeadersMiddleware adds cache and security related HTTP headers suitable for static assets served from an embedded filesystem in a production environment (!dev build tag). It differentiates between HTML files (applying specific caching and security headers) and other assets like CSS, JS, images (applying long-term immutable caching).

func ValidateEmail

func ValidateEmail(email string) error

ValidateEmail checks if an email address is valid according to RFC 5322 Returns nil if valid, or an error describing why the email is invalid

func WriteJsonError

func WriteJsonError(w http.ResponseWriter, resp jsonResponse)

writeJsonError writes a precomputed JSON error response

func WriteJsonOk

func WriteJsonOk(w http.ResponseWriter, resp jsonResponse)

For successful precomputed responses

func WriteJsonWithData

func WriteJsonWithData(w http.ResponseWriter, resp JsonWithData)

WriteJsonWithData writes a structured JSON response with the provided data

Types

type App

type App struct {
	// contains filtered or unexported fields
}

app is a service with heavy objects for the handlers. and also a out the box coded endpoints handlers. (methods)

func (*App) Auth

func (a *App) Auth() Authenticator

func (*App) AuthWithOAuth2Handler

func (a *App) AuthWithOAuth2Handler(w http.ResponseWriter, r *http.Request)

AuthWithOAuth2Handler handles OAuth2 authentication Endpoint: POST /auth-with-oauth2 Authenticated: No Allowed Mimetype: application/json

func (*App) AuthWithPasswordHandler

func (a *App) AuthWithPasswordHandler(w http.ResponseWriter, r *http.Request)

AuthWithPasswordHandler handles password-based authentication (login) Endpoint: POST /auth-with-password Authenticated: No Allowed Mimetype: application/json

func (*App) BlockIP

func (a *App) BlockIP(ip string) error

TODO move to middleware BlockIP adds an IP to the blocklist in current and next time bucket with adjusted TTL

func (*App) Cache

func (a *App) Cache() cache.Cache[string, interface{}]

func (*App) Config

func (a *App) Config() *config.Config

func (*App) ConfigStore

func (a *App) ConfigStore() config.SecureStore

func (*App) ConfirmEmailChangeHandler

func (a *App) ConfirmEmailChangeHandler(w http.ResponseWriter, r *http.Request)

func (*App) ConfirmEmailVerificationHandler

func (a *App) ConfirmEmailVerificationHandler(w http.ResponseWriter, r *http.Request)

func (*App) ConfirmPasswordResetHandler

func (a *App) ConfirmPasswordResetHandler(w http.ResponseWriter, r *http.Request)

ConfirmPasswordResetHandler handles password reset confirmation Endpoint: POST /confirm-password-reset Authenticated: No Allowed Mimetype: application/json

func (*App) DbAuth

func (a *App) DbAuth() db.DbAuth

func (*App) DbQueue

func (a *App) DbQueue() db.DbQueue

func (*App) GetClientIP

func (a *App) GetClientIP(r *http.Request) string

GetClientIP extracts the client IP address from the request, handling proxies via configured header

func (*App) IsBlocked

func (a *App) IsBlocked(ip string) bool

IsBlocked checks if an IP is currently blocked

func (*App) ListEndpointsHandler

func (a *App) ListEndpointsHandler(w http.ResponseWriter, r *http.Request)

func (*App) ListOAuth2ProvidersHandler

func (a *App) ListOAuth2ProvidersHandler(w http.ResponseWriter, r *http.Request)

ListOAuth2ProvidersHandler returns available OAuth2 providers Authenticated: No Allowed Mimetype: application/json Example OAuth2 Providers List Response:

{
  "status": 200,
  "code": "ok_oauth2_providers_list",
  "message": "OAuth2 providers list",
  "data": {
    "providers": [
      {
        "name": "google",
        "displayName": "Google",
        "state": "random-state-string",
        "authURL": "https://..."
      }
    ]
  }
}

Endpoint: GET /list-oauth2-providers

func (*App) Logger

func (a *App) Logger() *slog.Logger

func (*App) MetricsHandler

func (a *App) MetricsHandler(w http.ResponseWriter, r *http.Request)

MetricsHandler serves Prometheus metrics in the standard format Endpoint: GET /metrics Authenticated: No Allowed Mimetype: text/plain

func (*App) Notifier

func (a *App) Notifier() notify.Notifier

func (*App) RefreshAuthHandler

func (a *App) RefreshAuthHandler(w http.ResponseWriter, r *http.Request)

RefreshAuthHandler handles explicit JWT token refresh requests Endpoint: POST /auth-refresh Authenticated: Yes Allowed Mimetype: application/json

func (*App) RegisterWithPasswordHandler

func (a *App) RegisterWithPasswordHandler(w http.ResponseWriter, r *http.Request)

RegisterWithPasswordHandler handles password-based user registration with validation Endpoint: POST /register-with-password Authenticated: No Allowed Mimetype: application/json TODO we allow register with password after the user has oauth, we just update the password and do not require validated email as we trust the oauth2 provider if password exist CreateUserWithPassword will succeed but the password will be not updated.

func (*App) RequestEmailChangeHandler

func (a *App) RequestEmailChangeHandler(w http.ResponseWriter, r *http.Request)

RequestEmailChangeHandler handles email change requests Endpoint: POST /api/request-email-change Authenticated: Yes (requires valid auth token) Allowed Mimetype: application/json

func (*App) RequestEmailVerificationHandler

func (a *App) RequestEmailVerificationHandler(w http.ResponseWriter, r *http.Request)

RequestVerificationHandler handles email verification requests Endpoint: POST /request-verification Authenticated: Yes Allowed Mimetype: application/json

func (*App) RequestPasswordResetHandler

func (a *App) RequestPasswordResetHandler(w http.ResponseWriter, r *http.Request)

RequestPasswordResetHandler handles password reset requests Endpoint: POST /request-password-reset Authenticated: No Allowed Mimetype: application/json

Important Security Notes: - Sending emails is an expensive operation and potential spam vector - Rate limiting is enforced via cooldown buckets - Email enumeration is prevented by uniform success responses - Email verification check prevents password reset on unverified accounts

func (*App) Router

func (a *App) Router() router.Router

Router returns the application's router instance

func (*App) SetAuthenticator

func (a *App) SetAuthenticator(auth Authenticator)

SetAuthenticator sets the authenticator implementation

func (*App) SetCache

func (a *App) SetCache(c cache.Cache[string, interface{}])

func (*App) SetConfigProvider

func (a *App) SetConfigProvider(provider *config.Provider)

func (*App) SetConfigStore

func (a *App) SetConfigStore(store config.SecureStore)

func (*App) SetDb

func (a *App) SetDb(dbApp db.DbApp)

SetDb sets the database interfaces for auth and queue

func (*App) SetLogger

func (a *App) SetLogger(l *slog.Logger)

func (*App) SetNotifier

func (a *App) SetNotifier(n notify.Notifier)

func (*App) SetRouter

func (a *App) SetRouter(r router.Router)

func (*App) SetValidator

func (a *App) SetValidator(v Validator)

SetValidator sets the validator implementation

func (*App) Validator

func (a *App) Validator() Validator

Validator returns the validator instance

type AuthData

type AuthData struct {
	TokenType   string     `json:"token_type"`
	AccessToken string     `json:"access_token"`
	Record      AuthRecord `json:"record"`
}

AuthData represents the authentication response structure

func NewAuthData

func NewAuthData(token string, user *db.User) *AuthData

NewAuthData creates a new AuthData instance

type AuthRecord

type AuthRecord struct {
	ID       string `json:"id"`
	Email    string `json:"email"`
	Name     string `json:"name"`
	Verified bool   `json:"verified"`
}

AuthRecord represents the user record in authentication responses

type Authenticator

type Authenticator interface {
	Authenticate(r *http.Request) (*db.User, jsonResponse, error)
}

Authenticator defines the interface for authentication operations

type DefaultAuthenticator

type DefaultAuthenticator struct {
	// contains filtered or unexported fields
}

DefaultAuthenticator implements Authenticator using the standard authentication flow

func NewDefaultAuthenticator

func NewDefaultAuthenticator(dbAuth db.DbAuth, logger *slog.Logger, configProvider *config.Provider) *DefaultAuthenticator

NewDefaultAuthenticator creates a new DefaultAuthenticator instance

func (*DefaultAuthenticator) Authenticate

func (a *DefaultAuthenticator) Authenticate(r *http.Request) (*db.User, jsonResponse, error)

Authenticate implements the Authenticator interface

type DefaultValidator

type DefaultValidator struct{}

DefaultValidator implements the Validator interface

func (*DefaultValidator) ContentType

func (v *DefaultValidator) ContentType(r *http.Request, allowedType string) (jsonResponse, error)

ContentType checks if the request's Content-Type matches the allowed type. Returns: - error (always "Invalid content type" for security) - precomputed jsonResponse for error cases Uses http.StatusUnsupportedMediaType (415) for invalid content types as per HTTP spec.

type JsonBasic

type JsonBasic struct {
	Status  int    `json:"status"`
	Code    string `json:"code"`
	Message string `json:"message"`
}

JsonBasic contains the basic response fields. All responses must have them

type JsonWithData

type JsonWithData struct {
	JsonBasic
	Data interface{} `json:"data,omitempty"`
}

JsonWithData is used for structured JSON responses with data

type OAuth2ProviderInfo

type OAuth2ProviderInfo struct {
	Name                string `json:"name"`
	DisplayName         string `json:"displayName"`
	State               string `json:"state"`
	AuthURL             string `json:"authURL"`
	RedirectURL         string `json:"redirectURL"`
	CodeVerifier        string `json:"codeVerifier,omitempty"`
	CodeChallenge       string `json:"codeChallenge,omitempty"`
	CodeChallengeMethod string `json:"codeChallengeMethod,omitempty"`
}

OAuth2ProviderInfo contains the provider details needed for client-side OAuth2 flow

type OAuth2ProviderListData

type OAuth2ProviderListData struct {
	Providers []OAuth2ProviderInfo `json:"providers"`
}

OAuth2ProviderListData wraps the list of providers for standardized response

type ResponseRecorder

type ResponseRecorder struct {
	http.ResponseWriter
	Status       int       // HTTP status code
	WroteHeader  bool      // Flag to track if headers were written
	BytesWritten int64     // Total bytes written to response
	StartTime    time.Time // When the request started
	RequestID    string    // Optional request ID for tracing
}

ResponseRecorder is a comprehensive recorder that captures various HTTP response metrics

func (*ResponseRecorder) Duration

func (r *ResponseRecorder) Duration() time.Duration

Duration returns the time elapsed since the request started

func (*ResponseRecorder) Write

func (r *ResponseRecorder) Write(b []byte) (int, error)

Write captures bytes written and ensures headers are written first

func (*ResponseRecorder) WriteHeader

func (r *ResponseRecorder) WriteHeader(status int)

WriteHeader captures the status code and marks headers as written

type Validator

type Validator interface {
	// ContentType checks if the request's Content-Type matches the allowed type
	ContentType(r *http.Request, allowedType string) (jsonResponse, error)
}

Validator defines an interface for request validation operations

func NewValidator

func NewValidator() Validator

NewValidator creates a new DefaultValidator instance

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL