Third Party Authentication — How: Tokens, Cookies and Redirects

Part II: Broad Technical Strokes

Samantha Wong
Government Digital Services, Singapore

--

If you haven’t already, you may wish to read Part I, which sets the stage for Part II, here.

Now that we know we want a federated identity system — how does one go about implementing it? We can abstract a few principles from our predecessors.

The first is the token structure itself.

Tokens

This digital token could simply be a unique string — that would be enough for consumers to differentiate between different digital users. But what if I wanted a bit more information about the real-life person said token represents — perhaps their name — one could attach that information onto the token as well. If I were concerned about others being able to access that information, I could choose to encrypt it, and if I wanted to be sure of the origin of the token, I could have the provider could sign it and myself verify its signature upon decryption.

This is where the JSON Web Token (JWT) family or ‘jots’ come in. It’s a popular standard that software systems use to transfer information, or a set of ‘claims’ from one system to another. It allows for signing (JWS) and encryption (JWE) as well.

JWTs typically consist of a header segment, a data segment and a signature segment like so:

JWTs demystified.

Save for the signature segment, both header and data portions can simply be base64 decoded to extract the plain text within. The data portion of a JWT, unencrypted, and unsigned, is just a plain-old JSON object when decoded. So it looks something like this:

{
"Name": "Bob Dylan",
"IC": "S1000001A"
}

Where you can attach any number of attributes, or claims to it. And then you can jazz it up by signing this token, or encrypting it. If you encrypt it, users who get hold of the token won’t be able to decrypt its contents, or create another token, without the encryption key.

Cookies

We talked about using a token to represent the digital user. Now let’s talk about how we might store and transport this token.

One of the most natural places to store a session token would be in the browser’s Cookie Store. I’ll bet if you bring up Developer Tools/Inspect on whatever browser you are on, and look inside the “Storage” tab, you’ll find a number of cookies in your cookie jar, either from a news site you just visited, or most likely from Google if you use Google.

A Sample Browser Cookie Store.

Cookies are just key-value pairs stored in your local machine-browser application, otherwise known as client-side, associated with a domain, whose expiration can be set.

There are of course other places in the browser that can hold data, such as localStorage, sessionStorage and globalThis. They each have different properties that informs what one might want to put inside them. Here’s a table comparing their various properties. Take note that different browsers may have slightly different handlings for some properties.

Information in this table might become outdated as browser standards and implementations change.

Cookies are sent along requests to the same domain mysite.com and all other subdomains such as api.mysite.com or random.api.mysite.com, with some caveats. This makes them convenient for authorisation purposes where you want your backend server to validate if a digital user represented by the cookie-token is eligible to retrieve a certain resource or perform a certain action.

A note on the size of cookies. The practical browser size limitation on cookies informs storing minimally identifying information, and not an exhaustive set of user properties. After all, we will be sending these packets over nearly every request to a certain domain and its subdomains.

It’s not that it can’t be done — you could build a browser that has a 1GB cookie store and send it over every network request; but you see why that might be inadvisable. Keeping to the spirit of cookies would be keeping them as little bits of transportable information, in my opinion.

Cookie Security

You might wonder about the security of cookies. After all, weren’t we taught that we are supposed to only perform authorisation on the backend side of things, because frontend clients cannot be trusted in the sense that users can tamper with data on the frontend?

That statement is true. The cookie, like any other client-side storage, is open to user modification.

However, we won’t be able to meaningfully modify that cookie unless we have the original encryption key used to encrypt it. And that encryption key shouldn’t be available to the frontend browser. It should be kept far far away, safely hidden in some secrets manager in a private network.

So the cookie-token is effectively “tamper-safe” because it is an encrypted one. If your service uses a plain text non-encrypted JWT, you should consider encrypting it. Or at the very least, a signed JWS that would highlight if it has been tampered with. With JWEs and JWSes, a user can choose to change it in a random way, but if they do so — the backend would not be able to meaningfully decrypt it and/or would detect a mismatch in the signature.

The only way a user could meaningfully change the encrypted/signed cookie-token would be to get ahold of the key used to encrypt/sign the tokens, decrypt a sample token, get the relevant data structure, update the values for their desired mischief, re-encrypt/re-sign that new updated data and update their cookie store with the new token. Which to be honest isn’t that hard to do once you have the token encryption key. So your last line of defence is always the key used to encrypt/sign your tokens.

There are other properties of cookies that exist that can improve security, such as Secure, which tells the browser to only send the cookie with the HTTPS and not the HTTP protocol, and HTTPOnly, which makes the cookie inaccessible to JavaScript’s Document.cookie property, so client-side scripts wouldn’t be able to access it. You should also configure the SameSite property, which gives instructions on when cookies are sent with respect to whence the call originates, to your purposes.

A comprehensive diagram on how the ‘Same-Site’ property works from Son Xun Nguyen.

The key idea here is that you can have some token storage on your frontend, stored smartly so that it tracks your user as they move within your domain, but still do your access validation on the backend.

Domains and Redirects

Now for all the storages mentioned above, they have an important property of being only available within the same domain. Application code can access localStorage and sessionStorage belonging to the same domain. Servers can only set cookies belonging to their own domain, as far as web standards go today.

Author’s Impression of Browser Storage.

Browser-Side Access Control

If any application code could access the storage of other domains, it would imply a lack of cross-application data boundaries. Domains are web addresses that imply control of whatever is being served, and is thus a good, though imperfect, in the case where one’s server has been hacked, indicator of application boundaries.

Redirects to Identity Providers

There are two things that need to happen at the identity provider — one, allow users to enter their digital representation, and two, return a token and/or set a cookie in the response. Redirecting to a site controlled by the identity provider then becomes a natural model to fulfil both roles.

The identity provider site will serve up client-side content allowing user input and also set cookies in its response. It will then redirect users back to the consuming site once the former tasks are completed.

Looks familiar, eh?

If the identity provider also manages user sessions (which in all likelihood, it will not) — your consuming application’s client-side (frontend) can subsequently make calls to the identity provider (or now session provider) to check validity and/or renew their token.

Otherwise, your consuming application’s server-side (backend) might simply make another server-to-server request to the identity provider for the identity token and return a session token to the consuming application’s client-side for session management. Subsequent front-end calls to renew token or check session validity will be to the consuming application’s own backend service.

A model of one possible interaction of Consumer and Provider Frontends and Backends where the Identity Provider leaves the Consumer to manage sessions.

And there you have it — your complete frontend-to-backend-back-to-frontend, zero-to-hero, client-side-to-server-side guide to Third Party Authentication.

PostScript

And with that we are almost at the end of this whirlwind tour of Third Party Authentication. Regardless of the tokens, of the auth sessions, of refresh and access policies — I wanted readers to come away with the basic tools to understanding third party authentication.

There’s definitely more to unpack in terms of session management and cross-site authentication.

Implementation details will vary from application to application, provider to provider, browser to browser.

With knowledge on why we need tokens, where they are stored in browsers, where the access validation is done (on the backend) and domain restrictions — I hope you will have a better appreciation of what goes on the next time you key in some credential set to log onto a website, and why you always get redirected.

Other References

Browser Storage

  • Local Storage vs Session Storage vs Cookies: 1, 2, 3

Cookies

StackOverflow

This article is an extension-continuation of a presentation given to the ACE team in Feb 2021, which may be found in its original form here.

There was a lot of material covered, at least to me — if you spot any inaccuracies, feel free to reach out here.

--

--