A modular OAuth 2.0 / OpenID Connect server library for Go, structured around RFC-named packages. Each package implements a single specification and can be composed independently — bring only the grants and extensions you need.
srv := authlib.NewServer()
srv.RegisterGrant(authCodeFlow)
srv.RegisterEndpoint(introspectionFlow)
srv.CreateAuthorizationResponse(r, w, user) // GET /authorize
srv.CreateTokenResponse(r, w) // POST /tokenMost Go teams building an authorization server end up in one of two places: hand-rolling RFC 6749 against the spec text, or pulling in a full identity platform when all they needed was the protocol layer. Authlib sits in between.
- RFC-scoped packages, not a monolith.
rfc6749,rfc7636,rfc7662,rfc9068each do one thing and can be imported independently. You are not forced into a full OP/RP object graph to get PKCE validation. - Config + Flow pattern. Every grant is built the same way —
NewConfig()→ set dependencies →Must()— so once you've learned one flow, you've learned them all. - Extensions instead of inheritance. PKCE and OIDC ID Token issuance are plain extension interfaces (
AuthorizationRequestValidator,TokenProcessor, etc.) registered onto a base flow, rather than subclassed or forked flow implementations. - You own storage. Authlib defines the manager interfaces (
ClientManager,AuthCodeManager,TokenManager, ...); you implement them against whatever you already use. A reference SQL implementation is included inintegrations/sql. - Fails fast, not at runtime.
Must()validates required dependencies at construction time, so a missingTokenManageris a startup error, not a 500 in production.
- Go 1.23+
go get github.com/tniah/authlib| Specification | Package | Description |
|---|---|---|
| RFC 6749 §4.1 | rfc6749/authorization_code |
Authorization Code Grant |
| RFC 6749 §4.3 | rfc6749/ropc |
Resource Owner Password Credentials |
| RFC 6749 §4.4 | rfc6749/client_credentials |
Client Credentials Grant |
| RFC 6749 §2.3 | rfc6749/client_authentication |
Client authentication (client_secret_basic, client_secret_post, none) |
| RFC 6749 | rfc6749/code_generator |
Authorization code generation |
| RFC 6750 | rfc6750 |
Bearer Token (opaque access + refresh) |
| RFC 7636 | rfc7636 |
PKCE (Proof Key for Code Exchange) |
| RFC 7662 | rfc7662 |
Token Introspection |
| RFC 9068 | rfc9068 |
JWT Access Tokens |
| OpenID Connect | oidc/core/authorization_code |
ID Token generation |
Server is the central dispatcher. Register grant flows and endpoints before use, then call its handler methods from your HTTP routes.
srv := authlib.NewServer()
srv.RegisterGrant(authCodeFlow)
srv.RegisterGrant(ropcFlow)
srv.RegisterEndpoint(introspectionFlow)
// In your HTTP handlers:
srv.CreateAuthorizationResponse(r, w, user) // GET /authorize
srv.CreateConsentResponse(r, w, user) // POST /authorize (consent step)
srv.CreateTokenResponse(r, w) // POST /token
srv.EndpointResponse(r, w, "introspection") // POST /introspectFor finer control, use the split validate/respond methods to inspect a request before committing a response:
grant, req, err := srv.ValidateAuthorizationRequest(r, user)
grant, req, err := srv.ValidateConsentRequest(r, user)
grant, req, err := srv.ValidateTokenRequest(r)Every flow follows the same Config + Flow pattern:
// 1. Build config
cfg := authorizationcode.NewConfig().
SetClientManager(myClientManager).
SetAuthCodeManager(myAuthCodeManager).
SetTokenManager(myTokenManager)
// 2. Register optional extensions (PKCE, OIDC, etc.)
cfg.RegisterExtension(pkceFlow)
cfg.RegisterExtension(oidcFlow)
// 3. Instantiate — validates config, fails fast on missing dependencies
flow, err := authorizationcode.Must(cfg)
// 4. Register with server
srv.RegisterGrant(flow)A single object can implement multiple extension interfaces and be registered once via RegisterExtension. Extensions are called in registration order.
| Interface | Called when |
|---|---|
AuthorizationRequestValidator |
Validating /authorize request |
ConsentRequestValidator |
Validating the consent step |
AuthCodeProcessor |
Before saving the authorization code |
TokenRequestValidator |
Validating /token request |
TokenProcessor |
Before writing the token response |
PKCE (rfc7636) and OIDC (oidc/core/authorization_code) are implemented as extensions and plug into the Authorization Code flow via RegisterExtension — a useful pattern to follow if you need to add custom claims, audience restriction, or rate limiting at the same integration points.
import (
"github.com/tniah/authlib"
authorizationcode "github.com/tniah/authlib/rfc6749/authorization_code"
"github.com/tniah/authlib/rfc7636"
oidcflow "github.com/tniah/authlib/oidc/core/authorization_code"
)
// PKCE — plain and S256 both accepted by default (RFC 7636).
// To enforce S256-only per RFC 9700 §2.1, set AllowPlain to false.
pkce := rfc7636.New(
rfc7636.NewOptions().SetAllowPlain(false),
)
// OIDC ID Token
oidc, _ := oidcflow.Must(
oidcflow.NewConfig().
SetIssuer("https://auth.example.com").
SetSigningKey(privateKey, jwt.SigningMethodRS256, "key-1"),
)
// Authorization Code flow
flow, _ := authorizationcode.Must(
authorizationcode.NewConfig().
SetClientManager(clientMgr).
SetAuthCodeManager(authCodeMgr).
SetTokenManager(tokenMgr).
RegisterExtension(pkce).
RegisterExtension(oidc),
)
srv := authlib.NewServer()
srv.RegisterGrant(flow)import (
"github.com/tniah/authlib"
"github.com/tniah/authlib/rfc6749/ropc"
)
flow, _ := ropc.Must(
ropc.NewConfig().
SetClientManager(clientMgr).
SetUserManager(userMgr).
SetTokenManager(tokenMgr),
)
srv := authlib.NewServer()
srv.RegisterGrant(flow)import (
"github.com/tniah/authlib"
clientcredentials "github.com/tniah/authlib/rfc6749/client_credentials"
)
flow, _ := clientcredentials.Must(
clientcredentials.NewConfig().
SetClientManager(clientMgr).
SetTokenManager(tokenMgr),
)
srv := authlib.NewServer()
srv.RegisterGrant(flow)import "github.com/tniah/authlib/rfc9068"
jwtGen, _ := rfc9068.MustJWTAccessTokenGenerator(
rfc9068.NewGeneratorConfig().
SetIssuer("https://auth.example.com").
SetAudience("https://api.example.com").
SetSigningKey(privateKey, jwt.SigningMethodRS256, "key-1"),
)import "github.com/tniah/authlib/rfc7662"
introspection, _ := rfc7662.MustTokenIntrospectionFlow(
rfc7662.NewConfig().
SetClientManager(clientMgr).
SetTokenManager(tokenMgr),
)
srv.RegisterEndpoint(introspection)
// Handle: POST /introspect
srv.EndpointResponse(r, w, "introspection")srv.RegisterErrorHandler(func(r *http.Request, w http.ResponseWriter, err error) error {
// custom logging, formatting, etc.
return nil
})Implement the interfaces in the models package with your own data layer. See models/README.md for the full interface reference and integrations/sql/ for example SQL-backed implementations.
| Package | README |
|---|---|
rfc6749/authorization_code |
README |
rfc6749/ropc |
README |
rfc6749/client_credentials |
README |
rfc6749/client_authentication |
README |
rfc6749/code_generator |
README |
rfc6750 |
README |
rfc7636 |
README |
rfc7662 |
README |
rfc9068 |
README |
models |
README |
integrations/sql |
README |
utils |
README |
# All tests
go test ./...
# Specific package
go test ./rfc6749/authorization_code/...
# Single test
go test -run TestFunctionName ./path/to/package/...Mocks are generated with mockery. Configuration is in .mockery.yaml.
mockeryIssues and pull requests are welcome — especially around new RFC coverage (e.g. RFC 8628 Device Authorization Grant, RFC 7009 Token Revocation), storage backend examples beyond SQL, and real-world usage reports. If you're evaluating Authlib for a project, opening an issue with your use case helps prioritize the roadmap even if you don't send code.
BSD-3-Clause