Apigee with Firebase user authentication

We'd like to use Apigee to authenticate API calls from a 3P web app. At a high-level, the user authentication flow will look like: 

1) Web app calls API on behalf of a user

2) Apigee receives request and forwards to authentication service

3) Authentication service confirms user's credentials and issues encrypted cookie indicating the user is authenticated

4) Future web app calls to API on behalf of user include the cookie

5) Apigee confirms cookie is valid by hitting the authentication service's verify endpoint (which decrypts the cookie with the same secret key and returns a 200 response)

6) API access is granted

Please note that the web app can't own any of the user management due to product boundaries. 

So the questions here are: 1) For analytics, what would be the best way to connect the authenticated user to an Apigee user? Should we programmatically create an Apigee authentication user from the Apigee APIs in the authentication service and then pass this back from the verify endpoint to Apigee? 

2) Is there a better way to implement this authentication flow?

Solved Solved
3 18 499
1 ACCEPTED SOLUTION

I believe the approach you suggested could work with some modifications. Both our authentication service and Apigee will sit behind a reverse proxy (Nginx). The browser / web app will redirect the user to log in as needed through the reverse proxy. When the user logs in, the authentication service could perform the id for token exchange you described above with Apigee and then return the access token back to the browser as a cookie. When the user logs out, the authentication service can call an Apigee endpoint to invalidate the access token. I believe the authentication service would need to be registered as a developer app with Apigee to enable this flow, where all the OAUTH endpoints are locked down with API keys?

yes, all of that makes sense

We also need Apigee to forward the user's firebase UUID to our backend APIs, which I believe can be achieved by attaching custom metadata to the access token through Apigee.

yes, you can do that.

Our main limitation is that the auth app is built separate from the web app so we need some form of cookie-based session management since the web app is not directly integrated with the Firebase app. (Note: the web app will set in a VPC that is only accessible through our reverse proxy).

I dont quite understand this part, but it seems like you do, so that's fine.

1. How would refreshing the access token work? Is this something Apigee can do itself when it sees an expired token and attach the new access token as a cookie in the proxied API response?

