# coreos/go-oidc: a Go OpenID Connect client built on golang.org/x/oauth2

> The package turns any OIDC discovery endpoint into an oauth2.Config plus a verifier that checks signed ID tokens. It is a client library, not a server, and the README shows the exact four-step flow.

**coreos/go-oidc** — A Go OpenID Connect client.

- Repository: https://github.com/coreos/go-oidc
- Stars: 2,483 · Forks: 437
- Language: Go
- License: Apache-2.0
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/coreos-go-oidc

## What coreos/go-oidc actually solves for a Go service

OAuth 2.0 gives you an access token. It does not tell you who the user is, and it does not give you a signed statement you can check offline against a published key set. OpenID Connect adds that: the token response carries an ID token, a JWT signed by the identity provider, with claims for a stable identifier and, depending on scopes, an email address, a display name and a picture. The README shows the shape of those claims with sub, email, email_verified and name.

The package exists to do two things inside a Go program. It performs OpenID Connect discovery against an issuer URL, and it verifies ID tokens. The README describes it as a client that "enables OpenID Connect support for the golang.org/x/oauth2 package", which is the correct framing: it does not replace your OAuth 2.0 library, it feeds it. The audience is a Go backend that already has a redirect handler and a callback handler and now needs the identity layer on top. The README lists providers across consumer, enterprise and workload categories, including Google, Apple, Microsoft Entra ID, Okta, Auth0, Amazon Cognito, GitHub Actions and Kubernetes service account tokens. That spread is the point of the standard: one client, many issuers.

## Discovery, oauth2.Config and the ID token verifier

The mechanism is small enough to hold in your head. You call oidc.NewProvider with a context and the issuer URL. That fetches the provider's discovery document. From the returned provider you take two things: provider.Endpoint(), which supplies the authorization and token endpoints for an oauth2.Config, and provider.Verifier, which returns an ID token verifier bound to your client ID.

The scopes matter. The README notes that "openid" is required for OpenID Connect flows, and the example adds ScopeProfile and ScopeEmail alongside ScopeOpenID. Without the openid scope you are back to plain OAuth 2.0 and there will be no ID token to verify.

The redirect half is untouched OAuth 2.0. The README states plainly that "OAuth2 redirects are unchanged", and the example builds the authorization URL with oauth2Config.AuthCodeURL(state). On the callback you exchange the code with oauth2Config.Exchange, pull the raw token out with oauth2Token.Extra("id_token"), and hand it to idTokenVerifier.Verify. Only after that does idToken.Claims(&claims) decode the payload into your own struct. The verifier is doing the work that matters: checking the signature against the provider's keys, checking the issuer, and checking that the token was issued for your client ID. Skipping it and decoding the JWT yourself would accept any well-formed token.

## Installing coreos/go-oidc and running the first verification

The module path is github.com/coreos/go-oidc/v3 and the package you import is the oidc subpackage. The go.mod in the repository declares go 1.25.0 and depends on github.com/go-jose/go-jose/v4 v4.1.4 and golang.org/x/oauth2 v0.36.0. Add it with go get:

```bash
go get github.com/coreos/go-oidc/v3/oidc
```

Then construct the provider and the OAuth 2.0 config. This block is the README's initialization example, and the thing to notice is that Endpoint comes from the provider rather than from hand-written URLs, so a provider that moves its endpoints does not break you:

```go
provider, err := oidc.NewProvider(ctx, "https://accounts.google.com")
if err != nil {
    // handle error
}

oauth2Config := oauth2.Config{
    ClientID:     clientID,
    ClientSecret: clientSecret,
    RedirectURL:  redirectURL,
    Endpoint:     provider.Endpoint(),
    Scopes:       []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail},
}

idTokenVerifier := provider.Verifier(&oidc.Config{ClientID: clientID})
```

On the callback, exchange the code and verify the token before you trust any claim in it. The README's callback example extracts the raw token from the oauth2 token's extra fields, verifies it, and only then decodes claims:

```go
oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code"))
if err != nil {
    // handle error
}

rawIDToken, ok := oauth2Token.Extra("id_token").(string)
if !ok {
    // handle missing token
}

idToken, err := idTokenVerifier.Verify(ctx, rawIDToken)
if err != nil {
    // handle error
}

var claims struct {
    Email         string `json:"email"`
    EmailVerified bool   `json:"email_verified"`
    Name          string `json:"name"`
    Picture       string `json:"picture"`
}
if err := idToken.Claims(&claims); err != nil {
    // handle error
}
```

What you should see on a successful run is a populated claims struct. The example directory in the repository has idtoken, logout and userinfo subdirectories with their own README, which is where to look for a runnable version of this rather than copying the snippets above verbatim.

## Where coreos/go-oidc stops: no provider, no sessions, no middleware

This is a client library. The README's own list of supported providers is a list of things it talks to, not things it is. There is no OpenID Provider implementation in the repository, so if you are building the identity side rather than consuming it, this is the wrong project and the search phrase "go oidc server" is not what this codebase addresses.

