zitadel/oidc: OpenID Connect Client and Server for Go
Easy to use OpenID Connect client and server library written for Go and certified by the OpenID Foundation
At a glance
- What is it?
- zitadel/oidc is a Go module providing OpenID Connect client and server building blocks, including relying parties, resource servers, and providers. It suits developers who need protocol-level OIDC integration and direct control over provider behavior.
- Who is it for?
- Adopt zitadel/oidc when a Go application needs to build an OpenID Provider or consume OIDC endpoints with control over protocol details. Do not choose it when a fixed identity provider, a general OAuth 2.0-only integration, or a non-Go runtime is the actual requirement.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Where zitadel/oidc Fits in a Go Identity Stack
zitadel/oidc addresses the protocol work required to connect a Go application to OpenID Connect. It covers both sides of that connection. An application can use the relying-party packages to sign users in, exchange authorization codes, retrieve and verify tokens, and request user information. An application acting as a protected API can use the resource-server package to inspect authorization. Developers building an identity service can use the provider package as the starting point for an OpenID Provider.
The target audience is broader than teams looking only for a login SDK. Backend services, web applications, APIs, and identity-service developers all have different entry points in this repository. The examples include a resource server that uses token introspection, a web relying party using authorization code flows, a service demonstrating the JWT Profile Authorization Grant, and provider implementations with a basic login interface.
The README states that the relying-party implementation is certified for the OpenID Foundation's basic and config profiles. That certification is narrower than a claim that every possible OIDC behavior is covered. Application-specific requirements such as account registration, password policy, federation, or production identity administration are not described here. Those concerns cannot be inferred from the protocol library alone.
Relying Party, Resource Server, and Provider Package Boundaries
The repository separates shared OIDC definitions from role-specific implementations. The `pkg/oidc` directory contains definitions used by both clients and servers. A relying party lives under `pkg/client/rp`, where the README describes token retrieval, exchange, and verification. An API that consumes protected resources belongs under `pkg/client/rs`, and provider-side interfaces and implementation live under `pkg/op`.
This separation matters when deciding what an application should import. A web client that initiates user authentication should start with the relying-party API, while an internal API generally needs token validation or introspection behavior rather than browser redirects. A service issuing identity tokens has a different requirement again and should inspect the provider package. Importing the full SDK merely because an application participates in authentication can therefore create unnecessary coupling.
The listed examples map those roles to concrete scenarios. `example/client/api` represents a resource server using token introspection. `example/client/app` demonstrates authorization code flow with code, PKCE, and JWT profile variants. `example/client/github` extends the OAuth 2.0 library with an HTTP client and reusable token source, while `example/client/service` covers the JWT Profile Authorization Grant. On the provider side, `example/server` includes static and dynamic implementations with a basic login UI. These are references for integration design, not documented production deployment packages.
Authorization Code, PKCE, and Discovery Paths
The main end-to-end example follows an authorization code flow between two local processes. The provider publishes discovery information at `/.well-known/openid-configuration`. The web client uses its issuer, client credentials, scopes, and local port to initiate authentication. A successful login redirects back to the client, which then displays user information.
The example describes authorization code flow with code, PKCE, and JWT profile authentication methods. PKCE is also represented in the repository topics and is relevant to public clients because it binds the authorization request to the code exchange through a verifier. The repository does not claim that every OAuth 2.0 grant is equally central, so the JWT Profile example and generic OAuth 2.0 extension should not be mistaken for a complete list of supported flows.
Discovery is the starting point for locating provider endpoints. Based on the documented local address, a client can retrieve configuration before constructing the rest of its OIDC exchange. The repository layout also shows shared protocol definitions under `pkg/oidc`, which avoids duplicating type definitions between the client and server sides. However, the README does not document custom discovery-field behavior or all provider extension points, so those details need confirmation in the package API before an application depends on them.
Running the First zitadel/oidc Client and Provider
The quickest documented first use is the repository's paired example server and web client. It assumes a Go environment capable of building a module that declares `go 1.26.0`. Start the OpenID Provider from the repository context with the command below. Its documented discovery endpoint will be available at `http://localhost:9998/.well-known/openid-configuration`.
go run github.com/zitadel/oidc/v3/example/serverIn a second terminal, start the web client with the exact example credentials and scopes. It listens on port 9999.
CLIENT_ID=web CLIENT_SECRET=secret ISSUER=http://localhost:9998/ SCOPES="openid profile" PORT=9999 go run github.com/zitadel/oidc/v3/example/client/appOpen `http://localhost:9999/login`. The browser is redirected to the provider's login interface. The documented credentials are `test-user@localhost` and `verysecure`; they belong to the example and should not be reused as production credentials. After authentication, the provider redirects to the client application, which displays the user information returned through the OIDC exchange.
Dynamic Issuer Configuration and Local DNS
The repository also provides a dynamic issuer example. It can be started with the following command, after which the same web client can use it as the issuer.
go run github.com/zitadel/oidc/v3/example/server/dynamicThe README presents `oidc.local` as a local development convenience. If that name points to `127.0.0.1` in the hosts file, the web client can be started with the issuer URL below. The command otherwise uses the same client identifier, secret, scopes, and port as the earlier example.
CLIENT_ID=web CLIENT_SECRET=secret ISSUER=http://oidc.local:9998/ SCOPES="openid profile" PORT=9999 go run github.com/zitadel/oidc/v3/example/client/appFor this issuer, the README says usernames are suffixed with the hostname, so the local login becomes `[email protected]` rather than `test-user@localhost`. This distinction illustrates why the issuer and host name matter in the example setup. It also exposes a deployment boundary: the README supplies a hosts-file instruction for local use, but it does not document DNS, TLS, persistent storage, backup, or production scaling for the example provider.
Provider Environment Variables and Example Data
The example provider accepts several environment variables for end-to-end service testing. `PORT` sets its listening port, `REDIRECT_URI` accepts a comma-separated list of allowed redirect URIs, and `USERS_FILE` names a local JSON file containing user data and credentials. These controls make it possible to vary one example without editing the application, but the documentation frames them as capabilities of the example server rather than a complete provider configuration system.
The user file follows the structure used by a default example user. The following is the documented JSON representation for one account.
{
"id2": {
"ID": "id2",
"Username": "test-user2",
"Password": "verysecure",
"FirstName": "Test",
"LastName": "User2",
"Email": "[email protected]",
"EmailVerified": true,
"Phone": "",
"PhoneVerified": false,
"PreferredLanguage": "DE",
"IsAdmin": false
}
}The password is embedded because this is example data. No production guidance about secret storage, password hashing, or account migration is present in the documentation. A production provider based on the package would need a separate design for identity persistence and credential security. The library can reduce protocol implementation work, but it does not turn this sample JSON format into a production directory format.
Logging, OpenTelemetry, and Build-Time Configuration
OpenTelemetry instrumentation is enabled by default in zitadel/oidc. The `no_otel` build tag disables that instrumentation for applications that do not want it or use another instrumentation library. This is an explicit compile-time choice, which is useful when an application's dependency graph or observability policy requires excluding the default integration.
Logging uses Go's global `log/slog` functions and therefore follows `slog.Default()`. Applications that want JSON logs can configure the process-wide default before constructing an OIDC provider or relying party.
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stderr, nil)))To discard OIDC output, install a text handler that writes to `io.Discard`.
slog.SetDefault(slog.New(slog.NewTextHandler(io.Discard, nil)))The logging choice affects the entire process, not only zitadel/oidc. The README also states that `WithLogger`, `WithFallbackLogger`, and related logger parameters remain available for source compatibility but are deprecated. In particular, the provider-side logger options are no-ops, while `rp.WithLogger` still stores a logger for callers of `rp.Logger()`. New integrations should configure `slog.SetDefault` before constructing protocol components. This is a process-wide side effect that can alter logging behavior elsewhere in the same binary.
Versioning, Upgrades, and Apache-2.0 Obligations
The module path is `github.com/zitadel/oidc/v3`, and its `go.mod` requires Go 1.26.0. That floor matters before dependency selection: adopting the module means accepting both the major-version import path and the compiler requirement declared by this repository state. An older organizational Go toolchain cannot consume this version without first meeting that requirement.
The project follows semantic versioning, and the README states that major releases can contain breaking changes. It provides `UPGRADING.md` for migrations beginning with the `v2` to `v3` increment. Recent listed patch releases include `v3.51.14`, `v3.51.13`, and `v3.51.12`, so teams should not assume that moving from one patch release is risk-free simply because the major version is unchanged. Semantic versioning reduces some upgrade ambiguity, but it does not replace reviewing the migration guide and release changes.
The repository is licensed under `Apache-2.0`, with `LICENSE` and `NOTICE` files present. That identifier permits use under the Apache License 2.0 terms, including its conditions and notices; it is not permission to disregard license text or third-party obligations. This review does not offer legal advice. Dependency licenses should be checked separately from the library's own `Apache-2.0` grant.
The repository is not archived. Its last push was on 2026-10-16, and the listed patch releases are from October 2026. That supports a conclusion of recent activity, not a guarantee of compatibility, production readiness, or an uninterrupted release cadence.
Where zitadel/oidc Is the Wrong Level of Abstraction
The main wrong-tool case is a service that needs OAuth 2.0 without OpenID Connect identity semantics. The README says zitadel/oidc reuses or extends `OAuth2 for Go`, but adopting the full OIDC module adds protocol-specific types and provider functionality that may be unnecessary when the application only needs a general OAuth 2.0 client.
A smaller alternative is to use `golang.org/x/oauth2` directly for the narrow OAuth 2.0 operations it already owns. The practical difference is scope: zitadel/oidc adds OpenID Connect discovery, identity-oriented relying-party and provider abstractions, shared OIDC definitions, ID-token verification, and OpenID Provider implementation. The lower-level OAuth 2.0 package leaves the team responsible for assembling the OIDC-specific layer themselves. That may be preferable when a standard library boundary matters, but it also moves more protocol work into the application.
Another mismatch is an application that does not own identity-provider behavior. A team seeking to delegate authentication to an existing service would still need client and relying-party functionality, not necessarily `pkg/op`. Conversely, a team wanting a finished production identity product should not treat `example/server` as one. The README describes a basic login UI and local example users, not production account operations or deployment architecture.
Editorial conclusion
Adopt zitadel/oidc when a Go application needs to build an OpenID Provider or consume OIDC endpoints with control over protocol details. Do not choose it when a fixed identity provider, a general OAuth 2.0-only integration, or a non-Go runtime is the actual requirement. Verify the Go 1.26.0 module requirement, the operation your application needs from the listed packages, and the logging and telemetry defaults before integrating it.
Frequently asked questions
How do I run the first zitadel/oidc client and provider example?
Start `example/server`, then start `example/client/app` with `CLIENT_ID=web`, `CLIENT_SECRET=secret`, `ISSUER=http://localhost:9998/`, `SCOPES="openid profile"`, and `PORT=9999`. Open `http://localhost:9999/login` and use the example account documented in the README.
What Go version does zitadel/oidc v3 require?
The repository's `go.mod` declares `go 1.26.0`. This is the module's stated compiler requirement for this repository.
How can I disable OpenTelemetry in zitadel/oidc?
Build with the `no_otel` build tag. The README says OpenTelemetry is enabled by default and that this tag disables its instrumentation.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/zitadel-oidc)Was this useful?