Using AD FS access tokens for workload identity federation
Workload identity federation supports OpenID Connect, so it should be compatible with AD FS. But until recently, workload identity federation didn’t work with AD FS-issued access tokens – only ID tokens worked properly. What was the issue there?
For ID Tokens, OpenID Connect is pretty specific about their format: They need to be JSON Web Tokens (JWTs), they must contain certain claims, and they must satisfy certain rules to be considered valid.
In contrast, OpenID Connect is pretty lax about access tokens: They can be JWTs, but they don’t have to. Many identity providers (including Google) use opaque JWTs, and that’s perfectly fine as far as OpenID Connect is concerned.
This lax-ness is a bit of a problem for workload identity federation when it receives tokens issued by external identity providers and needs to validate them. For ID tokens, it can follow the rules defined in the specification. But how is it supposed to validate access tokens?
As it turns out, workload identity federation simply applies the same rules: Whether we pass an ID token, access token, or any other kind of JWT to workload identity federation – in all cases, workload identity federation will follow (a slightly simplified set of) the OpenID connect rules to validate the token.
AD FS access tokens
AD FS access tokens are JWTs, so that’s a good start. But how similar are they to ID Tokens? Let’s take a look.
Here’s an ID token:
{
"typ": "JWT",
"alg": "RS256",
"x5t": "zxmfhQrUJynLMHh3Q0loFA5pSW0",
"kid": "zxmfhQrUJynLMHh3Q0loFA5pSW0"
}.{
"aud": "48b87db5-7bf1-4c9d-8464-4d149e75270e",
"iss": "https://login.example.net/adfs",
"iat": 1643959688,
"nbf": 1643959688,
"exp": 1643963288,
"auth_time": 1643959687,
"sub": "MBKAaGfXod0u6GOmSnFUmk9nW6wsPWxb70GF8+Z4jdE=",
"upn": "[email protected]",
"unique_name": "EXAMPLE\\bob",
"sid": "S-1-5-21-2588715005-111030-563750101-1109",
"apptype": "Public",
"appid": "48b87db5-7bf1-4c9d-8464-4d149e75270e",
"authmethod": "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
"ver": "1.0",
"scp": "openid"
}.[Signature]
And here’s an access token:
{
"typ": "JWT",
"alg": "RS256",
"x5t": "zxmfhQrUJynLMHh3Q0loFA5pSW0",
"kid": "zxmfhQrUJynLMHh3Q0loFA5pSW0"
}.{
"aud": "microsoft:identityserver:48b87db5-7bf1-4c9d-8464-4d149e75270e",
"iss": "https://login.example.net/adfs/services/trust",
"iat": 1643959688,
"nbf": 1643959688,
"exp": 1643963288,
"apptype": "Public",
"appid": "48b87db5-7bf1-4c9d-8464-4d149e75270e",
"authmethod": "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
"auth_time": "2022-02-04T07:28:07.359Z",
"ver": "1.0",
"scp": "openid"
}.[Signature]
There are obviously a number of differences, but one strikes out: the two tokens use a different issuer (iss
):
- The ID Token uses
https://login.example.net/adfs
as issuer. In compliance with the OpenID Connect Discovery specification, we can append/.well-known/openid-configuration
to that value and get the URL of the OIDC discovery document, which points us to the JWKS. - The access token uses
https://login.example.net/adfs/services/trust
as issuer, and trying to append/.well-known/openid-configuration
to that URL gets us nowhere.
This explains why workload identity federation failed to accept AD FS access tokens: It simply didn’t recognize them as coming from a trusted issuer.
There was a workaround for this issue: The URL https://login.example.net/adfs/services/trust
is the
federation service identifier,
and this identifier can be modified in the AD FS settings. But it’s a global setting that affects
all clients, so this workaround wasn’t great.
Luckily, this workaround isn’t necessary anymore. Workload identity federation now implements the AD FS-specific rules to validate access tokens, so that both ID tokens and access tokens can be used for authentication.
Updated documentation
I’ve updated the workload identity federation documentation to cover AD FS:
The documentation now also contains some details on how to combine workload identity federation with Integrated Windows Authentication (IWA), a topic we covered in a previous blog post.