# limen: a plugin-first authentication library for Go with separate modules per method

> limen is a Go authentication library that keeps its core small and ships each authentication method as its own importable module, so a password-login service does not pull in OAuth code it never calls. The core provides interfaces, session management and security primitives, while the database and web framework are yours to bring.

**thecodearcher/limen** — Modern, composable authentication for Go

- Repository: https://github.com/thecodearcher/limen
- Website: https://limenauth.dev
- Stars: 555 · Forks: 28
- Language: Go
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/thecodearcher-limen

## The design decision is that each auth method is its own Go module

limen describes itself as a modular authentication library for Go that takes a plugin-first approach, and the README puts it plainly: the core ships with interfaces, session management and security primitives, while every authentication method lives in its own importable Go module. The stated benefit is that you compose exactly the auth stack your application needs without pulling in code and dependencies you do not use. That is a real answer to a common Go complaint, where a single auth package drags in a provider SDK for a provider you never use. The project is inspired by better-auth, and it targets Go 1.25 or newer. Out of the box it provides credential and password authentication, OAuth 2.0, two-factor authentication, and session management, with the tagline that you bring your own database and your own framework, since Limen adapts to your stack rather than the other way around.

## Installing is one module for the core and one per plugin

The installation section is short because the module boundary is the design. The core comes first:

```bash
go get github.com/thecodearcher/limen
```

Then you add only the adapter and plugins your application needs, and the README gives a GORM adapter and the credential-password plugin as the examples:

```bash
go get github.com/thecodearcher/limen/adapters/gorm
go get github.com/thecodearcher/limen/plugins/credential-password
```

Because the plugins are separate Go modules, adding OAuth later is a separate `go get` rather than an upgrade of an existing dependency, and a build that never imports the OAuth plugin never compiles it. The dependency footprint in go.mod is small as a result, requiring only stretchr/testify for testing and golang.org/x/crypto, plus the indirect yaml and sys packages. That is a much smaller surface than a typical all-in-one auth library.

## The quick start shows a complete server in under forty lines

The README's Quick Start is a full program, and it is short enough to read as the reference:

```go
package main

import (
	"log"
	"net/http"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"

	"github.com/thecodearcher/limen"
	gormadapter "github.com/thecodearcher/limen/adapters/gorm"
	credentialpassword "github.com/thecodearcher/limen/plugins/credential-password"
)

func main() {
	db, err := gorm.Open(postgres.Open("your-dsn"), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
	}

	auth, err := limen.New(&limen.Config{
		BaseURL:  "http://localhost:8080",
		Database: gormadapter.New(db),
		Secret:   []byte("your-32-byte-secret-key-here!!!!"),
		Plugins: []limen.Plugin{
			credentialpassword.New(),
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	mux := http.NewServeMux()
	mux.Handle("/api/auth/", auth.Handler())

	log.Println("listening on :8080")
	log.Fatal(http.ListenAndServe(":8080", mux))
}
```

Several things stand out. The constructor takes a `limen.Config` with a `BaseURL`, a `Database` built by wrapping your GORM handle in the adapter, a `Secret` byte slice, and a `Plugins` slice of `limen.Plugin` values. The plugin list is the composition point: adding a second method means adding another entry, not another configuration key. The handler is then mounted on a plain `http.ServeMux` at `/api/auth/`, which is the whole integration story for the standard library. For Gin or other frameworks, the README points at the `examples/` directory, which contains `basic`, `gin`, `oauth-google` and `two-factor` directories plus an `adapters` directory.

## The secret can come from the environment instead of the struct

One small operational detail is worth pulling out of the README, because it is the kind of thing that ends up in a config file by accident. Alternatively, you can set the `LIMEN_SECRET` environment variable and omit the `Secret` from the struct. That matters for deployments, where a secret literal in the source is a finding waiting to happen, and the example code in the README uses a placeholder string of the right length, which is a reminder that the secret is expected to be a fixed-length key rather than an arbitrary passphrase. The core repository layout shows how much machinery sits behind that one field: `crypto.go` and its test, `cookie_manager.go` for sessions, `account.go` with `account_schema.go` and `account_introspect.go`, `session` handling in `database_actions.go` and `database_helper.go`, `email_verification.go` with its config and challenge files, and `hook.go` for extension points.

## Handlers, middlewares and cache adapters are separate from the auth methods

The repository layout separates the transport layer from the authentication methods, and that is where the composability pays off. `limen_handlers.go` holds the HTTP handlers, `limen_http_core.go` and `http_config.go` and `http_utils.go` cover the HTTP plumbing, and `middlewares.go` handles the framework integration points that a Gin or Echo app would use. The persistence side is abstracted behind `database.go` and its helper and action files, with `cache_adapter.go` and `cache_adapter_memory.go` providing an in-memory cache implementation. The `access/` and `clients/` directories, plus `cmd/` and `cli_config.go`, cover administrative and client-facing surfaces. The practical effect is that swapping GORM for something else touches the adapter layer rather than the auth code, and adding a framework touches the middleware file rather than either.

