4
votes

I'm using Firebase to authenticate users in my app. Firebase generates a JWT token that I need to authentify on my server. I use tyk.io to do it. Tyk supports these tokens but requires the data source for the public keys to be in the https://tools.ietf.org/html/rfc7517 format.

Is there an easy way to get this directly from Google/Firebase?

I know I can get the keys from https://www.googleapis.com/service_accounts/v1/metadata/x509/[email protected] but this is not the expected format.

I can also get jwk from https://www.googleapis.com/service_accounts/v1/jwk/[email protected] which is the right format but doesn't contain the keys (I need the keys in a X5c field, so X.509 Certificate Chain)

1
It is wrong to say that "which is the right format but doesn't contain the keys": "n" and "e" parameters are the public key (modulus and public exponent). A "x5c" field is a certificate containing the key, but Firebase does not use, nor need to use an X.509 certificate to encapsulate and publish the key since the RSA public key publication is done by means of a SSL/TLS service using a certificate signed by a valid CA. So getting the JWK from googleapis.com... is a way to give you the key in a secure fashion. Therefore, no need to have a certificate and google does not provide one.Alexandre Fenyo
Thanks for your comment. My question might not have been precise enough. I need a x5c field in the jwks I get from Google/firebase. Unfortunately that's the only format Tyk is able to handle. I just want to know if it easily possible or not?Pierre Leonard
In the Tyk documentation, there are only two ways that are offered to check the signature of the token: RSA public key and HMAC using a shared secret, they do not talk about a certificate: tyk.io/docs/security/your-apis/json-web-tokens So please can you explain why you think a x5c field would be necessary?Alexandre Fenyo
The interesting part is at at tyk.io/docs/security/your-apis/json-web-tokens/… Furthermore you can check the relevant Tyk gateway code at github.com/TykTechnologies/tyk/blob/…Pierre Leonard
Thanks for pointing me to googleapis.com/service_accounts/v1/jwk/…, couldn't find it anywhere in Google's documentationdarnmason

1 Answers

7
votes

As you noticed reading the Tyk library on GitHub, Tyk has chosen to only support RSA keys published within a certificate, in JWK sets.

PRELIMINARY NOTES:

The RSA key that Google uses to sign a JSON Web token is taken among one of the two keys published at https://www.googleapis.com/service_accounts/v1/jwk/[email protected]

Those two keys are the ones that are used to make the two certificates at https://www.googleapis.com/service_accounts/v1/metadata/x509/[email protected]

Note that these certificates are self-signed, here is the content of one of them:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 4804715264884888226 (0x42adc713b25c52a2)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: CN=securetoken.system.gserviceaccount.com
        Validity
            Not Before: Apr  2 21:20:50 2019 GMT
            Not After : Apr 19 09:35:50 2019 GMT
        Subject: CN=securetoken.system.gserviceaccount.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:cc:7c:14:e6:5c:95:94:4b:95:74:0d:47:9d:e1:
                    [...]
                    60:d1
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage: critical
                TLS Web Client Authentication
    Signature Algorithm: sha1WithRSAEncryption
    [...]

As you can see, the issuer has the same value than the subject, therefore the certificate chain is made of a single self-signed certificate. This means that you can not check the validity of this certificate using a Certificate Authority: there is no global well-known Certificate Authority that has signed the public RSA keys from Google to make those certificates. But the certificates are downloaded by means of SSL/TLS from a Google web server that is authenticated with a certificat that is signed by GlobalSign, so downloading the certificates using SSL/TLS is sufficient to be sure those certificates contain the RSA keys that Google uses to sign the JWT.

The certificates are valid only during a few weeks and have an overlapping period of validation to avoid clock skews, and there is a rolling key mechanism that is applied nearly every two weeks.

ANSWER TO YOUR QUESTION:

You need to create the public-key source yourself for Google to work with Tyk: you must create a document on your application server that contains the JWK set. We suppose you publish this JWK set here: https://my-application-server.com/jwks.json

So, in your Tyk API Definition, in the JWT secret field, you need to put this JWK set URL: https://my-application-server.com/jwks.json.

For that, see the part about JWT secret field in this page: https://community.tyk.io/t/multiple-auth-schemes-for-single-api-definition/694/4

You need to refresh this document every week, because Google does roll the keys about every two weeks.

This document can be made with the following shell command, using only curl and the JSON Command Line Processor named jq:

curl -s 'https://www.googleapis.com/service_accounts/v1/metadata/x509/[email protected]' | jq '[ to_entries | .[] | {alg: "RS256", kty: "RSA", use: "sig", kid: .key, x5c: (.value | sub(".*"; "") | sub("\n"; ""; "g") | sub("-.*"; "")) } ] | {"keys": .}'

