Our software uses Google Firebase to provide a "single sign-on" experience for our enterprise clients who have implemented an OIDC (OpenID Connect) provider.
We have a problem with one client.
Their OIDC Discovery Document is located at:
https://abc-vis-cert-cloud3.ourclient.com/.well-known/openid-configuration.
In this Discovery Document, we were expecting an issuer claim of:
"issuer": "https://abc-vis-cert-cloud3.ourclient.com"
But their issuer claim is:
"issuer": "urn:VISCertC3:abc"
When we attempt to connect to their OIDC provider, Google Firebase (quite rightly) returns the error:
"INVALID_IDP_RESPONSE : issuer
claim in OIDC discovery document does not match the issuer specified in the request."
We have asked our client to fix the issuer
value in their discovery document, but they have said that they can’t….. they have hundreds of existing integrations relying on the current configuration, and all of those are working just fine.
My question:
Is there a workaround that we (or they) can implement to make this work?
i.e. Is there a Google Firebase setting? Or some kind of proxy we can implement? Or some way for them to display a different Discovery Document to us (and us alone) ?
There’s a similar question here regarding Azure B2C, and the answer in Azure B2C looks as simple as setting skipIssuerCheck: true
or strictDiscoveryDocumentValidation: false
. Another similar question here.
2
Answers
Skipping issuer check altogether should be considered a security issue, as then one has no guarantee if the token was issued by the right party. It would affect other clients which would have less strict token validation and introduce possibility to pass token issued by any party for the problematic client.
If there is absolutely no possibility to get it right, I’d rather suggest a solution in which you can handle white-listed exceptions.
Most of the back-end authentication libraries have possibility to hook into the token validation process and introduce custom validation logic. Your logic could be "if is token for client X, check if issuer field matches white-listed exception/override for X". Store your white-list as back-end configuration and you’re good to go.
I’m not sure Firebase Authentication can handle this natively. Like other StackOverflow users we also can’t comment on the internals of how Firebase handles these requests as we aren’t members of the Firebase Engineering team.
Per the OIDC spec, the discovery document should specify the issuer with the
https
(as they are doing) and use that same value in the tokens they issue (which they are not).In my loose testing, I was able to proxy the request coming from the Firebase Authentication client server using a HTTPS Cloud Function (2nd gen). This function would relay the request to the OpenID provider’s server (the one hosting the OIDC discovery document), download the response, mutate it and return it back to the Firebase Authentication client.
In theory, with an appropriate API key pair and mutations, this should allow you to override the issuer field and any other necessary fields to make Firebase Authentication think this is the proper discovery document.
The text that follows assumes you are familiar with Cloud Functions for Firebase and have followed the Getting Started guide on how to deploy functions before continuing.
The next three code blocks are all in the same file (e.g.
index.js
), but I’ve split them up here to help explain what each part is doing. They are split into three groups: dependencies & helper functions, configuration, and the request handler.Part 1: Dependencies & Helper Functions
This bit of code just allows for future expandability. During setup, you would call
registerMutation
to bind a pattern or string value to a callback that changes the response sent back to the client. When handling requests, you would instead callfindMutator
to check if the incoming request is supported and handle it when it is. Lastly,pick
allows for a handful of properties to be selected from an object, but only if they exist on that object with a defined, non-null value.Part 2: Mutators & Configuration
In this section, we register each mutation that we want to support.
In the below block, we are defining a mutator for requests targeting
abc-vis-cert-cloud3.ourclient.com
that overrides the issuer with a new value ("urn:VISCertC3:abc"
), leaving the rest of the properties unchanged. Other overrides can be made here as needed.Part 3: Handling Requests
In this block, we define the HTTPS Cloud Function (2nd gen) at the heart of this mutating proxy. This function expects to be called with a URL of the format
https://<DEPLOYED_CLOUD_RUN_DOMAIN>/<PROVIDER_SERVER_DOMAIN>/<PATH_TO_OIDC>
. For the given example, this would behttps://oidcproxy-<ID>-<REGION>.a.run.app/abc-vis-cert-cloud3.ourclient.com/.well-known/openid-configuration
.This function can currently handle up to 10 concurrent requests from any origin at a time per instance, thanks to the
{ cors: true, concurrency: 10 }
configuration. This can be adjusted as needed.Upon receiving an in-scope request (must match one of the configured patterns), it extracts the domain and path from the requested URL, assembles a HTTPS request to the OpenID provider’s server and executes it. Upon receiving a response, the response body is fed through the mutator before being sent back to the client.
The error handling below is not very robust, but it should handle most processing errors gracefully.
If you get errors related to formatting/linting when you try to deploy the above code, you can disable the linting check by removing the contents of the
"predeploy"
array nested under the"functions"
object in yourfirebase.json
file.Usage
Once your Cloud Function is deployed, you will be provided with the URL that your function is hosted at, such as
https://oidcproxy-3x4mpl3-uc.a.run.app
. Next, you will need the domain of the target OpenID provider’s server, in the provided example, this would beabc-vis-cert-cloud3.ourclient.com
. Lastly, you will need the path to the provider’s/.well-known/openid-configuration
document. Spec-compliant discovery documents should have it hosted at<issuer>/.well-known/openid-configuration
. For the given example, their document is located athttps://abc-vis-cert-cloud3.ourclient.com/.well-known/openid-configuration
.Using these components and the following format:
We can now construct the URL:
You should now be able to visit this URL to view the mutated OIDC discovery document.
Once you’ve checked its working, we need to trim off the
.well-known/openid-configuration
component before we can use it in the Firebase OIDC Provider setup instructions included below.Now you can test it by initialising an
OAuthProvider
for that service provider as described in the rest of the instructions.