Building an OAuth Provider Inside the Product Changes More Than Authentication
Many products talk about “opening up an API” as if it is a documentation task.
In reality, the moment you decide external developers should be able to build on your app, you are no longer just shipping endpoints. You are shipping platform behavior:
- client registration
- redirect URI validation
- consent
- scope design
- token lifecycle
- revocation
- developer support expectations
We built Trek Point’s OAuth support inside the main product instead of outsourcing the whole thing to a separate platform team or service. That decision was mostly about speed, but it also forced us to become much more explicit about how our product model should be exposed to outsiders.
Why Build It In-Product
The strongest reason was simple: the public API needed the same domain rules as the application.
Activities, routes, sharing, and ownership already existed in the core product. Rebuilding those semantics in a separate API service would have introduced a second interpretation of the same concepts. That is exactly how drift starts.
By keeping the authorization server, developer portal, and public API close to the existing models, we could:
- reuse the same auth/session system for consent
- reuse the same domain validation logic
- keep scopes aligned with actual product capabilities
- move faster without inventing a new internal control plane
That gave us a coherent first platform instead of a disconnected “API sidecar.”
Redirect URI Validation Was More Product Work Than Spec Work
One of the most important parts of the OAuth implementation is also one of the least glamorous: redirect URI validation.
In theory the rule sounds easy:
“Only allow safe redirect URIs.”
In practice that turned into decisions like:
- allow
https - allow
httponly for localhost - support a custom native-app scheme
- reject duplicates
- cap the total number of registered URIs
Those choices are not just security hardening. They define what kinds of developers you are willing to support.
If you are too strict, legitimate mobile and local-development workflows get painful. If you are too loose, the client registry becomes an attack surface.
That balance is the real work.
We Optimized for Interop, Not Spec Purism
There are a couple of places where Trek Point intentionally leans pragmatic.
For example:
- we tolerate PKCE-related behavior that some native stacks send even when a client did not initiate the flow with a challenge
- confidential clients can authenticate at the token endpoint using either
client_secret_basicorclient_secret_post
Purists can argue for stricter behavior. I get it. But product platforms live in the world of real client libraries, mobile stacks, and integration developers who do not care which corner of the RFC they are violating if their app still works elsewhere.
The trick is knowing where to be lenient without making your security story sloppy.
The Developer Portal Mattered as Much as the Token Endpoint
A lot of internal API launches underinvest in developer UX. We tried not to.
If the only way to manage clients is manual database work or opaque admin actions, you have not built a developer platform. You have built a support dependency.
That is why Trek Point includes:
- a developer subdomain
- UI for client creation and redirect URI management
- support for public versus confidential clients
- one-time secret reveal behavior
- a CLI path for operational and internal workflows
The portal is not just garnish. It is the interface contract for the people integrating with your product.
Scopes Forced Us to Name the Product Properly
I think scopes are underrated as a design exercise.
When you name scopes like:
activities_readactivities_writeroutes_readroutes_write
you are forced to say what your product actually exposes as stable capabilities.
That is healthy. It prevents the API from becoming a random mirror of internal model methods.
It also creates a discipline the rest of the team can reason about. Marketing might describe the feature as route planning. The OAuth layer has to define what that means in permission terms.
What I’d Watch Carefully
The biggest long-term risk in this design is not the OAuth server itself. It is versioning pressure.
When the public API reuses internal models and helpers, you move fast early, but you also increase the chance that an internal refactor becomes an external compatibility incident.
That does not mean the design was wrong. It means you should go in knowing that your monolith has now become a platform surface, and platform surfaces age differently.
The Takeaway
Building OAuth inside the product was one of those decisions that looked smaller at the beginning than it does now.
We thought we were adding API access.
What we were really doing was:
- defining stable external capabilities
- creating a developer-facing control plane
- hardening product semantics into explicit permission boundaries
That is why I think “we added OAuth” understates the work. We did not just add authentication. We taught the product how to be extended by people who do not work here.