zitadel/oidc Go SDK

repository·main·Indexed 23 days ago

https://github.com/zitadel/oidc

An OpenID Connect (OIDC) SDK for Go providing both Relying Party (Client) and OpenID Provider (Server) implementations. It supports various flows including Code Flow, Client Credentials, Device Authorization, and Token Exchange, and includes features such as PKCE, JWT Profile, and discovery. The SDK is designed to extend existing Go OAuth2 packages and uses log/slog for logging and OpenTelemetry for instrumentation.

Tokens
6.8K
Snippets
15
Records
45
Agent score
84%

What's inside zitadel/oidc

  1. Overview of the OpenID Connect SDK for Go

    main

    The zitadel/oidc project provides a complete OpenID Connect (OIDC) implementation for Go, supporting both the Relying Party (RP/Client) and the OpenID Provider (OP/Server) roles.

    Core Packages

    • pkg/client/rp: Implementation of an OIDC Relying Party (client).
    • pkg/client/rs: Implementation of an OAuth Resource Server (API).
    • pkg/op: Implementation of an OIDC OpenID Provider (server).
    • pkg/oidc: Shared definitions used by both clients and servers.
  2. Configure logging in the OP package

    main

    The OIDC framework now uses the standard library log/slog for logging.

    • Default Behavior: By default, slog.Default() is used.
    • Configuration: Use the WithLogger() method on the Provider to configure a custom logger.
    • Accessing Logger: The OpenIDProvider and sub-interfaces like Authorizer and Exchanger provide a Logger() method to retrieve the configured logger.
    • Customizing Log Output: You can implement the LogAuthRequest interface on your AuthRequest type. If implemented, the AuthRequest is passed to the logger after an error, using its LogValue() method to control which fields are printed (useful for omitting sensitive data).
  3. Migrate to the global `slog` logger

    main

    The OIDC SDK now uses the standard Go log/slog package for all logging. Instead of configuring loggers on individual OP or RP instances, you must configure the process-wide default slog handler before constructing your OIDC components.

    Key changes:

    • op.WithLogger and op.WithFallbackLogger are now no-ops.
    • op.Provider.Logger() returns slog.Default().
    • rp.WithLogger is deprecated; the RP package no longer writes through the provided logger, instead using the global slog default.
    • Arguments passed to RequestError, WriteError, and TryErrorRedirect are now ignored.
    • Remove dependencies on logging.ToContext and github.com/zitadel/logging middleware. Use your own middleware and slog.Handler for request logging or context enrichment.
  4. Migrate from OIDC v2 to v3

    main

    To upgrade your project from version 2 to version 3 of the OIDC SDK, follow these steps:

    1. Download the latest v3 module: go get -u github.com/zitadel/oidc/v3.
    2. Replace all imports in your Go files from github.com/zitadel/oidc/v2 to github.com/zitadel/oidc/v3.
    3. Run go mod tidy to clean up the module file.

    Note: This migration involves significant breaking changes, including the addition of context.Context to most function signatures and changes to several interfaces becoming struct types.

    go get -u github.com/zitadel/oidc/v3
    find . -type f -name '*.go' | xargs sed -i \
        -e 's/github\.com\/zitadel\/oidc\/v2/github.com\/zitadel\/oidc\/v3/g'
    go mod tidy
  5. Configure logging using slog

    main

    The library uses the standard Go log/slog package for logging. Because the logger is process-wide, you should configure the global slog default before constructing any OIDC provider or relying party.

    To use JSON logging:

    slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stderr, nil)))

    To disable all OIDC logs, set the default logger to a handler that discards output:

    slog.SetDefault(slog.New(slog.NewTextHandler(io.Discard, nil)))

    Note: Existing WithLogger, WithFallbackLogger, and Logger APIs are deprecated and should not be used in new code.

  6. Run the OIDC Quickstart Example

    main

    You can test the full flow by running the provided example server and web client in separate terminals. This demonstrates the authorization code flow with a basic login UI.

    1. Start the OpenID Provider (OP) server:
      go run github.com/zitadel/oidc/v3/example/server
    2. Start the Web Client (RP) in a new terminal:
      CLIENT_ID=web CLIENT_SECRET=secret ISSUER=http://localhost:9998/ SCOPES="openid profile" PORT=9999 go run github.com/zitadel/oidc/v3/example/client/app
    3. Authenticate:
      • Open http://localhost:9999/login in your browser.
      • Use username test-user@localhost and password verysecure to log in.
    # start oidc op server
    go run github.com/zitadel/oidc/v3/example/server
    
    # start oidc web client (in a new terminal)
    CLIENT_ID=web CLIENT_SECRET=secret ISSUER=http://localhost:9998/ SCOPES="openid profile" PORT=9999 go run github.com/zitadel/oidc/v3/example/client/app
  7. Use Response and Redirect for OIDC server responses

    main

    When returning data from op.Server methods, use these two types:

    • op.Response: Used for JSON-based responses. Use op.NewResponse(data) to create one. The Data field is marshaled to JSON in the response body. You can also add custom headers via the Header field.
    • op.Redirect: Used for endpoints that must redirect the user (e.g., Authorize, EndSession). Use op.NewRedirect(url) to create one. This initiates a http.StatusFound redirect.
  8. Implement the Server interface to build an OIDC Provider

    main

    To build an OpenID Connect (OIDC) or OAuth2 Provider, you must implement the op.Server interface. This interface defines the methods required to handle standard OIDC and OAuth2 requests (e.g., Discovery, Authorization, Token exchange, UserInfo).

    Key Implementation Requirements:

    • Forward Compatibility: Implementations MUST embed op.UnimplementedServer to ensure that adding new methods to the interface in the future does not break your implementation.
    • Request Handling: Methods are called after the HTTP route is resolved and the request body is parsed into the Data field of the Request or ClientRequest object. You can assume required fields are already validated according to the relevant standard.
    • Response Formats: Use op.NewResponse(data) for standard JSON responses and op.NewRedirect(url) for redirection responses. While the Data field in Response is any to allow for custom extensions, you should follow the recommended types provided in the method documentation to remain compliant with OIDC/OAuth2 standards.
  9. Configure the OIDC Example Server via Environment Variables

    main

    The example server implementation supports the following environment variables for testing and configuration:

    NameFormatDescription
    PORTNumber (1-65535)OIDC listen port
    REDIRECT_URIComma-separated URIsList of allowed redirect URIs
    USERS_FILEPath to JSON filePath to a local JSON file containing user data and credentials

    User Data Format

    The USERS_FILE should point to a JSON object where keys are user IDs. Example structure:

    {
      "id2": {
        "ID": "id2",
        "Username": "test-user2",
        "Password": "verysecure",
        "FirstName": "Test",
        "LastName": "User2",
        "Email": "test-user2@zitadel.ch",
        "EmailVerified": true,
        "Phone": "",
        "PhoneVerified": false,
        "PreferredLanguage": "DE",
        "IsAdmin": false
      }
    }
  10. Use Request and ClientRequest for OIDC server methods

    main

    The op.Server methods use two primary request wrappers to provide access to HTTP metadata and parsed data:

    1. op.Request[T]: Used for endpoints that do not require client authentication (e.g., Discovery, UserInfo, Introspect). It contains:

      • Method: The HTTP method.
      • URL: The parsed URL.
      • Header: The HTTP headers.
      • Form / PostForm: URL-encoded form values.
      • Data: A pointer to the parsed body/parameters of type T.
    2. op.ClientRequest[T]: A specialized version of op.Request[T] used for endpoints that require client authentication (e.g., CodeExchange, RefreshToken, Revocation). It includes the Client field, which contains the authenticated Client object.

  11. Update Client/Profile TokenSource usage

    main

    The client/profile package introduces a TokenSource interface that extends oauth2.TokenSource with a TokenCtx method to allow explicit context passing.

    Constructors now require a context as the first argument:

    • NewJWTProfileTokenSource
    • NewJWTProfileTokenSourceFromKeyFileData
    • NewJWTProfileTokenSourceFromKeyFile