In OIDC,
roughly speaking and as I understand it, there are three possible
roles: the identity provider ('OP'), a Client or 'Relying Party'
(the program, website, or whatever that has you authenticate with
the IdP and that may then use the resulting authentication information),
and what is sometimes called a 'resource server', which uses the
IdP's authentication information that it gets from you (your client,
acting as a RP). 'Resource Server' is actually an OAuth2 term, which
comes into the picture because OIDC is 'a simple identity layer'
on top of OAuth2 (to quote from the core OIDC specification). A website
authenticating you with OIDC can be described as acting both as a
'RP' and a 'RS', but in cases like IMAP authentication with
OIDC/OAuth2, the two roles are separate; your mail client is a RP,
and the IMAP server is a RS. I will broadly call both RPs and RSs
'consumers' of OIDC tokens.
When you talk to an OIDC IdP to authenticate, you can get back
either or both of an ID Token and
an Access Token.
The ID Token is always a JWT with some claims
in it, including the 'sub(ject)', the 'issuer', and the 'aud(ience)'
(which is what client the token was requested by), although this
may not be all of the claims you asked for and are entitled to. In general, to verify an ID Token (as a
consumer), you need to extract the issuer, consult the issuer's
provider metadata
to find how to get their keys, and then fetch the keys so you can
check the signature on the ID Token (and then proceed to do a number
of additional verifications on the information in the token, as
covered in the specification). You may
cache the keys to save yourself the network traffic, which allows
you to do offline verification of ID Tokens. Quite commonly, you'll
only accept ID Tokens from pre-configured issuers, not any random
IdP on the Internet (ie, you will verify that the 'iss' claim is
what you expect). As far as I know, there's no particular way in
OIDC to tell if the IdP still considers the ID Token valid or to
go from an ID Token alone to all of the claims you're entitled to.
The Access Token officially doesn't have to be anything more than
an opaque string. To validate it and get the full set of OIDC claim
information, including the token's subject (ie, who it's for), you
can use the provider's Userinfo endpoint.
However, this doesn't necessarily give you the 'aud' information
that will let you verify that this Access Token was created for use
with you and not someone else. If you have to know this information,
there are two approaches, although an OIDC identity provider doesn't
have to support either.
The first is that the Access Token may actually be a RFC 9068 JWT. If it is, you
can validate it in the usual OIDC JWT way (as for an ID Token) and
then use the information inside, possibly in combination with what
you get from the Userinfo endpoint. The second is that your OAuth2
provider may support an RFC 7662 Token Introspection
endpoint. This endpoint is not exposed in the issuer's provider
metadata and isn't mandatory in OIDC, so your IdP may or may not
support it (ours doesn't, although that may change someday).
(There's also an informal 'standard' way of obtaining information
about Access Tokens that predates RFC 7662. For all of the usual reasons,
this may still be supported by some large, well-established OIDC/OAuth2
identity providers.)
Under some circumstances, the ID Token and the Access Token may be
tied together in that the ID Token contains a claim field that
you can use to validate that you have the matching Access Token.
Otherwise, if you're purely a Resource Server and someone hands you
a theoretically matching ID Token and Access Token, all that you
can definitely do is use the Access Token with the Userinfo endpoint
and verify that the 'sub' matches. If you have a JWT Access Token
or a Token Introspection endpoint, you can get more information and
do more checks (and maybe the Userinfo endpoint also gives you an
'aud' claim).
If you're a straightforward Relying Party client, you get both the
ID Token and the Access Token at the same time and you're supposed
to keep track of them together yourself. If you're acting as a
'resource server' as well and need the additional claims that may
not be in the ID Token, you're probably going to use the Access
Token to talk to the Userinfo endpoint to get them; this is typically
how websites acting as OIDC clients behave.
Because the only OIDC standard way to get additional claims is to
obtain an Access Token and use it to access the Userinfo endpoint,
I think that many OIDC clients that are acting as both a RP and a
RS will always request both an ID Token and an Access Token. Unless
you know the Access Token is a JWT, you want both; you'll verify
the audience in the ID Token, and then use the Access Token to
obtain the additional claims. Programs that are only getting things
to pass to another server (for example, a mail client that will
send OIDC/OAuth2 authentication to the server) may only get an
Access Token, or in some protocols
only obtain an ID Token.
(If you don't know all of this and you get a mail client testing
program to dump the 'token'
it obtains from the OIDC IdP, you can get confused because a JWT
format Access Token can look just like an ID Token.)
This means that OIDC doesn't necessarily provide a consumer with a
completely self-contained single object that both has all of the
information about the person who authenticated and that lets you
be sure that this object is intended for you. An ID Token by itself
doesn't necessarily contain all of the claims, and while you can
use any (opaque) Access Token to obtain a full set of claims, I
believe that these claims don't have to include the 'aud' claim
(although your OIDC IdP may chose to include it).
This is in a sense okay for OIDC. My understanding is that OIDC is
not particularly aimed at the 'bearer token' usage case where the
RP and the Resource Server are separate systems; instead, it's aimed
at the 'website authenticating you' case where the RP is the party
that will directly rely on the OIDC information. In this case the
RP has (or can have) both the ID Token and the Access Token and all
is fine.
(A lot of my understanding on this is due to the generosity of
@Denvercoder9 and others after
I was confused about this.)
Sidebar: Authorization flow versus implicit flow in OIDC authentication
In the implicit flow,
you send people to the OIDC IdP and the OIDC IdP directly returns
the ID Token and Access Token you asked for to your redirect URI, or rather has the person's browser do it.
In this flow, the ID Token includes a partial hash of the Access
Token and you use this to verify that the two are tied together.
You need to do this because you don't actually know what happened
in the person's browser to send them to your redirect URI, and it's
possible things were corrupted by an attacker.
In the authorization flow, you
send people to the OIDC IdP and it redirects them back to you with
an 'authorization code'. You then use this code to call the OIDC
IdP again at another endpoint, which replies with both the ID Token
and the Access Token. Because you got both of these at once during
the same HTTP conversation directly with the IdP, you automatically
know that they go together. As a result, the ID Token doesn't have
to contain any partial hash of the Access Token, although it can.
I think the corollary of this is that if you want to be able to
hand the ID Token and the Access Token to a Resource Server and
allow it to verify that the two are connected, you want to use
the implicit flow, because that definitely means that the ID
Token has the partial hash the Resource Server will need.
(There's also a hybrid flow
which I'll let people read about in the standard.)