<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/building-an-oidc-provider-from-scratch-in-go-apjugsw4x" -->

---
title: Building an OIDC Provider from Scratch in Go | daily.dev
description: A developer shares their experience building a production-grade OIDC Provider in Go from scratch, using hexagonal architecture and multiple AI coding...
canonical: https://daily.dev/posts/building-an-oidc-provider-from-scratch-in-go-apjugsw4x
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Building an OIDC Provider from Scratch in Go | daily.dev
og:description: A developer shares their experience building a production-grade OIDC Provider in Go from scratch, using hexagonal architecture and multiple AI coding...
og:url: https://daily.dev/posts/building-an-oidc-provider-from-scratch-in-go-apjugsw4x
og:image: https://api.daily.dev/og/posts/APjUGsW4x.png
og:image:alt: Building an OIDC Provider from Scratch in Go
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Building an OIDC Provider from Scratch in Go

**[Medium](https://daily.dev/sources/medium_js)** · 23 min read · 1 upvotes · 0 comments

## Summary

A developer shares their experience building a production-grade OIDC Provider in Go from scratch, using hexagonal architecture and multiple AI coding assistants (Claude Code, Codex GPT-5.5, Gemma 4, Fable). The post covers the full OIDC authorization code flow with PKCE, CSRF token handling, token lifecycle management, and Supabase integration. Key lessons include: AI lacks design intent behind protocols (illustrated by a missed CSRF token bug), multiple AI models provide complementary perspectives, and humans must stay in the review loop. The architecture uses CQRS-style Use Cases mapped to OIDC endpoints, with typed value objects and a clean separation of infrastructure from domain logic. Testing strategy spans unit tests, E2E shell scripts, smoke/stress tests, Playwright via MCP in Docker, and OIDC conformance tests.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://medium.com/@siwench/building-an-oidc-provider-from-scratch-in-go-dcedc1c44ad0>

## Questions this post answers

### How do I integrate a custom OIDC provider with Supabase Auth using Supabase CLI v2?

Supabase CLI v2.x config.toml does not support a generic OIDC provider directly, and calling signInWithOAuth with provider 'oidc' returns a 400 error. The workaround is to configure the provider as if it were Keycloak, enabling [auth.external.keycloak] in config.toml and implementing Keycloak-compatible endpoints (/protocol/openid-connect/auth, /certs, /token) plus the standard OIDC discovery and JWKS endpoints on the custom provider.

_Developers wiring custom identity providers into Supabase can find integration quirks like this one on daily.dev._

### What is the difference between the OAuth state parameter and a login CSRF token in an OIDC authorization flow?

State ensures the redirect from the OIDC provider back to the client's callback endpoint comes from the original client request, and is verified by the client-side backend. A login CSRF token, generated by the OIDC provider itself in the /authorize step and embedded as a hidden form field, instead ensures that a sign-in form submission actually came from the provider-rendered page, protecting a different trust boundary than state or nonce.

_Anyone implementing sign-in flows can dig into distinctions like state versus login CSRF tokens on daily.dev._

### Why would a refresh token flow fail id_token nonce validation in Supabase GoTrue with a custom OIDC provider?

The refresh token flow does not include a nonce in the id_token, which is correct per the OIDC specification, but Supabase validates the nonce by default and will reject such tokens. Setting skip_nonce_check = true under [auth.external.keycloak] in config.toml disables that check so refresh-token-issued id_tokens pass verification.

_Teams debugging Supabase OIDC nonce errors can track fixes like this on daily.dev._

## Similar posts on daily.dev

- [Medium](https://daily.dev/posts/medium-tpgjrsou1) · Medium · 1 upvotes · 0 comments
- [Custom OIDC Providers for Supabase Auth](https://daily.dev/posts/custom-oidc-providers-for-supabase-auth-3y4rruxmi) · Supabase · 4 upvotes · 0 comments
- [Building a cloud native internal developer platform with Kubernetes, GitOps, and supply chain security](https://daily.dev/posts/building-a-cloud-native-internal-developer-platform-with-kubernetes-gitops-and-supply-chain-securi-gtwycp0j7) · CNCF · 7 upvotes · 1 comments
- [Apache Polaris OIDC with Keycloak and Okta](https://daily.dev/posts/apache-polaris-oidc-with-keycloak-and-okta-cjfeyusju) · Dremio Blog · 0 upvotes · 0 comments
- [Configuring generic OIDC provider for authentication](https://daily.dev/posts/configuring-generic-oidc-provider-for-authentication-djrqahxia) · OctopusDeploy · 1 upvotes · 0 comments

---

Tags: [#architecture](https://daily.dev/tags/architecture), [#golang](https://daily.dev/tags/golang), [#authentication](https://daily.dev/tags/authentication), [#ai-coding](https://daily.dev/tags/ai-coding)

[View this post on daily.dev](https://daily.dev/posts/building-an-oidc-provider-from-scratch-in-go-apjugsw4x)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Building an OIDC Provider from Scratch in Go","url":"https://daily.dev/posts/building-an-oidc-provider-from-scratch-in-go-apjugsw4x","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/building-an-oidc-provider-from-scratch-in-go-apjugsw4x"},"datePublished":"2026-07-22T19:36:01.669Z","dateModified":"2026-09-14T06:51:47.269Z","description":"A developer shares their experience building a production-grade OIDC Provider in Go from scratch, using hexagonal architecture and multiple AI coding...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/799dd3536dfec59d206ca0fb1494a603?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/799dd3536dfec59d206ca0fb1494a603?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Medium","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Medium","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/medium","url":"https://daily.dev/sources/medium_js"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/building-an-oidc-provider-from-scratch-in-go-apjugsw4x","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":1},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"architecture,golang,authentication,ai-coding","timeRequired":"PT23M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Medium","item":"https://daily.dev/sources/medium_js"},{"@type":"ListItem","position":3,"name":"Building an OIDC Provider from Scratch in Go"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/building-an-oidc-provider-from-scratch-in-go-apjugsw4x#faq","mainEntity":[{"@type":"Question","name":"How do I integrate a custom OIDC provider with Supabase Auth using Supabase CLI v2?","acceptedAnswer":{"@type":"Answer","text":"Supabase CLI v2.x config.toml does not support a generic OIDC provider directly, and calling signInWithOAuth with provider 'oidc' returns a 400 error. The workaround is to configure the provider as if it were Keycloak, enabling [auth.external.keycloak] in config.toml and implementing Keycloak-compatible endpoints (/protocol/openid-connect/auth, /certs, /token) plus the standard OIDC discovery and JWKS endpoints on the custom provider. Developers wiring custom identity providers into Supabase can find integration quirks like this one on daily.dev."}},{"@type":"Question","name":"What is the difference between the OAuth state parameter and a login CSRF token in an OIDC authorization flow?","acceptedAnswer":{"@type":"Answer","text":"State ensures the redirect from the OIDC provider back to the client's callback endpoint comes from the original client request, and is verified by the client-side backend. A login CSRF token, generated by the OIDC provider itself in the /authorize step and embedded as a hidden form field, instead ensures that a sign-in form submission actually came from the provider-rendered page, protecting a different trust boundary than state or nonce. Anyone implementing sign-in flows can dig into distinctions like state versus login CSRF tokens on daily.dev."}},{"@type":"Question","name":"Why would a refresh token flow fail id_token nonce validation in Supabase GoTrue with a custom OIDC provider?","acceptedAnswer":{"@type":"Answer","text":"The refresh token flow does not include a nonce in the id_token, which is correct per the OIDC specification, but Supabase validates the nonce by default and will reject such tokens. Setting skip_nonce_check = true under [auth.external.keycloak] in config.toml disables that check so refresh-token-issued id_tokens pass verification. Teams debugging Supabase OIDC nonce errors can track fixes like this on daily.dev."}}]}
```

