Inspiration
I didn’t set out to build another login page.
I started NazoAuth because I kept looking for an open-source Rust server that treated OAuth and OpenID as a complete system, and I couldn’t find one. Rust had strong libraries for tokens, signatures, HTTP, and individual protocol pieces. What was missing was the whole server: clients, keys, browser login, high-assurance authorization, back-channel approval, security events, and digital credentials, all working under one security model.
That gap bothered me because identity systems rarely fail in the obvious place. They fail where two parts meet. Add mTLS and client authentication changes. Add DPoP and token storage changes. Publish discovery metadata and another system may start making security decisions from it.
I wanted to find out whether one Rust project could handle those seams without becoming a pile of unrelated demos. That project became NazoAuth.
NazoAuth already existed when Build Week began. My goal for the week was to push it further, but also to answer a harder question: could the server prove that it worked without giving its own tests a private shortcut?
Build Week scope
This submission only claims work committed after 2026-07-13T16:00:00Z.
The last commit before the eligible period is public. The Build Week engineering record contains the audited source range, dated pull requests, setup and deployment notes, and links to the relevant tests.
What it does
NazoAuth is a self-hosted OAuth 2.1 and OpenID Connect server written in Rust.
It handles ordinary website sign-in, but it also supports systems with stricter security and interoperability needs: financial-grade authorization, approval on another device, enterprise identity events, and the issuing and presentation of verifiable credentials.
The standards behind those features include OAuth, OpenID Connect, FAPI, CIBA, SCIM security events, and OpenID4VC. A public instance is running at auth.nazo.run. Its documentation, discovery metadata, and public keys can be inspected without access to my deployment environment.
What I built during Build Week
At the start of the week, NazoAuth had grown into a large Rust codebase. Protocol logic, HTTP behavior, storage, key handling, and runtime features were too close together. I split the server into focused modules and made the dependency direction explicit. This wasn’t a file-cleanup exercise. I wanted the structure of the code to show where one security boundary ended and another began.
I also completed the issuer and verifier paths for OpenID4VC. That meant treating issuance and presentation as full lifecycles, including proof of possession, nonce handling, encrypted responses, immediate and deferred delivery, batch issuance, notifications, SD-JWT VC, and mdoc. I finished the supported FAPI-CIBA poll and ping combinations and added RFC 9967 SCIM security-event delivery as well.
The turning point, though, came from the conformance setup.
The test runner could create clients directly in the database. It was convenient, and it made the suite easier to run, but no real integrator gets that privilege. A green test could prove that a protocol exchange worked while saying nothing about whether client application, approval, credential delivery, or the public ingress path worked.
So I removed the shortcut.
A conformance client now applies through the public onboarding flow, waits for approval, establishes trust, collects its credentials once, runs through public ingress, and cleans up afterward. The runner behaves like an outside client, and the retained evidence contains no reusable client secrets.
That changed the meaning of a passing test. The suite now goes through the product instead of reaching behind it.
How I built it with Codex
I used Codex as an engineering partner that could move across the whole repository quickly. I gave it the specifications, the boundaries, and the result I was prepared to accept. It traced existing behavior, implemented changes, wrote tests, investigated failures, and followed work through CI and deployment checks.
I made the decisions that shaped the product: the architecture, how I interpreted the protocols, which roles NazoAuth would support, where the security boundaries belonged, and whether a change was ready to merge or release.
I also had to learn how to ask for the right thing.
Early on, it was tempting to say, “make this test pass.” That framing produced narrow fixes. The better instruction was, “implement the requirement, then use the test to find where we are wrong.” Once I made that change, Codex stopped treating the suite as the goal and started using failures as pointers back to the specification.
Challenges I ran into
Client seeding was the most stubborn problem of the week. I would get one CIBA plan working, then another plan would regress. Each failure looked small enough to patch, so for a while I kept adjusting the setup.
Eventually I realized I was fixing the symptom. The seed wasn’t the product; it was a privileged test fixture. Making it more elaborate would only tie NazoAuth more closely to behavior that real clients could never use.
That was when I changed the design. Application, approval, trust setup, one-time credential delivery, public execution, and cleanup became part of the conformance flow itself.
The new path took more work, but the recurring seed problem disappeared for the right reason. The suite had become a client of the product, not a special authority inside it.
What I learned
I understood these standards best at the points where they touched.
Specifications live in separate documents. Implementations don’t. They meet in database rows, redirects, key selection, browser sessions, error responses, and the exact moment a piece of state is consumed.
Client authentication changes token handling. Sender binding changes storage. Discovery metadata can become a security boundary.
I also came away with a clearer way to work with Codex. Speed helps, but the framing matters more. If I ask for a green test, I may get a green test. If I define the requirement and the boundary first, the test becomes evidence instead of the destination.
What I’m proud of
The answer is simple: the proof now goes through the product.
A judge can inspect the live service, read its metadata and documentation, trace the eligible source range, and review the dated conformance evidence without my database access or deployment credentials. The tests no longer get an easier version of NazoAuth than a real client does.
What’s next
The next step is independent interoperability. I want to test NazoAuth with wallets, issuers, verifiers, and client implementations maintained by other people, and make the public conformance path easy for third parties to reproduce.
I’ll keep removing privileged assumptions wherever I find them. The closer the evidence is to a real deployment, the more useful it is.
Try it in 60 seconds
- Open the live documentation.
- Inspect the OpenID Connect discovery document and public keys.
- Review the Build Week engineering record.
- Read the dated public black-box result.
No judge account is required for these read-only paths.
Built With
- openai
- rust
Log in or sign up for Devpost to join the conversation.