Go web application

Add OneiD to a Go application with coreos/go-oidc, step by step.

View as Markdown

This quickstart adds OneiD to a server web application with coreos/go-oidc. Every file is complete and runs as it is.

Tip Using an AI coding agent? Give it this page as Markdown (add .md to the address) together with the Connector specification. See Build with AI coding agents.

Before you start

  • A OneiD address, for example https://YOUR_ONEID. The code below uses the demonstration instance https://auth.oltinid.com; replace it with your own.
  • A client registered for this application (step 1). The code uses the client ID quickstart; replace it with yours.
  • A user who can sign in to your OneiD. For the demonstration instance, ask for a demo account.

1. Register the application

Ask your OneiD administrator to register a client with these settings, or register it yourself in the admin console. See Register an application.

Setting Value
Client type public (no secret)
Grant types authorization_code (add refresh_token if you request offline_access)
Redirect URI http://localhost:3000/callback
Post-logout redirect URI http://localhost:3000
Allowed scopes openid profile email

2. Install

go get github.com/coreos/go-oidc/v3/oidc golang.org/x/oauth2

3. Add the code

main.go

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"

	"github.com/coreos/go-oidc/v3/oidc"
	"golang.org/x/oauth2"
)

func main() {
	ctx := context.Background()

	// Reads the endpoints and signing keys from OneiD. The issuer name ends with a slash.
	provider, err := oidc.NewProvider(ctx, "https://auth.oltinid.com/")
	if err != nil {
		log.Fatal(err)
	}
	verifier := provider.Verifier(&oidc.Config{ClientID: "quickstart"})

	endpoint := provider.Endpoint()
	endpoint.AuthStyle = oauth2.AuthStyleInParams // a public client: no secret, no Basic header
	config := oauth2.Config{
		ClientID:    "quickstart",
		Endpoint:    endpoint,
		RedirectURL: "http://localhost:3000/callback",
		Scopes:      []string{oidc.ScopeOpenID, "profile", "email"},
	}

	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		state, pkce := oauth2.GenerateVerifier(), oauth2.GenerateVerifier()
		setCookie(w, "state", state)
		setCookie(w, "pkce", pkce)
		http.Redirect(w, r, config.AuthCodeURL(state, oauth2.S256ChallengeOption(pkce)), http.StatusFound)
	})

	http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
		state, _ := r.Cookie("state")
		pkce, _ := r.Cookie("pkce")
		if state == nil || pkce == nil || r.URL.Query().Get("state") != state.Value {
			http.Error(w, "state does not match", http.StatusBadRequest)
			return
		}

		token, err := config.Exchange(ctx, r.URL.Query().Get("code"), oauth2.VerifierOption(pkce.Value))
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadGateway)
			return
		}

		// Check the signature, issuer, audience and expiry of the ID token.
		rawIDToken, _ := token.Extra("id_token").(string)
		idToken, err := verifier.Verify(ctx, rawIDToken)
		if err != nil {
			http.Error(w, err.Error(), http.StatusUnauthorized)
			return
		}

		var claims struct {
			Name  string `json:"name"`
			Email string `json:"email"`
		}
		if err := idToken.Claims(&claims); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		fmt.Fprintf(w, "Hello, %s (%s)", claims.Name, claims.Email)
	})

	log.Println("http://localhost:3000/login")
	log.Fatal(http.ListenAndServe("localhost:3000", nil))
}

func setCookie(w http.ResponseWriter, name, value string) {
	http.SetCookie(w, &http.Cookie{Name: name, Value: value, Path: "/", HttpOnly: true, MaxAge: 600, SameSite: http.SameSiteLaxMode})
}

4. Run it

Run: go run . Then open http://localhost:3000/login.

The example uses a public client so that it runs without a secret. For your own application, use a confidential client and set ClientSecret in the oauth2.Config.

Checkpoint Open http://localhost:3000/login and the browser goes to OneiD. After you sign in, the page shows Hello, followed by the user’s name and, in parentheses, the email address.

Common issues

  • OneiD shows an error page about the redirect address (invalid_request). The redirect URI the code sends is not registered on the client exactly as written. Register it, including scheme and port.
  • invalid_grant from the token endpoint. The code was used before or expired. Start the sign-in again; do not reload the callback page.
  • More errors and fixes: Errors and troubleshooting.

Learn more