Bugsy
Explains
All explainers

Bugsy explains

OAuth, JWT and passkeys: what's the difference?

Three words that get mixed up because they show up in the same login: OAuth lends an app limited access, a JWT is a signed note anyone can read, and a passkey replaces your password with a key that phishing can't steal.

9 min 1 toy

  • auth
  • security
  • web

You tap Sign in with Google on a new app, and a moment later you're in. Did that app now know your Google password?

No, and the reason is a small piece of clever design. On this page you'll meet three words that often appear in the same login: OAuth, JWT and passkeys. They solve three different problems, and that's exactly why they work well together.

Who are you, and what may you do?

Two questions hide inside every login. Authentication asks "who are you?" and checks that you really are that person. Authorization asks "what may you do?" and decides which doors open for you.

Think of a hotel. At the front desk you show your ID: that's authentication. You get back a key card that opens your room and the gym, but not other guests' rooms: that's authorization. The card doesn't know your name. It only knows what it may open, and it stops working on the day you check out.

OAuth: lending a key, not your password

OAuth 2.0 is about authorization, and specifically about delegation: letting one app do a limited job for you at another service, like a recipe app reading your Google Calendar, without ever handing it your password.

Here's the usual flow. The app sends you to Google. You log in at Google, on Google's own page, and Google asks whether you allow this app to see your calendar. If you agree, Google sends you back to the app with a short-lived, one-time authorization code.

The app's server then swaps that code with Google for an access token: the key card. From then on, the app shows the token every time it calls Google's API. The app never sees your password, and you can take the key back at any time in your Google account settings.

Quick check

Scopes, and how OAuth became a login

An access token isn't a master key. When the app sends you to Google, it lists scopes: exactly what it wants, such as reading your calendar or seeing your email address. Google shows that list on the consent screen, and the token only works for what you approved.

Notice what plain OAuth doesn't say: who you are. An access token is meant for Google's API, not for the app to read: it isn't a promise about who logged in. OpenID Connect (OIDC) is a thin layer on top of OAuth that fixes this: the app also asks for the openid scope, and gets an ID token back next to the access token. The ID token is a signed statement from Google: "this is user 42, and they logged in a moment ago".

JWT: a format, not a protocol

OAuth is a protocol: a set of steps that apps and servers follow. A JWT (JSON Web Token, often said "jot") is something else: a format for writing a token down. OIDC's ID tokens are always JWTs, and many access tokens are too, though OAuth doesn't require it.

A JWT is three parts joined by dots: header.payload.signature. The header says how it was signed, the payload holds the claims (facts about the user and the token), and the signature protects both. Here's what the first two parts look like once decoded:

{ "alg": "HS256", "typ": "JWT" }.{  "sub": "42",  "name": "Sam",  "role": "user",  "iat": 1767225600,  "exp": 1767226500}.base64url(HMAC-SHA256(base64url(header) + "." + base64url(payload), secret))

sub (subject) is who the token is about, iat is when it was issued and exp is when it expires, both in seconds since 1970. Here exp is 900 seconds after iat: this token lives for 15 minutes. The last line is how the signature is made: an HMAC over the two encoded parts joined by a dot, which is then encoded with base64url too.

Encoding is not encryption

Each part is turned into text with base64url, an encoding that makes bytes safe to put in a URL. Encoding isn't encryption: anyone holding the token can decode the payload and read it, no key needed. So never put a secret in a JWT.

What stops someone from editing the payload, say from user to admin? The signature. The server made it from the header, the payload and a secret that only the server knows. When a token comes back, the server computes the signature again. If even one character changed, the two no longer match.

Try it: change the role and watch what the server says.

Try it

Why servers like JWTs, and the catch

Because a JWT carries its own proof, a server can check it with a little maths instead of looking it up in a database. Any server that has the key can do it, which makes JWTs handy when many servers share the work.

The catch is the flip side of that: if no one looks the token up, no one can cancel it early. A stolen JWT keeps working until its exp. That's why access tokens are kept short-lived, often minutes, and a separate refresh token is used to get a fresh one. A refresh token can be cancelled, because the server does look that one up.

With HS256, the same secret makes and checks the signature, so every server that can check a token can also forge one. Tokens that travel between companies use RS256 or ES256 instead: Google signs ID tokens with a private key, and any app can check them with Google's public key. You'll see that key-pair trick again with passkeys.

Quick check

Passkeys: nothing to steal

Passwords have two big problems. Servers store something derived from them, which leaks in data breaches. And people type them into fake sites that look real, which is called phishing. Passkeys (built on the WebAuthn and FIDO2 standards) replace the password altogether.

When you create a passkey, your device makes a new key pair only for that site. The private key stays with you: on your phone or laptop, or synced end-to-end encrypted by your password manager. The site stores only the public key, which can check signatures but can't make them.

To log in, the site sends a random challenge. Your device asks for your fingerprint, face or PIN, then signs the challenge, together with the address of the site that asked, with the private key. The site checks the signature with the public key. The challenge is new every time, so a signature someone records today is useless tomorrow. Your fingerprint never leaves the device: it only unlocks the key.

So a breach leaks only public keys, which are useless to an attacker. And each passkey is tied to the real site's domain: on a look-alike like go0gle.com, your browser has no passkey for that site at all, so there's nothing to trick you into handing over.

How they fit together

Now the three pieces snap into place. A passkey proves who you are. OAuth decides what an app may do on your behalf. A JWT is one common way to write the resulting statements down so a server can trust them.

For example: you log in to a site with a passkey, and it gives you a session cookie or a short-lived JWT. Or you tap "Sign in with Google": Google checks you with a passkey, then uses OAuth and OpenID Connect to hand the app an ID token (a JWT) and an access token.

Boss question

Put it all together.

Quick check

Done reading?

Solve 3 more quick checks to finish.