# How Hister Handles Authentication and Authorization: A Technical Deep Dive into the Go Implementation

> Explore Hister's Go implementation of cookie-based authentication with HMAC tokens and OAuth 2.0 integration. Learn how it enforces authorization via ownership checks.

- Repository: [Adam Tauber/hister](https://github.com/asciimoo/hister)
- Tags: deep-dive
- Published: 2026-09-01

---

**Hister implements a lightweight, cookie-based session authentication system using HMAC-verified tokens, supplemented by OAuth 2.0 integration for external identity providers, while enforcing authorization through explicit ownership checks in API handlers.**

Hister is an open-source Go application that demonstrates practical patterns for securing web APIs without heavy frameworks. Understanding how Hister handles authentication and authorization reveals a minimalist yet secure approach that combines cryptographically secure session tokens, proof-based verification, and resource-level access control.

## Session-Based Authentication Architecture

Hister establishes user identity through persistent sessions stored in secure cookies. When a user first accesses the application, the server generates a cryptographically random token and establishes a session record.

### Secure Token Generation and Storage

In [`server/session.go`](https://github.com/asciimoo/hister/blob/main/server/session.go), the `newSessionToken` function generates a 32-byte random token for each new session. Rather than storing the raw token in the database, Hister uses `sessionTokenHash` to store only the hashed value, ensuring that a database compromise does not expose active session tokens. The cookie containing the original token is then transmitted to the client and validated on every subsequent request via `validSessionToken`.

```go
// server/session.go
token, _ := newSessionToken()                     // generates a 32-byte random token
hashed := sessionTokenHash(token)               // store only the hash in DB
session.Values["user_id"] = user.ID             // attach authenticated user
session.Save(r, w)                              // cookie sent to client

```

### Session Validation with HMAC Proofs

To verify that a client possesses the original token without transmitting it repeatedly, Hister implements a token proof mechanism. The `tokenProof` function creates an HMAC of the token using a secret `authKey` derived from the server’s configuration. This proof is stored in the session and checked using `tokenAuthenticated` for subsequent requests, preventing session fixation and replay attacks.

## OAuth 2.0 Integration for External Identity Providers

Hister supports external authentication through Google, GitHub, and generic OpenID Connect providers, implemented primarily in [`server/oauth_handler.go`](https://github.com/asciimoo/hister/blob/main/server/oauth_handler.go) and [`server/oauth/providers.go`](https://github.com/asciimoo/hister/blob/main/server/oauth/providers.go).

### Authorization Flow Implementation

The OAuth flow begins when `oauth.NewProvider` looks up the requested provider configuration and builds an authorization redirect URL. After the provider redirects back to the callback endpoint, the handler exchanges the authorization code for an access token, retrieves the user profile, and creates or updates a local record via `model.GetOrCreateUser` in [`server/model/user.go`](https://github.com/asciimoo/hister/blob/main/server/model/user.go). Finally, `sessionStore.authenticateUser` attaches the numeric user ID to `session.Values["user_id"]`, establishing an authenticated session state.

```go
// server/oauth_handler.go (simplified)
func (c *Controller) OAuthCallback(ctx *gin.Context) {
    // ... exchange auth code for token ...
    userInfo, _ := provider.GetUserInfo(ctx, token)
    user, _ := model.GetOrCreateUser(userInfo.Email) // local user record
    sessionStore.authenticateUser(session, user.ID)   // log the user in
    // redirect back to UI
}

```

### API Bearer Token Authentication

For programmatic API access, Hister uses `authenticateToken` to validate bearer tokens. Upon successful validation, the system stores a `token_auth_proof` in the session values under the key `token_auth_proof`, which subsequent middleware checks via `tokenAuthenticated` to verify the request’s legitimacy without re-querying the external provider.

## Authorization Through Ownership Verification

Authorization in Hister is enforced at the endpoint level through explicit ownership checks rather than middleware-based role systems. Handlers in [`server/api.go`](https://github.com/asciimoo/hister/blob/main/server/api.go) such as the update endpoint (`/api/update`) extract the `user_id` from the session and compare it against the resource’s `OwnerID`.

### Resource-Level Access Control

The `requireUser` helper (implemented in [`server/api.go`](https://github.com/asciimoo/hister/blob/main/server/api.go)) retrieves the current user from the session and validates ownership of the requested resource. If the `OwnerID` does not match the authenticated `user_id`, the handler aborts with `http.StatusForbidden`. This pattern appears consistently across mutation routes including the history endpoint and document updates.

```go
// server/api.go pattern (UpdateDocument example)
func (c *Controller) UpdateDocument(ctx *gin.Context) {
    sess, _ := sessionStore.Get(ctx.Request, "hister")
    userID, ok := sess.Values["user_id"].(uint)
    if !ok {
        ctx.AbortWithStatus(http.StatusUnauthorized)
        return
    }
    doc, _ := model.GetDocument(req.ID)
    if doc.OwnerID != userID {
        ctx.AbortWithStatus(http.StatusForbidden) // not authorized
        return
    }
    // ... perform update ...
}

```

## Summary

- **Session Security**: Hister generates 32-byte random tokens in [`server/session.go`](https://github.com/asciimoo/hister/blob/main/server/session.go), stores only hashed values in the database, and validates sessions using HMAC-based token proofs created with a secret `authKey`.
- **OAuth Integration**: External authentication flows through [`server/oauth_handler.go`](https://github.com/asciimoo/hister/blob/main/server/oauth_handler.go) using `oauth.NewProvider` to support Google, GitHub, and OIDC, with local user records managed via `model.GetOrCreateUser` in [`server/model/user.go`](https://github.com/asciimoo/hister/blob/main/server/model/user.go).
- **API Authentication**: Bearer tokens are supported through `authenticateToken` and verified via `token_auth_proof` session values.
- **Authorization Model**: Access control relies on explicit ownership checks in handlers (e.g., `requireUser` in [`server/api.go`](https://github.com/asciimoo/hister/blob/main/server/api.go)) that compare `session.Values["user_id"]` against resource `OwnerID` fields, rejecting unauthorized requests with HTTP 403.

## Frequently Asked Questions

### How does Hister securely store session tokens?

Instead of storing raw session tokens in the database, Hister uses `sessionTokenHash` to store only cryptographic hashes of the tokens generated by `newSessionToken`. The actual token exists only in the client’s secure cookie, and the server validates possession through HMAC proofs created by `tokenProof` using a secret `authKey`.

### Which OAuth 2.0 providers does Hister support?

According to the source code in [`server/oauth/providers.go`](https://github.com/asciimoo/hister/blob/main/server/oauth/providers.go), Hister supports Google, GitHub, and generic OpenID Connect (OIDC) providers. The `oauth.NewProvider` function in [`server/oauth_handler.go`](https://github.com/asciimoo/hister/blob/main/server/oauth_handler.go) handles the provider lookup and initiates the authorization code flow for each supported identity provider.

### How does Hister authorize access to protected resources?

Hister implements authorization through explicit ownership checks in API handlers. Functions like `requireUser` in [`server/api.go`](https://github.com/asciimoo/hister/blob/main/server/api.go) extract the `user_id` from the session and compare it against the target resource’s `OwnerID` field. If the IDs do not match, the handler returns `http.StatusForbidden`, ensuring users can only modify their own resources.

### Can Hister authenticate API requests using bearer tokens?

Yes, Hister supports bearer token authentication for API access through the `authenticateToken` function in [`server/session.go`](https://github.com/asciimoo/hister/blob/main/server/session.go). Upon successful validation, the system stores a `token_auth_proof` in the session, which subsequent requests verify using `tokenAuthenticated` to confirm the client’s identity without requiring interactive OAuth flows.