HNHacker News
TopNewBestAskShowJobs

maxwellg

601 karma · joined September 28, 2018

Software Engineer and OAuth guy at https://stytch.com/

blog - https://maxgerber.com

email - max at stytch dot com

submissionscomments
maxwellg··on Self-Signed JWTs
Each JWT was passed as a query param over a 307 redirect from my service to the other side, so the JWT itself was the whole request to prevent tampering from the browser. It was for an internal tool that did one thing, did it well, and never caused me any problems.
maxwellg··on Self-Signed JWTs
I _love_ JWTs for API authentication - one of the nicest APIs I ever consumed was essentially JSON RPC over JWTs. Unfortunately they represent a huge usability hit over API Keys for the average joe. Involving cryptography to sign a JWT per request makes an API significantly harder to consume with tools like Postman or CURL. You can no longer have nice click-to-copy snippets in your public docs. You either have an SDK ready to go in your customer's language or ecosystem of choice, or you're asking them to write a bunch of scary security-adjacent code just to get to their first successful request. No, I don't have a JWT library recommendation for Erlang, sorry.

Not that an API couldn't support both API Keys and JWT based authentication, but one is a very established and well understood pattern and one is not. Lowest common denominator API designs are hard to shake.

maxwellg··on We revamped our docs for AI-driven development
The Honeycomb MCP server has a similar tool call. How long until we get MCP support workflows and mixins as a standalone SaaS?
maxwellg··on Building MCP servers for ChatGPT and API integrations
It is very nice to get MCP support in ChatGPT. OpenAI really fumbled the bag with the OpenAPI-based Custom Actions (or was it Custom GPTs?). The web editor experience was always incredibly buggy, even months after initial release. MCP servers allow us to move nearly all of the tool definition bits into the server codebase itself, so we can change things on the fly / version control / feature flag tools etc. much better.
maxwellg··on My Self-Hosting Setup
You're right that signing the token doesn't prevent login CSRF. Signatures protect against the second attack you mentioned. PKCE my favorite OAuth extension for preventing login CSRF as described.

> In that case, the signature doesn't solve that attack, because since everything happens in the attacker's browser, the attacker can modify the js locally to disable signature verification.

The frontend doesn't need to verify the ID Token - the backend of the relying part does. The backend can never trust the frontend. The signature is needed so the backend can verify that the ID Token credential was issued by the IDP.

> Just from reading the spec, a reader can wonder, "why is there all this unexplained complexity for no apparent reason?"

Yeah, this is true. Oftentimes the attacks are enumerated at the end of a spec, so it isn't clear when reading an earlier section. For example, the attack we've been discussing is described here: https://openid.net/specs/openid-connect-core-1_0.html#TokenM...

maxwellg··on My Self-Hosting Setup
> Why does the ID Token have a signature?

The ID Token can be passed from the Identity Provider to the Relying Party (RP) in a few ways.

When `response_mode=id_token` is used, the ID Token can be passed in the front channel directly to the RP during a browser redirect. Since the ID Token is coming from the browser, it must be signed to ensure that a malicious actor can't tamper with it. Otherwise an actor could swap out a `sub` or an `email` claim and the RP would be none the wiser.

The ID Token can also be returned from the `/token` endpoint after exchanging an authorization code. Since the `/token` endpoint is a back channel call over HTTPS, the ID Token doesn't necessarily need to be signed here to avoid tampering. The RP can trust that TLS gets the job done. However, there are substantial benefits to having it be signed:

- If ID tokens were only signed sometimes, we'd have two different standards for how to construct and handle an ID Token, which would be quite confusing.

- Signed ID Tokens can be passed around to assert identity to other entities within the system. For example, there are some promising draft specifications that explore exchanging ID Tokens for access tokens in other systems. This is only possible because the ID Token cannot be tampered with.

https://datatracker.ietf.org/doc/draft-parecki-oauth-identit...

