How Authentication and Authorization Work for SPAs

Adding authentication to public clients such as Single Page Applications (SPA) and JavaScript applications can be a source of confusion. Identity Providers like Okta try to help you via multiple support systems. Still, it can feel like a lot of work. Especially since you’re responsible for way more than authentication alone in the applications you work on!


This is a companion discussion topic for the original entry at https://developer.okta.com/blog/2023/04/04/spa-auth-tokens

I don’t understand from this how to validate the aud claim on the access token in my API.

e.g. I have an SPA served on app.example.com.

I have a JSON/HTTP API served on api.example.com.

I have configured my SPA to discover from https://trial-12345.okta.com - so it is hitting https://trial-12345.okta.com/.well-known/openid-configuration as a start point.

After successful authentication my SPA gets an access_token field containing "aud": "https://trial-12345.okta.com".

I pass that as Authorization: Bearer <access_token> when calling https://api.example.com. But how do I validate it server side? Do I have to make my server side app happy to accept https://trial-12345.okta.com as the aud? That seems wrong, surely it should be https://api.example.com as the aud?

I can change the default Authorization Server in Settings → Security → API to have an Audience of https://api.example.com - but that makes no difference to the aud claim returned in the access_token when doing the OIDC flow. I suspect because I am getting the metadata from https://trial-12345.okta.com/.well-known/* rather than https://trial-12345.okta.com/oauth2/default/.well-known/*. Am I right in this?

I find it very frustrating that this is so opaque and obscure when getting an access token to use with an API must be the absolutely standard use case, and validating the aud claim of an oauth2 acces token is meant to be a critical security action?