Skip to main content
Version: 0.1.0

Names, pictures and what SAG is willing to say about somebody

SAG asserts one thing it can prove: an email address. Everything else on the profile scope either came from an upstream provider or was guessed, and the two should not look the same to a relying party.

Relaying from an upstream

When a person signs in through Microsoft or Google, their id_token usually carries a name and often a picture. Those are relayed to the relying party that asked for the profile scope, filtered through a fixed allow list:

name given_name family_name middle_name nickname
preferred_username picture locale zoneinfo

Nothing else crosses. That is not tidiness: an upstream that could put arbitrary claims into somebody else's id_token would be a way to attack the relying party through SAG. In particular sub and email are never taken from an upstream - SAG has already decided what those are - and a picture that is not an https URL is dropped, because it is a value a relying party will put in an <img>.

Narrow it further if a deployment has no business carrying any of it:

PROFILE_CLAIMS=name given_name family_name
PROFILE_PICTURE=false

PROFILE_PICTURE is separate from the list because it is the one claim with a side effect beyond SAG: a relying party rendering it makes every one of its pages fetch an image from Google or Microsoft. It is also the only reason the Content-Security-Policy on SAG's own screens allows https: images at all, so switching it off narrows that too.

The email code path, where there is nothing to relay

A person who signs in with an email code has given SAG one fact about themselves. There is no name to relay, and a relying party that wants to greet somebody has three options: print the raw address, ask for a name of its own, or guess.

The honest position is that name is supposed to mean the person's name, and a guess dressed as one is worse than no claim at all. So inference is off by default. It is offered because the alternative deployments actually reach for is worse: jamie.taylor@example.org printed into a greeting, or every relying party building its own profile form.

PROFILE_NAME_FROM_EMAIL=infer

With that set, the local part is read the way people actually write addresses:

AddressGuess
jamie.taylor@Jamie Taylor
jamie_taylor@, jamie-taylor@Jamie Taylor
j.taylor@J. Taylor
jamie.taylor+shopping@Jamie Taylor
jamie@Jamie

And it errs firmly towards saying nothing. A wrong guess is worse than no guess, so all of these produce no name at all:

  • anything with a digit in it - jamie2 might be a second Jamie or a birth year, and there is no way to tell;
  • a machine identifier: a long hexadecimal string, a UUID, a bare number;
  • a role address - admin, support, no-reply, postmaster and the rest - which names a function rather than a person;
  • a single letter, or more than four words, or anything that is not letters.

A separator is all it has to go on. Addresses are folded to lower case before any of this happens - which is what stops one person holding two accounts - so there is no case boundary left to split on and jamietaylor@ becomes "Jamietaylor", one word.

It will get other things wrong too. van.der.berg becomes "Van Der Berg", and mcdonald becomes "Mcdonald". Which is why:

A guess is labelled as one

Whenever name came from the address rather than from an upstream, the token carries an extra claim:

{
"name": "Jamie Taylor",
"urn:sag:name_inferred": true
}

There is no standard claim that means "this is our best guess", so this is a namespaced one. A relying party that ignores it gets a sensible default; one that reads it can treat the value as something to confirm on first sign-in rather than as a fact, which is what it is. It appears in claims_supported in the discovery document only when inference is switched on, like everything else there.

Avatars, and why not Gravatar

PROFILE_AVATAR_FALLBACK=initials

Somebody with no upstream picture gets a small SVG with their initials on it, inline as a data: URI - about 450 bytes, deterministic, so the colour does not change between sign-ins. It is only drawn once there is a name to draw from: initials taken from an opaque local part would be noise.

The obvious alternative is Gravatar or one of its equivalents, and it is not offered. Those need the address, usually as an MD5 or SHA-256 hash, which hands a third party a record of every person who signs in anywhere on the deployment - and a hashed address is not anonymous, because the space of real addresses is small enough to enumerate. An identity provider is the last place that trade makes sense. An SVG drawn locally tells nobody anything.

On SAG's own screens

The "continue signing in" and "sign out?" screens show the name and picture SAG holds, because a person confirming an account recognises a name and a face faster than they parse an address. The address is always shown as well, since that is the thing actually being asserted.

PROFILE_SHOW_ON_SCREEN=false

turns that off without affecting what relying parties receive - for a deployment where a shared machine might show somebody's name to the next person at it.