maxwellg··on Exposed MCP servers across the internet
Yep, if you put something on the open internet it needs authentication or it is public to everyone. This isn't a vulnerability unique to MCP - plenty of databases, REST APIs, S3 buckets, and other sorts of resources have been left open before. MCP is just the latest shiny thing people can leave unsecured.
maxwellg··on Auth for B2B SaaS: it's not like auth for consumer software
The usual tagline is that "OAuth is authorization, not authentication". OIDC is an authentication layer built on top of OAuth. This means that OIDC can be used for both authentication and authorization to a third party API - most OIDC implementations allow the client to request an ID token (for authentication) and a traditional OAuth Access token (for authorization) in a single transaction. Clients can opt out of the authorization process by requesting only `response_type=id_token` and eschewing an authorization code entirely but that is not so common.

SAML does not grant any authorization to an upstream API.

maxwellg··on Auth for B2B SaaS: it's not like auth for consumer software
These are all features of SCIM, not of SAML. SAML only communicates user metadata on login. SCIM can be used with both SAML and OIDC.
maxwellg··on -2000 Lines of code (2004)
On days I write code, I try to do one "cleanup" PR a day just to get myself warmed up. Sometimes it is removing a feature flag, sometimes it is rewriting a file to use some new standards like a better logger library or test pattern. None of this is ticketed work, and if something takes longer than ten minutes or so I drop it and work on whatever I was going to work on originally. Make (trivial) cleanups a fun treat and a break from real work and it is easier to get other people excited about them.

Of course, lately anything trivial I ask codex to do - but there is still fun in figuring out what trivial thing I should have it take on next.

maxwellg··on (On | No) Syntactic Support for Error Handling
This is the right move for Go. I have grown to really love Go error handling. I of course hated it when I was first introduced to the language - two things that changed that:

- Reading the https://go.dev/blog/errors-are-values blog post (mentioned in the article too!) and really internalizing it. Wrote a moderately popular package around it - https://github.com/stytchauth/sqx

- Becoming OK with sprinkling a little `panic(err)` here and there for truely egregious invalid states. No reason forcing all the parent code to handle nonsense it has no sense in handling, and a well-placed panic or two can remove hundreds of error checks from a codebase. Think - is there a default logger in the ctx?

maxwellg··on Show HN: MCP Server SDK in Bash
Oh absolutely - but the infrastructure required to support a "click link, get remote MCP URL added to config automatically" flow is _so_ much smaller than the infrastructure required for a "click link, download and install arbitrary software that may or may not depend on having existing tools installed" flow.
maxwellg··on Show HN: MCP Server SDK in Bash
Ha! I love this. There's nothing like a proper Bash script to make me realize how terribly gross all of mine are.

The drum I'm currently beating is that local MCP is a ton of fun for techies like us - if you're on this website you can `npx ...` or install whatever you want with a modicum of common sense - but local MCP is going to be a dead end for mass adoption. If we want to build MCP servers that get used by everyday people (or on mobile or other locked down ecosystems) then remote MCP + OAuth is the only realistic way forward. I can't get my dad to open up a terminal window - anything over stdio or touching environment variables and API keys is a nonstarter.

maxwellg··on The double standard of webhook security and API security
Ooh this is a favorite pet peeve of mine. HMAC is the better solution IMO but API Keys are so much easier for your customers to use:

- API Keys are much, _much_ easier to use from the command line. CURL with HMAC is finicky at best and turns a one-liner into a big script

- Maintaining N client libraries for your REST API is hard and means you'll likely deprioritize non-mainstream languages. If a customer needs to write their own library to interact with your service, needing to incorporate their own HMAC adds even more friction.

- Tools have gotten much better in recent years- it is much easier to configure a logger to ignore sensitive fields now compared to ~10 years ago

maxwellg··on Trading with Claude, and writing your own MCP server
> When you add an MCP server to your Claude organization, you just add the MCP server. Each user will have to go through the integration's OAuth2 authorization flow separately.

Check out https://aaronparecki.com/2025/05/12/27/enterprise-ready-mcp - there are some great ideas there on how this can be simplified even more in the future.

maxwellg··on Trading with Claude, and writing your own MCP server
Check out https://github.com/metoro-io/mcp-golang - looks like they support strongly typed tool arguments.
maxwellg··on Trading with Claude, and writing your own MCP server
It looks like there are a few Golang implementations of MCP. The one used in the article doesn't use tags but mcp-golang does. Tags are great! I love tags.

- https://github.com/metoro-io/mcp-golang

