The JWT sub claim (short for subject) is the identifier an OpenID Connect provider gives to a user in an ID token or access token, and it is only guaranteed to be unique within the issuer that created it. The iss claim names that issuer. So the correct database key for “which user is this?” is the pair (iss, sub), never sub on its own, and never the email address. Two different identity providers can each issue a user with the same sub value, and they will be two different people.
This matters more as applications accept sign-ins from several identity providers (IdPs), through social login, enterprise single sign-on or an identity broker. This post covers what sub is, the rule for using it, why email makes a poor key, how pairwise identifiers change the picture, how brokering maps upstream identities to local users, and what Keycloak puts in sub.
Key takeaways
- OpenID Connect Core 1.0 defines
subas a locally unique and never reassigned identifier within the issuer, at most 255 ASCII characters long.- RFC 7519 says
submust be either locally unique in the context of the issuer or globally unique, so you cannot assume global uniqueness.- Key your users on
(iss, sub). Treat email as an attribute that can change or be reused.- In Keycloak,
subis the user’s Keycloak ID by default (usually a UUID), and it is safe to use as a database key as long as you also store the issuer.
What is the JWT sub claim?
When a user signs in through OpenID Connect, the provider issues an ID token, a JSON Web Token (JWT) that describes the authentication. One of its required claims is sub, the subject, which identifies the end user. Access tokens issued by the same provider usually carry the same sub (in Keycloak 26.x it comes from the Subject (sub) mapper in the default basic client scope), which lets an API recognize who a request is for. Our post on ID tokens vs access tokens explains how the two differ, and JWT best practices covers validating them.
The OpenID Connect Core specification describes sub as a locally unique and never reassigned identifier within the issuer for the end user, intended to be consumed by the client. “Never reassigned” is a useful property: if a user deletes their account, the provider should not give the same sub to a different person later.
What is the rule for using sub?
Because sub is unique only within an issuer, the identity of a user is the combination of iss and sub.
For example, imagine your app accepts sign-ins from two providers, and a user signs in with each:
Provider (iss) |
sub |
Person |
|---|---|---|
https://idp-one.example |
1042 |
Dana |
https://idp-two.example |
1042 |
Marcus |
Both providers issued the subject 1042, and nothing in any standard forbids that. If your application stores only sub, Marcus signs in and lands in Dana’s account. If you store (iss, sub), they are two distinct users, as they should be.
The same logic applies within one provider that exposes several tenants or realms with different issuer URLs. Each issuer URL is its own namespace, and the pair is what identifies the user.
Why is email a bad join key?
A natural shortcut is to look users up by email address, because it looks like a unique, human-readable identifier. It is a poor key for several reasons:
- People change email addresses. An account keyed by email loses its history when the address changes.
- Addresses get reused. A company can give a departed employee’s address to a new hire.
- Not every provider verifies email. A provider that lets users enter any address without confirming it lets someone claim an address they do not own.
- Providers can assert any email. An attacker who controls an IdP, or a weakly verified one, can assert a victim’s email and be matched to the victim’s account.
That last point is a common route to account takeover, which our post on what is account takeover covers. A related failure in Keycloak’s account-linking step, where the linking proof was not bound to the upstream identity, is covered in our post on CVE-2026-82968. Email is useful as a hint during first-time account linking, if you require it to be verified by a trusted provider, but it should not be the long-term key.
What are public and pairwise subject identifiers?
OpenID Connect defines two types of subject identifier. A public identifier is the same for every client application: whichever app asks, the user has the same sub. A pairwise identifier is different for each sector, which by default is the host of the client’s redirect URI (section 8 of OpenID Connect Core), so applications on different hosts cannot compare their sub values to learn that they are talking about the same person.
Pairwise identifiers are a privacy feature. They prevent applications from colluding to track users across services. Keycloak’s mapper uses a salt, and changing or losing it changes every sub that client sees. The cost is that you cannot use sub to correlate a user across your own applications unless they share a sector identifier. In Keycloak, pairwise identifiers are available through the “Pairwise subject identifier” protocol mapper on a client.
How do brokering and federation affect sub?
An identity broker sits between your app and one or more upstream IdPs. The user signs in at an upstream provider, such as a corporate IdP, and the broker issues its own tokens to your app. There are two subject identifiers in play:
- The upstream
(iss, sub)that the corporate IdP issued to the broker. - The local
subthat the broker issues to your app.
The broker is responsible for mapping the first to the second. A sound broker stores the upstream issuer and subject together, tied to a local user, and when the same upstream pair returns, it finds the same local user.
The iss value is only trustworthy once you have verified the token’s signature with the keys configured for that provider, because any party can put any string in iss. If two configured providers can claim the same issuer string, for example when tenants register their own IdPs, a lookup on issuer and subject alone could match one provider’s user to another’s account. Scoping the lookup to the provider configuration that validated the token closes that gap.
In Keycloak, the broker stores this as a federated identity link: each link records the identity provider alias, the upstream user ID and the local user it belongs to. Because the link is keyed by the provider alias, the same upstream subject at two different configured providers produces two different links, and, since 26.7, Keycloak refuses to save an enabled identity provider whose issuer URL is already used by another one when either supports JWT authorization grants or federated client authentication. The identity providers feature page describes how this works on Skycloak, and our post on modeling token claims for a legacy IdP shows how claims map across a broker.
What does Keycloak put in sub, and is it safe as a database key?
By default, the sub claim in a Keycloak token is the user’s Keycloak ID. For users created in Keycloak that is a UUID generated at creation. Users served from an external user store without import have an ID of the form f:<storage-component-id>:<external-id>, where the middle part is the internal ID of the user storage provider you configured, which is one more reason to treat sub as an opaque string. The ID does not change when the user changes their username or email, and it is not reused, which makes it a good stable key.
A frequent question is whether it is safe to use Keycloak’s sub as the user ID in your own application database. The answer is yes, with one addition: store the issuer alongside it. In a single-realm setup the issuer only changes if the realm’s hostname or name changes, for example when you move to a custom domain. Because that can happen, map each trusted issuer URL to a stable provider ID in your own database and key users on (provider_id, sub). A domain change then means updating one row instead of every user, and the same design handles a second realm or a different provider later.
There are two Keycloak-specific cautions. If you use the pairwise mapper, the sub your app sees is not the user’s internal ID, so do not use it to call the Admin API. And if you import users from another system with preserved IDs, make sure those IDs are unique within the realm.
Checklist for application developers
- Store
issandsubtogether as the identity key, with a unique constraint on the pair. - Validate
issagainst the issuers you trust, and verify the signature with that issuer’s keys, before you readsubat all. - Do not match users by email alone. If you must link by email, require a verified email from a trusted provider and ask the user to confirm.
- Treat
subas an opaque, case-sensitive string. Do not parse it or assume a format, and size the column for at least 255 characters (OpenID Connect’s limit; plain JWTs have none). - Consider pairwise identifiers when privacy between your services matters.
- When you federate several IdPs, scope account lookups to the specific provider that handled the sign-in.
- Plan for migration. Keep an internal user ID of your own, and map
(provider_id, sub)to it as described above.
Frequently asked questions
Is the sub claim unique?
It is unique within the issuer that created it, not globally. Specifications allow two issuers to give the same sub to different people. Use (iss, sub) as the unique key.
What is the difference between sub and email in a JWT?
sub is a stable identifier designed for machines and never reassigned. email is a profile attribute that can change, be reused or be unverified. Use sub to identify the user and email for contact and display.
Can I use the sub claim as a user ID in my database?
Yes. Store it with the issuer (iss) so identities stay distinct if you add providers. With Keycloak, the default sub is the Keycloak user ID, which is usually a UUID and is stable.
What does the sub claim contain in Keycloak?
By default it is the Keycloak user ID, usually a UUID. If you add the pairwise subject identifier mapper to a client, sub becomes a derived value specific to that client or sector.
How long can a sub value be?
OpenID Connect Core limits it to 255 ASCII characters. Do not assume it looks like a number or a UUID, because other providers use different formats.
Does the access token carry the same sub as the ID token?
Usually yes for tokens from the same provider and client, which lets an API associate a request with a user. Confirm this for your provider, because some access tokens use a different subject for machine clients. In Keycloak 26.x the access token’s sub comes from the Subject (sub) mapper in the default basic client scope, so if a client’s access tokens have no sub, check that the basic scope is still assigned.