Two consequences follow. First, the package does not manage sessions. It verifies a token and gives you claims; what you do with them, whether you set a cookie, mint your own session token, or keep the ID token in memory, is entirely your code. Second, there is no middleware. A search for "go oidc middleware" will not land on anything in this repository, and the top-level entries are documentation, licensing and the oidc package itself, not a net/http handler chain.

There is also a network dependency at startup. oidc.NewProvider fetches the discovery document, and verification needs the provider's signing keys. A service that cannot reach the issuer at process start will fail at that call, and a service that caches nothing will pay for key lookups on the verification path. The README does not document a key cache, a refresh interval, or an offline mode. Treat key retrieval as a runtime dependency you have to reason about in your own deployment, not something the library hides from you.

## How it compares with golang.org/x/oauth2 alone and with a hosted identity platform

The nearest alternative is not another Go library, it is doing nothing extra: use golang.org/x/oauth2 by itself and treat the access token as proof of identity. That works until it does not. An access token is opaque to your service unless you call the provider's userinfo endpoint on every request, and it carries no signed claim set you can validate locally. go-oidc exists precisely to add that signed layer, and the README's framing that it "enables OpenID Connect support" for the oauth2 package is a statement about composition, not competition. If you already vendor golang.org/x/oauth2, you are adding one dependency, not switching stacks.

The other direction is a hosted identity platform such as Okta, Auth0 or Microsoft Entra ID. Those are the issuers, not the client. Choosing one of them does not remove the need for something in your Go service that performs discovery and verifies the token, which is the role this package fills. The distinction that matters when you are comparing options is whether you want to run identity yourself or consume someone else's, and go-oidc only participates in the second case. It is also worth noting what the README claims about adoption: it says the package is imported by over 1000 open source projects and links to the pkg.go.dev imported-by page. That is a signal about the API surface being stable enough to depend on, not a measure of whether it fits your architecture.

## Maintenance, versioning and what the Apache-2.0 licence means here

The repository is not archived, and the last push was on 2026-09-09. The most recent release listed is v3.21.0 on 2026-09-01, preceded by v3.20.0 on 2026-07-08 and v3.19.0 on 2026-06-17. The default branch is named v3, and the module path carries the /v3 suffix, which is how Go modules encode a major version. Practically, that means upgrades within v3 are minor-version bumps and the import path stays github.com/coreos/go-oidc/v3/oidc. A future v4 would change the import path and would be a deliberate edit in your go.mod rather than a silent update.

The licence is Apache-2.0, and the repository carries LICENSE and NOTICE files at the top level. Apache-2.0 is a permissive licence that includes an explicit patent grant and requires attribution and retention of notices. This is not legal advice, and if you redistribute the library or a derivative, the NOTICE file is the thing your own legal review will want to look at. The dependency chain is short: go-jose for JOSE and golang.org/x/oauth2. A short chain is a smaller upgrade surface, but go-jose is the component doing signature verification, so its version is the one to watch when you audit.

The upgrade cost is dominated by the Go toolchain floor, not by API churn. The repository's go.mod declares go 1.25.0, so a project on an older toolchain will need to move before it can take current releases.

## Conclusion

Adopt coreos/go-oidc if your Go service already speaks OAuth 2.0 through golang.org/x/oauth2 and you need a signed ID token to identify a user or a workload; the discovery step and the verifier are the whole job. Do not adopt it if you need an OpenID Provider, a session store, or a middleware chain, because the repository contains none of those. Before writing code, verify that your issuer publishes a discovery document, that its signing keys are reachable from your environment, and that the module path you import is github.com/coreos/go-oidc/v3/oidc, since the default branch is v3 and the older import path is not what the README shows.

## FAQ

### What does OIDC stand for?

OpenID Connect. The README describes it as a specification on top of OAuth 2.0 that adds a provider-agnostic API for user attributes, delivered as a signed JWT with standard claims.

### Is OIDC the same as OAuth?

No. OAuth 2.0 is the flow, and OpenID Connect layers on top of it so the response includes a JWT signed by the identity provider with claims such as sub, email and name. The README notes that OAuth2 redirects are unchanged.

### Can coreos/go-oidc act as an OpenID Provider instead of a client?

No. The README calls it an OpenID Connect client, and the repository contains an oidc package plus examples for idtoken, logout and userinfo, with no provider implementation.

### Which import path should a Go project use for coreos/go-oidc?

The module is github.com/coreos/go-oidc/v3 and the package imported in the README's examples is github.com/coreos/go-oidc/v3/oidc. The /v3 suffix matches the default branch name and the major version.

### Does coreos/go-oidc handle sessions or HTTP middleware?

The README documents discovery, an oauth2.Config built from provider.Endpoint(), and ID token verification. It does not document session storage or a middleware chain, so both remain your application's responsibility.

## Sources

- [coreos/go-oidc on GitHub](https://github.com/coreos/go-oidc)
- [Issues](https://github.com/coreos/go-oidc/issues)
- [License: Apache-2.0](https://github.com/coreos/go-oidc/blob/v3/LICENSE)
- [README](https://github.com/coreos/go-oidc/blob/v3/README.md)
- [Releases](https://github.com/coreos/go-oidc/releases)

---

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