IdentityServer Integration possible with JwtAuthProviderReader but needs small adjustment

Hi Demis,
in your documentation you mention a third party plugin to use ServiceStack together with IdentityServer. With your JwtProviderReader it’s actually already possible:

host.Plugins.Add(new AuthFeature(() => new AuthUserSession(),
	new[]
	{
		new JwtAuthProviderReaderCustom()
		{
			PublicKeyXml = _config.TokenSigningCertXml, // may also be fetched from https://demo.identityserver.io/.well-known/openid-configuration/jwks
			HashAlgorithm = "RS256",
			Audience = "xxxxx",
			ServiceRoutes = new Dictionary() // it throws a NullRef if not initialized :(
		}
	}
));

I’d propose to mention this in your documentation.

There are 3 small things to fix:

  1. ServiceRoutes initialization
    JwtAuthProviderReader throws if not initialized.

  2. “Aud” check should be contains and not ==
    The RFC (RFC 7519 - JSON Web Token (JWT))

The “aud” (audience) claim identifies the recipients that the JWT is
intended for. Each principal intended to process the JWT MUST
identify itself with a value in the audience claim

if (jwtPayload.TryGetValue("aud", out audience))
{
	if (!audience.Contains(Audience)) // <-- contains
		throw new TokenException("Invalid Audience: " + audience);
}

But i also see that contains might be problematic. But since in the RFC there is no separator defined, i think it’s the only viable option.

  1. Map values to session
    Claims are defined here: JSON Web Token (JWT)
    Since you call session.PopulateFromMap(jwtPayload); in CreateSessionFromPayload, it would make sense to map those Fields also in PopulateFromMap() like you do in JwtAuthProvider.CreateJwtPayload.
    In our case we had to fork JwtAuthProviderReader to map auth_time, nickname, given_name, family_name.

What dou you think about those changes?

I could do a PR, but just if you are interested…

cheers
tobi

1 Like

Sure would love a PR :slight_smile: I’ve never used Identity Server but it would be great value add if they could be integrated easily, happy to accept PRs which make this easier.

@tobi,

Thanks for your post regarding getting IdentityServer to work with ServiceStack. Can you please share how to use the JWKS data from IdentityServer to populate the PublicKeyXML field. It is the only thing holding me back.

Thanks.

update: Never mind, I was able to figure it out. You have to use IdentityModel.Base64Url.Decode to convert the signing keys as they modified the keys from the standard Base64 encoding mechanism.

@phantasm86, @tobi - do you think you could share how you got the detail out of the JWKS data from identity server?

TIA

Nic

Update: Never mind - I figured it out from @phantasm86’s clue:

For those that follow my code looks like this:

var configurationManager = new ConfigurationManager(appSettings.Get(“identityServerProvider.url”) + @"/.well-known/openid-configuration");
var config = configurationManager.GetConfigurationAsync().Result;
Issuer = config.Issuer;
Audiences.AddRange(new string[] { “discovery”, “api” });
HashAlgorithm = “RS256”;
PublicKey = new RSAParameters { Modulus = IdentityModel.Base64Url.Decode(config.JsonWebKeySet.Keys.First().N), Exponent = IdentityModel.Base64Url.Decode(config.JsonWebKeySet.Keys.First().E) };