maxwellg··on Trading with Claude, and writing your own MCP server
Sometimes Go can get under my skin. The MCP SDK makes you jump through all these hoops to configure tools according to a JSON schema, but when it comes to handling the actual request you need to deal with parsing everything again out of a `map[string]any`. It's such a pain to need to reach for codegen all the time.
maxwellg··on MCP is the coming of Web 2.0 2.0
MCP uses a date based system for versioning. The most recent specification is 2025-03-26 - https://modelcontextprotocol.io/specification/2025-03-26
maxwellg··on Next Password Could Be Stored in Plastic
Cool tech. Don’t put a password in something that can be read by anyone with a scanner though.
maxwellg··on Ask HN: Founders – how are you automating support?
Not a founder but our support org is a huge fan of Plain (plain.com) which has some AI features but is mostly a great way to manage both Slack and Email support in one place.
maxwellg··on OpenAI Codex hands-on review
Being able to make quick changes across a ton of repos sounds awesome. I help maintain a ton of example apps, and doing things like updating a README to conform to a new format, or changing a link, gets pretty tedious when there are 20 different places to do it. If I could delegate all that busywork to Codex and smash the merge button later I would be happy.
maxwellg··on Spaced repetition systems have gotten better
ChatGPT 4o's voice mode has been mindblowing for me for learning basic Mandarin. I'm sure I will hit the limits of the model sooner or later, but it has been so much fun bouncing around my apartment and asking what various objects are called / if words are related to other words. It is phenomenal for putting together short little sentences and getting immediate validation on grammar too.

No MCP on ChatGPT yet, but I can ask chat to generate an output of what we reviewed in a structured format.

maxwellg··on Directory of MCP Servers
I can't wait for first-party remote MCP servers to become more common. Right now we're taking a strange detour of everyone trying to proxy everyone else's APIs and do manual API Key juggling because platforms aren't running their own MCP servers and clients don't support the latest OAuth changes.

In a year from now, Github will run a single public Github MCP server that you will connect to via OAuth - you won't need to install it locally or faff around with tokens or environment variables at all.

maxwellg··on Coffee for people who don't like coffee
One-dimensionality isn't the issue - the issue is that most drip coffee makers and most cheap to-go coffee is terribly, terribly burnt. The coffee would taste so much better if it was brewed fresh at a lower temperature, but instead you end up with a pot of near-boiling water sitting on a heating element for hours.
maxwellg··on Coffee for people who don't like coffee
During Covid I spent a lot of time on my home coffee setup - I've since dialed it back but I've kept the pourover, the grinder, and the Chemex. We found a local business that roasts beans in their garage that we love.

The biggest problem is that really good coffee ruins bad coffee foreger. After making my own for so long, traveling and dealing with gas station or hotel breakfast coffee is especially jarring. Sometimes I wish I could forget how good coffee could taste.

maxwellg··on Show HN: I built an app to generate story relationships using Mermaidjs
Neat! Putting in reference books gives you a nice overview of the book's contents. Fun to think of database transactions as a character with relationships.

https://austen.pages.dev/c2276990-1f2a-40e6-bd26-0cabb55d713...

maxwellg··on The Agent2Agent Protocol (A2A)
I wasn't around for WSDL so please correct me if I am wrong - but the main weakness of WSDL was that no applications were able to take advantage of dynamic service and method discovery? A service could broadcast a WSDL but something needed to make use of it, and if you're writing an application you might as well just write against a known API instead of an unknown one. LLMs promise to be the unstructured glue that can take advantage of newly-discovered methods and APIs at runtime.
maxwellg··on Testing Without Mocks: A Pattern Language (2023)
I very much agree- but swapping out the database is a scary operation if you use the database for any sort of nontrivial logic. You need to make sure that your unique indicies, partial indicies, constraints, views, triggers, etc. work just as well. Another option is to start each test in an isolated transaction and roll back at the end instead of committing, but that comes with its own set of headaches too.
maxwellg··on I ate and reviewed every snack in our office kitchen
Grazing on leftover Forkable lunches is one of the great pleasures of going into the office now. I eat my regular lunch at noon. At three or four, I do a lap to see if there are any unclaimed leftovers from folks who didn't make it in. I might eat one right there, or hide it in the fridge to take home later. It's like a VC-funded loot box - will I get a sandwich? Some curry? Dim sum? The possibilities are endless.
← PreviousPage 2 of 5Next →