Reputation: 4539
I am working on a project where we are going to be using different services in a microservice architecture, and we would like to also use some Firebase services. I am working on an auth server that is going to mint custom JWT's for use in both Firebase, as well as the other API projects.
We would like to use the Firebase Auth SDK to easily integrate with FB, Google, Twitter etc, but we need to enrich the user's token with more data. Therefore, my thought process is that I'd create a Node.JS auth server that uses the Firebase Admin SDK to do this. The flow would be as follows:
Step 1-3 works fine. The problem arises when trying to verify the custom token on the other services.
TL;DR : There are two questions inhere:
kid
header?First, I am in doubt of the proper way to verify it. (Please excuse me, I'm not that experienced creating OAuth flows.) The algorithm used is RS256, so I should be able to verify the token using a public key. As I see it, there are two ways to get this key:
https: //securetoken.google.com/[PROJECT ID]/.well-known/openid-configuration
to retrieve the exponent and modulus for the correct kid
(key ID) and create the public key from those.
The token generated from the admin SDK by doing
admin.auth().createCustomToken(uid, additionalClaims).then(function(customToken)
with some custom claims looks something like this:
headers:
{
"alg": "RS256",
"typ": "JWT"
}
payload:
{
"claims": {
"premiumAccount": true,
"someRandomInnerObject": {
"something": "somethingRandom"
}
},
"uid": "<uid for the user>",
"iat": 1488454663,
"exp": 1488458263,
"aud": "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
"iss": "firebase-adminsdk-le7ge@<PROJECT ID>.iam.gserviceaccount.com",
"sub": "firebase-adminsdk-le7ge@<PROJECT ID>.iam.gserviceaccount.com"
}
I can't seem to get method 2 to work, though. One problem is that the generated token does not have a kid
header, and so does not conform to the OpenID spec (AFAIK), which leads to one of two options:
Problems with number 2:
kid
of the original token), and I can't seem to verify the generated token using the created PEM key for the kid.The way I do the latter is this (following a blog guide on the subject):
Go to https://www.googleapis.com/service_accounts/v1/jwk/[email protected] and retrieve the modulus (n) and exponent (e) for the relevant kid
Generate the public key using a lib (rsa-pem-from-mod-exp)
Use the key to verify using the 'official' jwt lib
The above results in a public key as such:
-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAxXpo7ChLMnv1QTovmm9DkAnYgINO1WFBWGAVRt93ajftPpVNcxMT MAQI4Jf06OxFCQib94GyHxKDNOYiweVrHVYH9j/STF+xbQwiPF/8L7+haC2WXMl2 tkTgmslVewWuYwpfm4CoQFV29OVGWCqwEcbCaycWVddm1ykdryXzNTqfzCyrSZdZ k0yoE0Q1GDcuUl/6tjH1gAfzN6c8wPvI2YDhc5gIHm04BcLVVMBXnC0hxgjbJbN4 zg2QafiUpICZzonOUbK6+rrIFGfHpcv8mWG1Awsu5qs33aFu1Qx/4LdMAuEsvX9f EmFZCUS8+trilqJbcsd/AQ9eOZLAB0BdKwIDAQAB -----END RSA PUBLIC KEY-----
Two things seem to be wrong. One is that the key is different from the one I can extract from the private key. The other is that the one I extract from the private key has these comments instead:
-----BEGIN PUBLIC KEY-----
-----END PUBLIC KEY-----
with no 'RSA'. Does this matter? In any case, it doesn't verify.
Finally, did I misunderstand the OpenID flow completely? Are the JWKs generated from a private key that I need as well to verify my JWTs? Should I expose my own JWKs on my auth server for the other services to contact and use instead of Google's? I'm a bit confused as to what the Firebase Admin SDK does and doesn't do, I think :-)
I know this is a lot of questions, but I think they're all related.
Some resources I've relied on in my research (besides the official admin sdk docs ofcourse):
Upvotes: 3
Views: 2405
Reputation: 4422
After re-authenticating the Firebase client SDK with the custom token, the client actually generates a new ID token with the claims from the custom token. This ID token is what you should use to verify requests made to your different microservices (documented here). So yes, your original ID token is discarded, but a new one is created in its place. And that ID token will be automatically refreshed every hour. So, you should be able to just call user.getToken()
to get a valid ID token whenever you need it. That method handles all the caching on your behalf.
Upvotes: 2