## The project is young, and it asks you to check the roadmap before contributing

The maintenance picture is one of recent and fast movement. The releases listed are v0.2.3 on 2026-09-25, v0.2.2 on 2026-09-18, and v0.2.1 on 2026-08-13, and the last push was on 2026-09-25. A 0.2 version line means the API is not settled, and for an authentication library that is the kind of thing a team should look at directly rather than take on trust. The README itself sets the expectation for contributors: curious about what is being built, what is open for contribution, and what is out of scope, see ROADMAP.md, and if you are planning to contribute, check there and open an issue first. That file, alongside SECURITY.md, is the honest description of a project that is still designing its plugin surface. The licence is MIT, and the security process asks that a vulnerability be emailed to security@limenauth.dev rather than filed as a public issue, with the disclosure details in SECURITY.md.

## Where limen is the wrong tool

Three limits are worth naming. The first is the age of the plugin ecosystem. The core is version 0.2.x, the releases are weeks apart, and the documentation lives on a separate site rather than in the repository, so the API you read about today may not be the API in the next patch release. The second is that the plugin boundary cuts both ways. If your auth needs are unusual, a session mechanism you did not choose, or a custom token format, you are writing a plugin against a 0.2 interface, and you own the maintenance of it. The third is the comparison with the ecosystem default. A Go service that just needs secure password login and a session cookie can get that from a framework middleware with far fewer moving parts and a decade of production use behind it. limen's case is the opposite case, a multi-tenant application with several auth methods and a desire to own each dependency, where assembling the stack from separate modules is the point rather than a complication.

## The alternative approach is a hosted identity provider, not another Go library

The realistic alternative is not another package but a different ownership model: an external identity provider that issues and validates tokens, with your service only holding a verifier. The difference in approach is where sessions and credentials live. With limen, your service owns the database of accounts, the password hashing, the session cookies and the two-factor secrets, and every plugin is code you deploy and audit. With a hosted provider, that state lives outside your database, your service holds a public key or a shared secret, and there is a network call in the path of every login and often every request. The first is more code and more control, the second is less code and an external dependency with its own cost and availability profile. Between those two ends sit the middle option of a single larger Go auth library that ships every method in one module, which gives you one dependency instead of several plugins but returns you to pulling in providers you do not use.

## Conclusion

limen fits a Go team that wants credential, OAuth and two-factor flows assembled from separate modules, with a database and framework of its own choosing, and that would rather read a plugin's source than inherit a monolithic auth dependency. It is the wrong tool for a service that needs one standard flow handled by a large vendor, since the modules here are young and the plugin surface is still moving, and it is the wrong tool if you cannot accept shipping the auth routes yourself under your own domain. Before you adopt it, read ROADMAP.md as the README asks, since it names what is built, what is open for contribution and what is out of scope, and confirm the v0.2.x line matches the plugin set your application needs.

## FAQ

### How do I install limen for Go?

Run `go get github.com/thecodearcher/limen` for the core, then add the adapter and plugins you need, for example `go get github.com/thecodearcher/limen/adapters/gorm` and `go get github.com/thecodearcher/limen/plugins/credential-password`. Go 1.25 or newer is required.

### What does limen.Plugin do in the configuration?

The `Plugins` field of `limen.Config` takes a list of `limen.Plugin` values, each built by the plugin's own constructor, such as `credentialpassword.New()`. Each authentication method ships as its own Go module, so you only import and compile the ones your application uses.

### How does limen handle the signing secret?

You pass a `Secret` byte slice in the configuration struct, or you set the `LIMEN_SECRET` environment variable and omit the `Secret` from the struct. The README's example uses a 32-byte placeholder string.

### Can limen be used with Gin or another web framework?

The quick start mounts the handler on a standard `http.ServeMux` at `/api/auth/`, and the repository contains a `gin` example directory alongside `basic`, `oauth-google` and `two-factor` examples. The README says to see the examples for a more complete setup with OAuth providers, two-factor auth and Gin integration.

## Sources

- [License: MIT](https://github.com/thecodearcher/limen/blob/master/LICENSE)
- [Project website](https://limenauth.dev)
- [README](https://github.com/thecodearcher/limen/blob/master/README.md)
- [Releases](https://github.com/thecodearcher/limen/releases)
- [thecodearcher/limen on GitHub](https://github.com/thecodearcher/limen)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/thecodearcher-limen
