How to integrate the Microsoft Identity Platform (AAD or B2C) with custom JWT authentication for Realm Cloud in .NET

Wow that’s the longest blogpost title I have ever used. Hopefully this will help finding this page if you are searching for a solution I am describing here.

One of our customers came to us with a question how to integrate our B2C product with Realm Cloud. I had looked at this product before but didn’t know what technically was possible for integration with B2C.

The request was to use B2C tokens with the custom JWT Authentication Realm cloud supports. They support several ways to authenticate users to sync the offline database back to the cloud. By default they switched on Nickname authentication which is very easy to get started with (just pass a username and that user is logged in). For production use they recommend JWT Authentication.

And here comes the issue with B2C (or AAD) integration. Realm requires you to upload a public key to their server which can help proof the tokens are signed by you (with your private key). The customer was asking if they could send the B2C tokens to Realm backend directly since the users were already logging in to the apps with B2C. It’s fairly easy to download the public key from the well-known openid endpoint. There is a list of public keys you can find. The problem is that our private keys might rotate and/or replaced with new ones. This is something Realm doesn’t support. You have to upload 1 public key and that’s it.

Besides that you also have to problem that anybody can sign up for a B2C tenant and create valid JWTs signed by B2C with the same key. The JWT Settings in Realm Cloud do allow you to provide a set of required attributes and you should configure the audience with the clientid of your app to make sure only your tokens are accepted. But again, this will fail if we start rotating our keys (which might never happen)

image

So the solution is to create your own tokens, sign them with your own private key, upload your public key to their server and start sending your signed JWTs to login users.

These are the steps I did to make that work in .NET:

First you need to create a public/private keypair. The instructions are on the site of Realm, but I ran into an issue with the .NET code I wrote which requires P12 (pkcs12) format files. So I had to change the commands a little bit:

First line I entered to create an RSA token and a cert (which I need later)

openssl req -x509 -nodes -days 365 -sha256 -newkey rsa:4096 -keyout mycert.pem -out mycert.pem

Fill in the requested information (not important what you fill in here)

Export the public key with this command:

openssl rsa -in mycert.pem -pubout > mykey.txt

And create the certificate file (with private key), remember the password you entered:

openssl pkcs12 -out certificate.pfx -inkey mycert.pem -in mycert.pem

The following code will read the pfx (I renamed it to p12 file), creates a new JWT and signs it. This JWT is used to signin the user.

Read the cert (I am not sure if the last parameter has to be set like this Smile)

X509Certificate2 privateCert = new X509Certificate2(@"cert_key.p12", "password", X509KeyStorageFlags.Exportable);

setup the infrastructure to be able to sign the JWT

var securityKey = new Microsoft.IdentityModel.Tokens.X509SecurityKey(privateCert);
var credentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, "RS256");
var header = new JwtHeader(credentials);

Create the payload for the JWT. This one is really simpel. It only needs the userId. I have it hardcoded here but here is where you can integrate B2C or AAD by using the OID or SUB claim from the ID token you got back from the B2C or AAD endpoint. Don’t use the email address since that might change (or the name). OID is always unique per person even over different applications so it’s the best claim to use for this scenario.

var payload = new JwtPayload
{
  { "userId", "Pipo" }
};

And create the access token:

var secToken = new JwtSecurityToken(header, payload);
var handler = new JwtSecurityTokenHandler();
var tokenString = handler.WriteToken(secToken);

Now you can do your Realm stuff. Typically you would check if you already have a logged in user (the SDK does that for you) but if the current user is null you log the user in with the following lines (btw, the documentation of Realm has a bug where passing null as the last parameter results in an exception. So I changed the highlighted part)

var Realmcredentials = Credentials.Custom("jwt", accesstoken, ImmutableDictionary<string, object>.Empty);
var user = await User.LoginAsync(Realmcredentials, new Uri("https://mahoekst.us1.cloud.realm.io/"));

That’s it. Your user will show up in the Realm Studio as JWT user.

image

The BIG and IMPORTANT caveat here is, you need to figure out how to protect your private key, that’s something you don’t want others to get access too since they can start generating their own tokens with that.

Perhaps using B2C Identity Experience Framework policies to do  REST call to your server to sign a custom JWT which you pass as claim back to your client and use that to sign in might be a nice work around for this. I leave that exercise to the reader, please let me know if it works 🙂

Mission accomplished.