Here is the output of this command line:

% curl -s 'https://www.googleapis.com/service_accounts/v1/metadata/x509/[email protected]' | jq '[ to_entries | .[] | {alg: "RS256", kty: "RSA", use: "sig", kid: .key, x5c: (.value | sub(".*"; "") | sub("\n"; ""; "g") | sub("-.*"; "")) } ] | {"keys": .}'
{
  "keys": [
    {
      "alg": "RS256",
      "kty": "RSA",
      "use": "sig",
      "kid": "7d2f9f3fb83d6337497b6f7cd2cff4dfa5c2e8b8",
      "x5c": "MIIDHDCCAgSgAwIBAgIIQq3HE7JcUqIwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UEAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTkwNDAyMjEyMDUwWhcNMTkwNDE5MDkzNTUwWjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tlbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMx8FOZclZRLlXQNR53h+mYYjo9X4Hi38dAsLrP2iRzt8PowOnqx7hxpr9Ag7NKTSAOXTUAmObCRkEooRLe6jcScu+nttpjqqvtOyzhTl2szbdsiRhveXmOwMMbZ/DCkXll6i2NoeeRxVy4qxMDMPM6pokjsUyuq9wkWH+fiUG8rYSZVhpCqfOtqoTBu0zf+PyuGDPlLKKkE6Y7WKp5Go/tIMS6kTz5vhnA3M0HNWdZDy+06kT0eTxajzcs/QXiqmMzJEsJ7ln0Qh1KnOZwMH578y9GYM3ytMdiybDQM0c4cWtLp0REjlKyzqh0LLA7fmdjmEmIgOSy1wzftcKnfYNECAwEAAaM4MDYwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADggEBAFpLrULiau0nFLjRXxt9lwjgft1elBt265Hu88TU3132Wbh00Yvm7hlOA34gVIVNPcgT5lCxc4qVLXbA6yPtnyDubvULmVxyCSKfX/2M4Td+yPF99xR3VVCpATPAVjtOo819Q/Of+icQlmSoy/I0lFJewwrqcRc0eC9UgzF+EvGXHzIfoNlQgFjf/fnG1OV7d2Zr8bj+Jk/zZwVRstKCTrPgyqCYe/y7PU9q0aIQnMRvYKdLj/TfaBolQ12Tlb2j+nGMrPH5uVsUUu6nZluhaMvmlhp8glvslmjlXCIuce/N8FSw7zVf/ofRrDzTw98N1DZG2aLRyRYdmsXDBqMaac8="
    },
    {
      "alg": "RS256",
      "kty": "RSA",
      "use": "sig",
      "kid": "ff1df5a15b5cf582b61a21385c0cfaedfdb6a748",
      "x5c": "MIIDHDCCAgSgAwIBAgIIcqcNMyhOv18wDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UEAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTkwMzI1MjEyMDUwWhcNMTkwNDExMDkzNTUwWjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tlbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKnDRgOsHY55P2i7wX6Uh8YO/rBWmGgVgeBqCowbHopF5HA1EdF0NXSmBn1Myg+DLFqxf0nvsi8B5pjd/rNtVPX6OUIkAKGPyva/d2aQqsHliCFh81QDs2vHIeIAThbQZP514t/z2M79SWR7A0vRYcWf+I+eTL6Vf1nqO3cFwTJVtwiTPom+NjZKx7ukowtqm1mVef+FqceC5zx8D5wzLLtUx/tMPvnXaysSjNN+86cPKc02kumCqlt7Yuf5G9VptjAVqsQyL/X5WuIVuzkFrfh/IPidw7Wzm3s5u928aLNSNbFpEpeXqJ8utD4AZnfm9mg6PYNtFWIB/L6xf21iZOECAwEAAaM4MDYwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADggEBAEdXAGI27mRT+Qkz9vbGlGbKJZKLG6b6c/SAQkOZJDOX4sJ16WgbfsixGBEWmvMMiEwhWV0hbhTiEC22xUd7cRP6veRlKba4rKoYA0x3GJhpDpAviuKPFkCanLFlFOXouexQIxBudWm32B8YFpLW9ds8m9yFmQZwE5GZs1hJ1jb2z2u7AuZNdQnaQ7mKy7Vmt3yHb9NjPlvmQ//ijFR3Lw4XB3nlYcL77mjjd3fhBodfUFkPJpsY2wOQpRLxDbvjHsXSfsgQ+/a+9IjKj0F2YOu9HnvjTXCopWObA9AGV6HR2L6RGaRLTwH+xV2El8UrzTYFPPN/URLui3XtaGdiREg="
    }
  ]
}

The content of this output must be saved in a file corresponding to https://my-application-server.com/jwks.json