How Hister Handles Authentication and Authorization: A Technical Deep Dive into the Go Implementation
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, 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.
// 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 and 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. Finally, sessionStore.authenticateUser attaches the numeric user ID to session.Values["user_id"], establishing an authenticated session state.
// 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 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) 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.
// 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, stores only hashed values in the database, and validates sessions using HMAC-based token proofs created with a secretauthKey. - OAuth Integration: External authentication flows through
server/oauth_handler.gousingoauth.NewProviderto support Google, GitHub, and OIDC, with local user records managed viamodel.GetOrCreateUserinserver/model/user.go. - API Authentication: Bearer tokens are supported through
authenticateTokenand verified viatoken_auth_proofsession values. - Authorization Model: Access control relies on explicit ownership checks in handlers (e.g.,
requireUserinserver/api.go) that comparesession.Values["user_id"]against resourceOwnerIDfields, 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, Hister supports Google, GitHub, and generic OpenID Connect (OIDC) providers. The oauth.NewProvider function in 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 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. 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →