Hacker News new | ask | show | jobs
by techthumb 1678 days ago
From the article itself:

  ... one might think implementing OAuth sign up is relatively trivial; after all, you just need to write a fetch request that redirects the user to the OAuth page, then another request that sends their email to the newsletter service of choice to sign them up. Well, the issue is that in order to do the second step of that process, one needs to hit an API endpoint that requires authentication (an API key). That is essentially a password and not something you want to expose on the front end and give everyone access to.


The OAuth Authorization Code Flow with Proof Key for Code Exchange (PKCE) solves this problem without needing a worker.

This article Auth0 does a good job of explaining PKCE: https://auth0.com/docs/authorization/flows/authorization-cod...

2 comments

The problem isn't about secure transmission of the access_token, it's about secure storage of the access_token: it's impossible to safely and securely store secrets in browser-based (JS) OIDC clients when using OIDC's Implicit flow, which is the only flow that works with client-side-JS applications (especially SPAs).

Conventional web-applications let the application server store per-user secrets (e.g. access_tokens). If the application server needs to be stateless then secrets are packed into a web-browser cookie with the "httponly" and "secure" attributes which prevents any and all client-scripts from accessing them. Of course browser cookies are not the same thing as a true Bearer Token, so this means that when using an SPA the SPA cannot make its own HTTP requests to other RPs, it needs to use some non-local secret-storing-proxy to make the request for it... which starts to make a mockery of how microservices should operate.

Code Flow with PKCE does not replace the Implicit flow. Also, the Auth0 article you linked to is not a "good job". On the contrary, that article talks about using client-secrets - which you *must never have* in a JS-only/SPA/static client.

The only real solution would be some kind of opaque OIDC client built-in to a browser that handles secrets-storage on-behalf of JS applications (such that JS code never gets to see or handle any secrets, including the auth code and access_token, but the OIDC identity_token should be exposed, of course). I'm surprised Google and Mozilla haven't done this already...

I think you misunderstood what I said. The API key I don't want to expose is the one that is required to register the user with the newsletter service, not the client key for the OAuth service.
In that case you could create a backend endpoint that accepts a request and makes the call with the API key on behalf of the client/front-end.

The title of the article says OAuth, and hence assumed that you wanted an authenticated client to be able to make the call to the backend for subscribing.

"In that case you could create a backend endpoint that accepts a request and makes the call with the API key on behalf of the client/front-end"

This is what is happening, except instead of a backend endpoint hosted on my own VPS, I'm using a Cloudflare worker.

"The title of the article says OAuth, and hence assumed that you wanted an authenticated client to be able to make the call to the backend for subscribing."

An authenticated client is necessary in order to retrieve the email of the client.

I follow the implementation here.

The conflating part here is that using the callback as a mechanism to imply subscription.

This works for your situation.

However, if you need to start making multiple backend calls, then, you will likely need to separate the authentication part from the subscription part.

Generally, OAuth implies that the requirement is to get authenticated by a provider and making multiple subsequent calls to some backend. Additionally, the backend will verify the authenticity of the short-lived token before allowing the operation to proceed.

OAuth doesn't use "API keys". Please clarify.
"not the client key for the OAuth service"

OAuth uses a client key and/or a client secret, for the application that is requesting access on behalf of the client.

OAuth + OIDC only uses client-secrets when using the client_credentials flow, which is only for us with non-human software, or when a client needs to authenticate and authorize itself independently of any human user. When humans are involved you won't be using client_credentials, you'll be using 'implicit' or 'code' (preferably with PKCE) - but ONLY when the client can actually safely store secrets - so static-website SPAs simply can't.

While non-human client-credentials can be used in-conjunction with a human-user's credentials it's largely unnecessary as an unauthorized client wouldn't be able to authenticate with a human-user because the redirect_uri sent from the client would be rejected automatically (and if that worked, there's always 'aud' audience filtering too), so the human-user wouldn't even be prompted to authenticate, they'd get an error message.

Yeah so in this case I'm using the client code, which is safely stored in the worker.