The way I have seen this implemented is

  • for each inbound request from the app, Apigee verifies the token (passed as Cookie, I guess, but that doesn't matter so much)
  • when the access token is expired, Apigee returns a 401
  • the app is smart enough to redirect to the authn app when 401 happens
  • if the authn is Firebase, then it's SSO and the signin will be automatic, and a new ID token gets returned to the app
  • the app can again present the ID token to get a new access token.

All of this happens automatically with no user interaction, though there will be 3 or 4 API requests, which can take a second or so in aggregate. So you might want to build that into the app automaticaly, so that it maintains a fresh access token in the background and doesn't wait for reject/expired. It doesn't relieve you from building the logic to handle the active reject, because access tokens will sometimes expire, and sometimes be revoked explicitly, so the app needs to handle those cases.

2. Can an API proxy be secured with both an API key and access token (if API key exists, then verify key and apply quota based on key, if access token exists, then verify access token and apply quota based on access token)?

Yes, you can implement conditional credential checking. There was a similar question this week on this topic; my response.

We need to do this so that developers can interact with the APIs using the developer portal as well.

I think that is not correct. You should be able to allow the use of tokens in the developer portal. You'd need the users to signin to your auth service. It's a little subtle - the developer is one authenticated person, and the USER of the API is a different authenticated person, so you'd need to clearly document that, and explain it . But developers are smart, and they're the only ones who will need to understand that and negotiate that path, so they'll figure it out. 

At the very least you could ask the dev to PASTE IN a token that they obtain via a UX that is somehow disconnected from the developer portal.  That would work though it would be klunky.  But, I think should be able to avoid even that, with the right configuration of your portal. 

View solution in original post

18 REPLIES 18

Is there a better way to implement this authentication flow?

YES

Here's what I suggest

The user operating the Webapp signs in to Firebase Auth (aka Google Cloud Identity Platform). The result of the signin is an ID token.

Webapp calls into a token-dispensing Endpoint managed by Apigee, sending in the ID token as a "credential".

In JS code it might look like this:

 

    const payload = {
      grant_type: constants.GRANT_TYPE,
      client_id: constants.APIGEE_CLIENT_ID,
      assertion: firebase_auth_id_token
    };
    fetch(constants.APIGEE_ENDPOINT + constants.TOKEN_PATH, {
        method: "POST",
        headers: {
          Accept: "application/json, text/plain, */*",
          "Content-Type": "application/x-www-form-urlencoded"
        },
        body: new URLSearchParams(payload)
      })
      .then(async (res) => [res.status, await res.json()])
      .then(([status, json]) => {
          // error handling elided
          const accessToken = json.access_token;
          ...
      })

 

To handle this request, Apigee verifies the ID token. Checks that it is (a) signed by firebase, (b) has the proper issuer, (c) is not expired, and (d) whatever else you need to check. (For step (a), checking the signature, this requires access to the public keys used by Firebase Auth. These are available at https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com , and can be cached. ) If everything looks good, then in response, Apigee sends back an "access token" (not a cookie). The access token has a lifetime. It should be not longer than the remaining lifetime of the ID token. (This ID-token-for-access-token exchange is a normal OAuth2 token dispensing endpoint, but it's not OpenID Connect.)

Subsequently, when the webapp needs to call an API managed by Apigee, the webapp sends in the access token in the Authorization header.

Apigee validates the access token (fast, requires no contact to firebase). And grants service if the token is valid.

As with a cookie, the access_token can be associated on the server side (within Apigee) to arbitrary state data. So if you need to retain a username or email, or something else, then you can attach that data to the token within Apigee. The webapp can't see that data - the token is opaque. Like a cookie would be.

As a twist, you might want to make a finer-grained authorization decision within Apigee during service handling. It's not enough to say "has this person logged into Firebase auth?" You might need an authorization engine to say "given this person has signedin to Firebase auth, what should I allow them to do?"

And that is the topic of a screencast I recently put together. Check it out. https://youtu.be/DaeytPjh6ys

For analytics, what would be the best way to connect the authenticated user to an Apigee user? Should we programmatically create an Apigee authentication user from the Apigee APIs in the authentication service and then pass this back from the verify endpoint to Apigee?

hmmm. There are no "end users" provisioned in Apigee. There are developers, but those are generally not the same group of people who are USING the apis. Developers are the people who build the 3P apps. In Apigee, developers "own" the client ID, the identifier of the client app. That will be a string that gets embedded into the webapp that developer builds. Basically it identifies which app is calling the Apigee APIs. It's informational, not for "authentication". If you need something stronger than this, you can configure Apigee to check that the client ID is being used only from a given Origin. (the developer's domain at which the webapp is launched).

So in the request-for-token exchange I described above, the webapp will send the embedded client id, as well as the ID token. The first IDENTIFIES but does not authenticate the webapp itself, the second (ID token) serves as an authentication credential for the end user of the web app.

Hi Dino,

Thanks for the detailed response! I updated my original question to reflect this constraint but the web app can't own any of the user management due to product boundaries so we were thinking of Apigee as a reverse proxy that could provide access to the APIs after authenticating the user. 

Regarding analytics, we'd like to understand how individual users of the web app are interacting with the APIs (indirectly). Does this mean we need to associate each authenticated user with their own client app? If so, are there any recommendations for how to associate a firebase UUID with a client app in Apigee?

the web app can't own any of the user management due to product boundaries

Not sure exactly what this means, but...  I think a good practice is that the webapp  prompts for user login.  

we were thinking of Apigee as a reverse proxy that could provide access to the APIs after authenticating the user. 

Right - that's what I tried to explain above.  Firebase auth authenticates the user, and that results in an ID Token. Then Apigee can exchange that for an access token. I hope that is clear.

The web app has no concept of user authentication. From the web app’s
perspective, it is just making API calls to the API proxies.

Hmm, well I'll be interested to hear what you come up with !

Hi Dino,

Thinking about this a bit more, I believe the approach you suggested could work with some modifications. Both our authentication service and Apigee will sit behind a reverse proxy (Nginx). The browser / web app will redirect the user to log in as needed through the reverse proxy. When the user logs in, the authentication service could perform the id for token exchange you described above with Apigee and then return the access token back to the browser as a cookie. When the user logs out, the authentication service can call an Apigee endpoint to invalidate the access token. I believe the authentication service would need to be registered as a developer app with Apigee to enable this flow, where all the OAUTH endpoints are locked down with API keys?

On subsequent requests to Apigee, the browser will attach the access token as a cookie that Apigee can then validate. We also need Apigee to forward the user's firebase UUID to our backend APIs, which I believe can be achieved by attaching custom metadata to the access token through Apigee. Our main limitation is that the auth app is built separate from the web app so we need some form of cookie-based session management since the web app is not directly integrated with the Firebase app. (Note: the web app will set in a VPC that is only accessible through our reverse proxy). So in terms of questions: 

1. How would refreshing the access token work? Is this something Apigee can do itself when it sees an expired token and attach the new access token as a cookie in the proxied API response?

2. Can an API proxy be secured with both an API key and access token (if API key exists, then verify key and apply quota based on key, if access token exists, then verify access token and apply quota based on access token)? We need to do this so that developers can interact with the APIs using the developer portal as well. These developers will sign in to the portal using their firebase credentials (as described here). Alternatively, would it make more sense to bundle the same proxies into two API products? One API product that manages auth through access tokens and the other product that manages auth through API keys? 

 

 

 

 

I believe the approach you suggested could work with some modifications. Both our authentication service and Apigee will sit behind a reverse proxy (Nginx). The browser / web app will redirect the user to log in as needed through the reverse proxy. When the user logs in, the authentication service could perform the id for token exchange you described above with Apigee and then return the access token back to the browser as a cookie. When the user logs out, the authentication service can call an Apigee endpoint to invalidate the access token. I believe the authentication service would need to be registered as a developer app with Apigee to enable this flow, where all the OAUTH endpoints are locked down with API keys?

yes, all of that makes sense

We also need Apigee to forward the user's firebase UUID to our backend APIs, which I believe can be achieved by attaching custom metadata to the access token through Apigee.

yes, you can do that.

Our main limitation is that the auth app is built separate from the web app so we need some form of cookie-based session management since the web app is not directly integrated with the Firebase app. (Note: the web app will set in a VPC that is only accessible through our reverse proxy).

I dont quite understand this part, but it seems like you do, so that's fine.

1. How would refreshing the access token work? Is this something Apigee can do itself when it sees an expired token and attach the new access token as a cookie in the proxied API response?

The way I have seen this implemented is

  • for each inbound request from the app, Apigee verifies the token (passed as Cookie, I guess, but that doesn't matter so much)
  • when the access token is expired, Apigee returns a 401
  • the app is smart enough to redirect to the authn app when 401 happens
  • if the authn is Firebase, then it's SSO and the signin will be automatic, and a new ID token gets returned to the app
  • the app can again present the ID token to get a new access token.

All of this happens automatically with no user interaction, though there will be 3 or 4 API requests, which can take a second or so in aggregate. So you might want to build that into the app automaticaly, so that it maintains a fresh access token in the background and doesn't wait for reject/expired. It doesn't relieve you from building the logic to handle the active reject, because access tokens will sometimes expire, and sometimes be revoked explicitly, so the app needs to handle those cases.

2. Can an API proxy be secured with both an API key and access token (if API key exists, then verify key and apply quota based on key, if access token exists, then verify access token and apply quota based on access token)?

Yes, you can implement conditional credential checking. There was a similar question this week on this topic; my response.

We need to do this so that developers can interact with the APIs using the developer portal as well.

I think that is not correct. You should be able to allow the use of tokens in the developer portal. You'd need the users to signin to your auth service. It's a little subtle - the developer is one authenticated person, and the USER of the API is a different authenticated person, so you'd need to clearly document that, and explain it . But developers are smart, and they're the only ones who will need to understand that and negotiate that path, so they'll figure it out. 

At the very least you could ask the dev to PASTE IN a token that they obtain via a UX that is somehow disconnected from the developer portal.  That would work though it would be klunky.  But, I think should be able to avoid even that, with the right configuration of your portal. 

Thanks, this is all very helpful!!

For the developer portal, since we want developer-level analytics to be collected I think it would be better for developers to use API keys rather than the token? Otherwise analytics would be collected for the app authorized to generate the OAUTH tokens (which in our case is the authentication service)?

> I dont quite understand this part, but it seems like you do, so that's fine.

To explain this a bit more, the web app that end users interact with is separate than the authentication web app (rather than integrating firebase auth into the web app itself). But since both the authentication web app and APIs the web app calls are all behind the same reverse proxy, I believe the cooking setting flow would work.

 


@sk123 wrote:

For the developer portal, since we want developer-level analytics to be collected I think it would be better for developers to use API keys rather than the token? Otherwise analytics would be collected for the app authorized to generate the OAUTH tokens (which in our case is the authentication service)?


Yes, I see.  That all makes sense.


@sk123 wrote:

the web app that end users interact with is separate than the authentication web app (rather than integrating firebase auth into the web app itself). But since both the authentication web app and APIs the web app calls are all behind the same reverse proxy, I believe the cooking setting flow would work.


Got it! 

One more follow up question: would it be more efficient to proxy Firebase login requests through Apigee instead of calling the token generation endpoint from the authentication service? 

After Firebase confirms the user's credentials are valid, the Apigee proxy could generate or refresh the existing access token? 

[warning: i'm not an expert!] Ive seen that the firebase/google identity platform endpoints for login have some per-IP rate limiting that might hurt you here. We've had issues where scripts and things that didn't cache tokes and just constantly hit the login endpoint more than like a small number of times times a minute would get blocked. proxying all of your users thru apigee would probably hit that quickly I'm guessing.

I suppose maybe the "admin" sdk equivalent endpoints have higher limits. I'd just recommend testing it cause I've had problems here 🙂

I think Firebase auth (Google Identity Platform) has some pretty low rate limits if you don't have a billing account set up.  Maybe that is what you saw?  For example, if you have no billing instrument, then you can do 10 SMS signins per day.  If you DO have a billing instrument, there is no limit. 

 

Ah thanks, this is a good thing to keep in mind. I was more concerned about potential effects on Firebase session management and whether it is recommended for Apigee to generate refresh tokens on its own.

 


@sk123 wrote:

would it be more efficient to proxy Firebase login requests through Apigee instead of calling the token generation endpoint from the authentication service? 


Hmmm, I think I've lost the plot here. I don't know what is calling what in your scenario.   So I can't contribute anything here.


@sk123 wrote:

After Firebase confirms the user's credentials are valid, the Apigee proxy could generate or refresh the existing access token? 


In my experience, using the Firebase auth SDK in a web app, the user signs in when the webapp calls  signInWithPopup() (or similar), and then the result of that is the ID Token. I think that's what you're doing too.  And then if I understand your flow correctly, I think you send that ID token in as a credential to an Apigee proxy, that dispenses an Apigee access token. At that point you also get a refresh token with the access token. 

And the app can send in the refresh token to the Apigee proxy, to get a new access token. Of course you have to support the refresh_token grant type in your API proxy, to make this happen. 

 

Hmmm, I think I've lost the plot here. I don't know what is calling what in your scenario.   So I can't contribute anything here.

So originally we were thinking our backend authentication service would call the Apigee generate token endpoint after verifying user credentials but I was wondering if it would be more efficient to proxy that login endpoint in the authentication service through Apigee such that Apigee forwards the request to login to the authentication service and once the authentication service confirms the user's credentials, Apigee would generate a token in the PostFlow of the proxy. 

 

So what I’m suggesting is that the login button in the “sign up with popup” points to an Apigee proxied login rather than to Firebase directly. 

what I’m suggesting is that the login button in the “sign up with popup” points to an Apigee proxied login

Are you talking about "sigin IN with Popup"?

That's a firebase SDK thing, right? It's one of the methods in the Firebase auth module. How would you proxy THAT? Isn't the endpoint sort of embedded into the Firebase SDK, predicated on the configuration you provide

So originally we were thinking our backend authentication service would call the Apigee generate token endpoint after verifying user credentials

I think I am still not clear on what is calling what, or why. Maybe if you produced a sequence diagram that explained the actors and the messages they exchanged.

but I was wondering if it would be more efficient to proxy that login endpoint ...

You're weighing these alternative designs in the pursuit of efficiency... For myself, I don't think "efficiency of login" is going to be a critical measure of success for most systems. How often will that happen, and how many concurrent transactions will there be? I suppose it will be "not very often" and "not very many" in contrast to the OTHER transactions, which are many and often. Though I could be wrong. If login happens rarely, then it's not worth bending your architecture into unusual shapes to accommodate the efficiency. But as I said, despite your verbal explanations, I don't understand what is calling what, and how, so I can't offer an opinion on whether one option is better than the other.

In my experience, if we are talking about a web app

  • a user visits a page that provides a service, but which requires login
  • the page logic calls signinWithPopup from firebase/auth
  • There is a signin flow, that points to firebase logic.  Firebase Auth (aka Identity Platform) handles this flow
  • After completion, the original page receives an ID token of the authenticated user, or ... nothing if the user declined to signin.
  • The page then goes about its business. One option is: It can send that ID token into a service that grants an access token, and then request service using that access token. 

like this:

screenshot-20240401-122901.png

Or, possibly, there is a separate service involved that grants access tokens.  Maybe it looks like this: 

screenshot-20240401-123300.png