Understanding Google’s IAP OAuth API Deprecation – Practical OAuth Guidance for Developers & Security Pros

Google made a change: In January 2025, they announced the deprecation of the Identity-Aware Proxy (IAP) OAuth 2.0 Admin API. In plain terms, this means you can’t programmatically create or manage IAP OAuth clients via API anymore. Going forward, when you secure an app with IAP, Google will auto-manage the OAuth client for you.

  • If you’ve never used the IAP OAuth API, you won’t notice much difference – your IAP-protected apps will just keep working (IAP already creates a client ID behind the scenes by default).

  • But if you had scripts or Terraform configs using that API – for example, automating google_iap_client or google_iap_brand resource creation – those are now broken since the API was fully shut down in July 2025. If you haven’t already, you’ll need to remove or replace those parts.

Why did Google do this? To simplify client management and reduce errors. Instead of requiring you to manually set up OAuth clients (and possibly get something wrong), IAP just handles it. From a security standpoint, this eliminates a class of misconfiguration (and saves time). It also ensures all IAP-protected apps in your organization default to an OAuth setup that only allows logins from your company’s Google Workspace users – a sensible default.

What about existing IAP OAuth clients? Good news: clients you created before aren’t being invalidated. Your applications that are already configured with custom IAP OAuth client IDs will continue to work without changes. You just won’t be able to update or create new ones via the API. If you do need to adjust something (say, tweak the consent screen), you’ll have to do it in the Google Cloud Console UI now.

Timeline with Brand Colors
  • Jan 22, 2025 – Deprecation Announced

    Google posts the deprecation notice. From this point, programmatic creation of IAP OAuth clients (via API) is discouraged and officially deprecated.

  • April 2025 – Support Ends

    The IAP OAuth Admin API is no longer supported by Google. Any issues won’t be addressed, and you’re expected to have migrated by now. The API might still function, but it’s on borrowed time.

  • July 2025 – API Shut Down

    The endpoint is turned off. Calls to create or manage IAP OAuth clients/brands will fail. IAP fully relies on its managed OAuth clients going forward. Any leftover Terraform resources or custom scripts trying to hit the old API will break.

If you never used or even heard of the IAP OAuth API, then no immediate action needed – but stick around, because this change is a great springboard to talk about OAuth 2.0 fundamentals that every developer and security engineer should know. Google’s assumption here is “we (Google) will handle the OAuth details for you.” That’s convenient, but understanding what’s happening under the hood is still crucial. So let’s refresh some OAuth basics and then see how things like IAP fit into the bigger picture.


OAuth 2.0 in a Nutshell (for the Busy Developer)

OAuth 2.0 is essentially a framework that lets one service authorize another service to access resources on behalf of a user. It’s often used for login (delegating authentication to an identity provider) or for API access (granting a client token to call an API). Here’s a quick rundown of core concepts – explained in practical terms:

Roles in OAuth (Who’s Who):

  • Resource Owner – The user who has the data or resource. Example: You, the user of a web app.

  • Client – The application requesting access to the resource on behalf of the user. Example: A todo-list web app that wants to access your Google Calendar.

  • Authorization Server (AS) – The service that actually authenticates the user and issues tokens. Example: accounts.google.com (Google’s OAuth server). It’s the “gatekeeper.”

  • Resource Server (API) – The service/resource the client wants to use, which validates tokens. Example: Google Calendar API, which will check that the token presented by the client is valid and has the right permissions.

In the context of Google IAP: the Authorization Server is Google’s system (it handles the login and token issuance), and your protected app is the Resource Server (it gets a token from IAP-proxy that it needs to validate).

OAuth Grant Types (How You Get a Token):

OAuth 2.0 defines a few methods (“flows”) for a client to obtain an access token. The main ones you should use today are:

  • Authorization Code Grant (with PKCE) – This is the go-to for apps that involve user login. The sequence is: the client app redirects the user to the auth server’s login page, the user logs in and consents, the auth server sends back a short-lived code to the client (via a redirect), and the client then exchanges that code for tokens (access token, and often a refresh token and/or an ID token). The PKCE extension (Proof Key for Code Exchange) is a must for public clients (like SPAs or mobile apps) – it’s an extra security step where the client proves it’s the one that initiated the request, thwarting interception attacks. In practice, if you use a modern OAuth library, PKCE happens under the hood (it will generate a random code verifier/challenge for you). Use Auth Code + PKCE for any app where a user signs in via OAuth.

  • Client Credentials Grant – This is for server-to-server communication (no user involved). The client here is typically a backend service or cron job. The client authenticates directly with the Authorization Server (usually using a client ID and secret, kind of like a username/password for the app) and gets an access token for a certain scope. Example: a microservice A needs to call microservice B – it can use client credentials to get a token that microservice B will accept. This flow is simple: one POST to the token endpoint with the client’s own credentials, and you get back an access token. Use this whenever an automated service or daemon needs to call an API.

  • Device Code (Device Authorization) Grant – Used for devices that don’t have a browser or easy input (think smart TVs). The device shows a code, you go to a URL on your phone or laptop to approve, and the device polls until it gets the token. You’d use this in niche cases (like TV apps, CLI tools, IoT).

You might hear about other grant types like Implicit and Resource Owner Password Credentials. Don’t use those anymore. They are considered deprecated/insecure in modern guidance. Implicit was a workaround for SPAs in the past; now SPAs should use Auth Code + PKCE. The password grant (where the app directly asks for the user’s username/password) is really only for legacy scenarios – it’s basically giving the app full credentials, which defeats a lot of the purpose of OAuth.

Tokens: Access, Refresh, ID – Keys to the Kingdom (and Why to Guard Them):

Once the client has completed a flow, it will receive one or more tokens. The most common are:

  • Access Token – Think of this as a key to access an API. The client doesn’t really “look at” what’s inside typically; it just holds onto it and presents it to the resource server when it wants to call an API. Access tokens usually have a limited lifetime (often ~1 hour) and are scoped to specific permissions. They often come as JWTs (JSON Web Tokens) or opaque strings. Important: The resource server (API) will check that the access token is intended for it (more on “audience” in a second) and that the token has the right scopes. Access tokens should be treated like passwords in transit – never leak them. Also, don’t confuse an access token with the user’s identity. It’s not who you are; it’s what you can do.

  • Refresh Token – A long-lived token that lets the client get a new access token without bothering the user again. For example, your mobile app might get a refresh token so the user doesn’t have to log in every hour after the access token expires. Refresh tokens can last days or months, but you must secure them tightly. If an attacker steals a refresh token, they can continue to get new access tokens (until it’s invalidated). Many providers now issue refresh tokens with rotation: every time you use it, you get a new one and the old one becomes invalid. That way, if one is stolen and used twice, the system knows and can shut it down. If you’re designing an app, prefer using refresh tokens (with rotation) over making very long-lived access tokens. It’s a good balance of security and usability.

  • ID Token – This is a token that carries identity information (who the user is). It’s a JSON Web Token (JWT) that typically comes with OpenID Connect (OIDC), which is an identity layer on top of OAuth 2.0. If you see an ID token, that’s meant for the client application itself. For instance, after a user logs in, your client might get an ID token so it can know the user’s name, email, etc., without another API call. ID tokens are not meant to be sent to your API for access to data (the access token is for that). However, in some scenarios like Google IAP, the distinction blurs: IAP will issue an ID token that your app receives and treats as proof of authentication. But notably, that ID token’s audience will be your application (or IAP) – meaning it’s intended to be used exactly in that context.

Here’s an analogy I like to use: An ID token is like a passport, and an access token is like a visa. The passport proves your identity (ID token says “this is Alice, authenticated by Google”). The visa is a specific permission to enter a country (access token says “Alice has read access to the Calendar API”). When you travel, you show your passport to verify who you are, but you also need a visa for the specific country you’re entering. A visa is only valid for the intended country – you can’t use a visa for France to enter Japan. Similarly, an access token is intended for a specific API or resource, and a good secure system won’t let you use it elsewhere. This brings us to audience and scopes.

Scopes and Audience (Permissions and Intended Audience):

When a token is issued, it often comes with scopes (what actions or resources it’s allowed) and an audience (which service it’s meant for).

  • Scopes: These are strings that represent permissions. For example, an OAuth token for Google Calendar might have a scope calendar.read indicating read-only access. If the client tries to do something outside that scope, the API should deny it. Scopes are how the user (or admin) grants a limited permission instead of full access. As a best practice, design your API with multiple narrow scopes rather than one big “do-everything” scope, so clients can request the least privilege they need. If you’re the one writing the client, only ask for what you truly need – users get suspicious (rightfully) if an app requests too broad access.

  • Audience: The audience is a claim in the token (often aud) that tells which resource server should accept this token. The authorization server (the issuer) will set aud to something like a unique identifier or URI of the service. For example, a token for Google Drive’s API might have "aud": "https://www.googleapis.com/drive/v3" (or some internal ID Google uses for Drive). If that token somehow reaches the Gmail API, Gmail should see the aud and say “this isn’t meant for me” and reject it. Every API should validate the audience on tokens it receives. This is one of the most important security checks in OAuth. Failing to do so is like accepting a visa for another country – it could let in a token that was never meant to access your service. In the Google IAP scenario, when your app gets that JWT from IAP, it must verify that the token’s audience matches your app’s expected identity (which is provided via IAP). If someone tries to use a token from a different app, it shouldn’t be accepted.

    • Audience isolation is why, for example, Auth0 requires you to specify an audience when requesting a token. If you don’t, it’ll give you just an ID token (for authentication) but no access token for an API. Auth0 wants you to be explicit: “I want a token for this API”. The token you get will only work for that API’s audience. If your app needs to call two different APIs, you’ll be getting two separate access tokens. This might seem slightly inconvenient, but it’s by design – it’s much safer to use two narrowly scoped tokens than one broad token that could be misused.

To summarize the best practice here: one access token per resource audience. Don’t reuse tokens across different services, and always check the audience on incoming tokens in your APIs. Also, always check the token’s issuer (iss) to make sure it’s from the provider you expect (e.g., tokens signed by accounts.google.com, or by your Okta domain, etc.).

Other OAuth Security Best Practices:

  • Use PKCE wherever recommended: As noted, PKCE is critical for public clients. Today, some providers even enforce PKCE for confidential clients too. It’s an easy win to prevent interception of the auth code. If you’re using a modern library, just verify that PKCE is turned on. (In code, you usually see it generate a code_challenge and code_verifier – if so, you’re good.)

  • Lock down redirect URIs: Always register the exact redirect URIs your app uses. Avoid wildcards. For example, if your app is at https://myapp.com/callback, register that exact URL. An open redirect URI is a common mistake that can let attackers steal codes or tokens by tricking the auth server into redirecting to their site.

  • Don’t use the Implicit flow: We said this above but it’s worth repeating – nowadays, there’s no good reason to use the old Implicit flow (where the token is returned in the URL fragment). It’s less secure and unnecessary with PKCE available. If you have older code using Implicit, plan a migration to Auth Code.

  • Keep tokens short-lived and refresh them: Access tokens should expire relatively quickly (often 1 hour or less). Rely on refresh tokens for long-lived sessions, and implement refresh token rotation if possible. That way if an access token is stolen, it’s only useful for a short window, and if a refresh token is stolen, the rotation mechanism can detect reuse. This is a key part of a robust security posture.

  • Secure storage: Never store tokens (especially refresh tokens) in places like localStorage where JavaScript can easily grab them if XSS is present. For SPAs, consider storing tokens in memory (and reauthenticate if page refresh clears it) or using secure HTTP-only cookies via a backend proxy. On mobile, use the secure keychain. On a server, keep them in memory or encrypted at rest. Basically, assume any token can potentially grant access and treat it with the same care as a password.

  • Logout / revocation: Have a plan for user logout or token revocation. Some providers allow you to revoke refresh tokens. At minimum, design your apps such that when a user logs out, you don’t keep using their token. (Tokens will naturally expire anyway; just don’t use a refresh token after logout.)

Alright, with that refresher in mind, you can see why Google’s change (auto-managing OAuth clients in IAP) is generally positive. It encourages secure defaults – short-lived tokens, proper audiences, etc., without the admin needing to configure them manually each time.

Now, let’s switch gears and talk about how OAuth concepts manifest in different platforms – specifically Google vs. Okta vs. Auth0 – since that’s often a point of confusion. If you grasp the fundamentals above, the differences are mostly terminology and some capabilities.


Same OAuth, Different Flavors: Google vs. Okta vs. Auth0

You might use Google Cloud, or an enterprise IdP like Okta, or a developer-focused service like Auth0 to handle authentication and authorization. These systems all implement OAuth 2.0 under the hood, but they of course use different terms and have slightly different ways to configure things. Let’s break down a few key concepts across these three:

  • Tenant / Domain: This is your identity “home base.” In Google Cloud, your Google Cloud Project is essentially where OAuth clients live (and it’s tied to a Google Cloud Organization for enterprise setups). In Okta, your Org (your Okta domain, e.g. dev-12345.okta.com) is the container for all your identity stuff. In Auth0, they literally call it a Tenant (e.g. yourcompany.us.auth0.com). It’s all the same idea: an isolated context for identities, applications, and configs.

  • Authorization Server: Google doesn’t really give each project its own OAuth server instance – when you use Google OAuth, you’re using Google’s central authorization service (accounts.google.com) for authenticating Google users. So we say Google’s is a global multi-tenant authorization service – your project just registers clients and sets up things like consent screen, but token issuance is by Google’s central system. Okta provides each org with a default Org Authorization Server (used for Okta’s own authentication and default OAuth tokens like for Okta’s API or SSO tokens), and (if you have the feature enabled) you can create Custom Authorization Servers. These custom AS are what you’d use to mint tokens for your own APIs, with your own domain as the issuer. For example, you could have https://yourorg.okta.com/oauth2/customas1 issuing tokens for an API you have. Each custom AS can have its own audience and scopes. Auth0 takes a slightly different approach: each Auth0 tenant effectively has one authorization server (issuer) – your Auth0 domain – but you configure multiple APIs within it. So tokens from yourtenant.auth0.com can be for API A or API B, distinguished by audience. You don’t run multiple separate auth servers in Auth0; you use one and just manage multiple resource servers within it.

  • OAuth Clients (Applications): In Google, you create OAuth 2.0 Client IDs through the Cloud Console, choosing the type (Web, iOS, etc.). Google’s expecting you to use these for Google’s own ecosystems (or maybe Google Identity Platform if you use Google as an IdP for your app’s users). In Okta, an Application is essentially an OAuth client (or SAML integration, etc., but focusing on OAuth). When you create an Okta Application, you specify if it’s a web app, SPA, native, service, etc., which translates to allowed grant types and whether it has a client secret. You also set which authorization server it uses and what scopes it can request. In Auth0, similarly, an Application represents an OAuth client. You mark it as a Regular Web App, SPA, Native, or Machine-to-Machine. This will determine some defaults (e.g., SPAs won’t have a secret and will require PKCE). In all cases, the concept of client ID and optional client secret is present. Google auto-generates these for you. Okta and Auth0 do as well when you register an application (and you can view the client ID/secret in their dashboards).

  • Scopes & Claims: Google has fixed scopes for its APIs (like Gmail, Drive, etc.), and if you use Google as an IdP for login (OIDC), you get standard OIDC scopes like openid, email, profile. You can’t arbitrarily define new OAuth scopes in Google’s system for your own APIs (unless you’re using something like Google Cloud Identity Platform to host your own users, which is a separate product). With Okta Custom Authorization Servers, you can define custom scopes – e.g. you could add a scope orders.read for your Orders API. You can also define custom claims in the tokens (like include the user’s department, or a boolean “isAdmin” flag, etc.) based on Okta user profile or group membership. Auth0 likewise lets you define scopes for each API you set up, and it has a feature to add custom claims (via Rules or Actions) to tokens. For instance, Auth0 can include user roles in the token if you enable that. In summary, Okta and Auth0 give you more flexibility to craft tokens containing the info and permissions your apps need. Google’s tokens are more rigid (great for Google’s services, but not meant to be a general-purpose IdP for all your apps unless you go into their Identity Platform realm).

  • Audience & Resource Servers: We talked about audience isolation – all three enforce it, just in different ways. With Google, if your app uses IAP, the token your app sees has an audience that is a unique identifier for your IAP application (effectively the OAuth client ID or a composite ID for that IAP resource). If you use Google OAuth to access Google APIs, the audience is implicitly the Google API you called (and Google’s backend will ensure the token’s scopes are valid for that API). With Okta’s custom AS, you set an Audience value for the authorization server (usually a URI like api://yourapi) when you create it, and all tokens from that AS will carry that aud. Your API should check for it. If you have multiple APIs with very different audiences, you might actually use multiple custom AS (though many orgs just use one custom AS and one audience, and then use scopes/claims to differentiate access – or use multiple audiences in a token if needed). Auth0, as mentioned, makes you specify an audience in the request. Each API you register in Auth0 has an identifier (like https://api.myapp.com), and that becomes the required audience in the token. Auth0 won’t issue a token with an audience unless you ask for it, which prevents accidental misuse.

Now, how about some practical guidance for each?

Google Cloud (IAP & OAuth)

Clients: For most Google Cloud use cases, you don’t manually create OAuth clients anymore when using IAP – Google does it. If you’re integrating with Google APIs, use Google’s client libraries and credentials (they handle token exchange, refresh, etc.). For IAP specifically, make sure your app verifies the JWT it receives: check Google’s signing keys and ensure the aud matches the IAP resource ID expected. Ease of use vs. control: Google’s approach is very convenient, but you trade some flexibility. If you need a custom OAuth flow (say you want to use Google as an IdP for your own non-Google API), you might instead use Google Identity Services or switch to an IdP like Okta/Auth0 for that part.

Okta (Enterprise IdP)

Custom Auth Server: If you plan to secure your own APIs, enable Okta’s Custom Authorization Server and create a dedicated authorization server for your API (or use the default one provided in Okta’s API Access Management). Define granular scopes that align with your API’s functionality, and add any custom claims your API will need in the token (e.g., user roles, tenant ID). Apps Configuration: Register applications in Okta and specify which authorization server and scopes they can use. For a machine-to-machine integration, you might create an Okta Service App and generate a client secret for it; for a web app, you’ll set it up for Auth Code + PKCE. Okta allows a lot of policy control too – you can require certain user groups to be the only ones who can get certain scopes, etc. Use those policies to enforce security (for instance, only allow the “admin” scope to be issued to clients that are trusted and users that are admins).

Auth0 (Developer-friendly IdP)

APIs & Audiences: Start by defining your API in Auth0 (name and identifier). Auth0 will issue access tokens with that identifier as the aud when requested. SPA / Mobile vs. Server: Use Auth0’s application types properly – e.g., set your SPA to “Single Page Application” so that it enforces PKCE and doesn’t expect a client secret. For a backend service or CLI, use “Machine to Machine” which lets you do client credentials. Token contents: Leverage Auth0 Rules or Actions to enrich tokens with useful info (but don’t bloat the token unnecessarily). For example, you can add the user’s roles or permissions into the token if your API needs that. Auth0 also has built-in role management which can map to scopes. And be mindful of token lifetime settings in Auth0 – you can configure how long access and refresh tokens last. Balance the user experience (not having to log in too often) with security (not keeping tokens alive too long).

The main thread across all three is knowing what maps to what: A Google Cloud Project’s OAuth client is like an Auth0 Application or Okta Application; Google’s IAP is kind of like a resource server (like an API) that issues its own tokens (in a limited way); Okta’s custom auth server is like having your own mini-OAuth issuer that you control; Auth0’s tenant is a one-stop shop where you define everything. But they all use the core OAuth standards underneath.

Now, let’s cement this with a few real-world examples and common pitfalls, to see how these concepts play out in practice.


Real-World Scenarios and Gotchas

It’s theory-to-practice time. Below are a few scenarios you might encounter and how to approach them (with potential pitfalls noted):

Scenario 1: Single-Page App (SPA) + API using Auth0
Imagine you have a React front-end (SPA) that talks to a back-end API you’ve built. You decide to use Auth0 for authentication. Here’s how it typically works:

  • Auth Flow: When a user needs to log in, your SPA will redirect them to Auth0 (probably using Auth0’s JS SDK which under the hood uses the Auth Code + PKCE flow). The user logs in (maybe with username/password or Google SSO, etc.), and Auth0 redirects back to your app with an authorization code. The Auth0 SDK on your SPA exchanges that code for tokens. You get an ID token (to know who the user is) and an access token to call your API. Importantly, when you initiated the login, you specified the audience of your API (e.g., audience: https://api.myapp.com in Auth0 config). That tells Auth0 to give you an access token intended for your API.

  • Using the Token: Now the SPA calls your API and includes the access token (typically in the HTTP Authorization header). Your API, on receiving a request, will validate the token. In this case, the token will be a JWT signed by Auth0, with an audience of https://api.myapp.com. You should verify the signature (Auth0 publishes JWKS keys for your tenant) and check that the aud matches your API’s identifier and that the token isn’t expired.

  • Pitfalls to avoid:

    • Make sure your SPA is indeed using PKCE (if you use Auth0’s SDK, it will). If you roll your own, don’t skip that step.

    • Don’t treat the ID token as an access token. For example, if Auth0 returns an ID token and an access token, use the access token for API calls. The ID token is just for the client’s use. (In our analogy, passport vs. visa: use the visa to go through the API gate!).

    • Also, don’t request scopes or audiences you don’t need. If your API only needs read access to something, don’t ask for global admin scopes. Users will see the consent (if any) and it’s good hygiene.

    • A common challenge: token storage in SPA. If you store the access token in browser local storage, it could be vulnerable to JavaScript injection attacks. Consider using memory or a secure cookie if possible. And if your SPA is single-domain with your API, you could use an authorization code flow that goes to your backend (though that’s a more advanced setup).

    • Token renewal: If your SPA needs to keep the user logged in without frequent reauth, you’ll use refresh tokens. Auth0 allows refresh tokens in SPAs (with rotation) if you enable it. Ensure that’s set up so the user isn’t prompted to log in after an hour. The refresh token will be stored securely (Auth0’s guidance is to store it in memory and, if the SPA is closed, the user will have to log in again – which is a tradeoff for security).

Scenario 2: Service-to-Service API Call using Okta
Now picture two backend services: Service A (a client) and Service B (an API provider). You want to secure Service B so that only Service A (and maybe a few others) can call it, and you want to use OAuth tokens rather than a static API key. Okta is your IdP.

  • Auth Flow: This is a textbook case for the Client Credentials flow. In Okta, you’d create an Application for Service A (likely a “Service” app or just a confidential client) and give it access to the necessary scope on a Custom Authorization Server that represents Service B’s API. For example, on your Okta Custom AS, you define a scope serviceB.call or something appropriate. You then configure Service A with the client ID and secret Okta provides.

  • Using the Token: Service A will request a token from Okta by calling the /token endpoint with its client ID/secret and requesting the serviceB.call scope. Okta will respond with an access token (JWT) that has aud set to the audience of Service B’s API (something you defined like api://serviceB) and contains the scope serviceB.call. Service A then includes this token in requests to Service B. Service B, upon receiving the request, must validate the token: check the signature (Okta’s keys), the issuer (yourorg.okta.com), the audience (api://serviceB), and that the scope serviceB.call is present (and any other claims you might care about).

  • Pitfalls to avoid:

    • Don’t skip the audience check. If Service B just checks the token signature and says “looks legit” without checking aud, then any token issued by your Okta (even for some other API) could potentially be used to access Service B. We want Service B to only trust tokens explicitly meant for it.

    • Protect the client secret on Service A. Treat it like a password (store in a secrets manager or secure config, not in code or repo).

    • Handle token caching: Service A should cache the access token until it’s near expiration to avoid hitting Okta for every request. But also handle refresh (in client credentials, you often just request a new token when the old one expires, since there’s no refresh token in this flow – you just use the client secret again).

    • Monitor usage: Okta’s system logs can show when tokens are issued. If Service A gets compromised and someone tries to misuse its credentials, you’d want to detect unusual token requests. Good monitoring and possibly limiting the network egress of Service A (so it can only call Okta and Service B endpoints it needs) can help.

    • Optional: You might consider mTLS or other binding for extra security. Okta supports Client Authentication with certificate too. This ensures that just stealing the client secret isn’t enough if the calls also require a certain client cert. That’s advanced, but worth mentioning if this is a particularly sensitive integration.

Scenario 3: Internal Web App protected by Google IAP
Let’s use the case that started this discussion. You have an internal web app running on GCP (could be on App Engine, Cloud Run, GKE, whatever), and you use Identity-Aware Proxy (IAP) to secure it. What happens and what do you need to know?

  • Auth Flow: With IAP, when a user tries to access your app’s URL, IAP (sitting in front) will check if the user is authenticated and allowed. If not, it triggers a Google OAuth flow. The user will log in with their Google account (typically restricted to your company’s Google Workspace domain), and possibly see a consent if it’s the first time (the “consent screen” that you might have configured in the Cloud Console). After that, IAP will let the request through, but it also injects an HTTP header (x-goog-iap-jwt-assertion) into the request to your app. That header contains a JWT – effectively an ID token asserting the user’s identity (and some info like email, user ID, etc.). The audience of this JWT is a identifier for your IAP application.

  • App Validation: Google IAP will do a lot for you: it’s keeping unauthenticated requests out and only sending you ones that have a valid JWT. However, for an extra layer (or if you’re not fully trusting IAP to always be in front), your app can also validate that JWT. Google provides the public keys and the expected audience (which in IAP’s case is usually the OAuth client ID of the IAP or a computed ID). Essentially, your app can double-check “did this request really go through IAP?” by verifying that token.

  • Programmatic Access: If a service (like a scheduled job or a command-line tool) needs to call your IAP-protected app, it needs a valid token as well. In Google’s world, that means it likely needs to obtain an ID token for the IAP-protected resource. This can be done with service account credentials (using gcloud auth print-identity-token --audiences=<your IAP audience> or with the Auth library). A common snag is developers accidentally try to use a normal OAuth access token (say, an access token for Cloud Run) to call an IAP app – that won’t work, because IAP expects that specific JWT with the IAP audience. The deprecation of the IAP API means you no longer manually set up the OAuth client for IAP, so you just use the one Google provides. If you need a service account to access it, you’d allowlist that service account’s client ID in the IAP settings for programmatic access (basically telling IAP “also trust tokens issued to this client ID”).

  • Pitfalls to avoid:

    • Not realizing that IAP tokens are ID tokens and thus slightly different in handling. For instance, libraries expecting access tokens might not parse the IAP JWT by default. You might need to manually validate it. Google’s docs have examples for this.

    • If you had previously set up IAP with your own client, be aware of the change: now it’s automatic. This mostly affects the setup process (one less thing to do), but after migration, check that everything works. Usually, it’s seamless, but if you baked the old client ID into any config (like allowed audiences in your code), update it to the new managed one.

    • Ensure your IAP access policies are correct. OAuth aside, IAP will only allow users who are authorized (you can restrict by Google Group, etc.). Make sure those groups are up to date; we’ve seen cases where someone left a company but their account was still in an allowed Google Group, and thus they technically still had access until group membership was corrected.

    • Audit logs: Use Google Cloud’s audit logs for IAP to monitor access. You’ll see who accessed the app and if any denied attempts occurred. This is more general security hygiene, but helps catch problems (e.g., someone trying to access who shouldn’t, or a misconfigured service trying and failing).


Wrapping Up (and Moving Forward)

OAuth can be a complex topic, but we’ve broken down the essentials and connected them to a very real change (Google’s IAP update). Google’s deprecation of the IAP OAuth API is part of a broader trend of cloud providers abstracting away identity details to improve security. It’s a nudge for all of us to continue to strengthen our identity and access practices.

Moving forward, here are a few key takeaways:

  • Stay informed on changes like this deprecation. Cloud services evolve, and it often revolves around security improvements. In this case, Google tightened up how IAP clients are managed. Tomorrow, another provider might announce, say, a new token format or deprecation of an old flow. When these come up, map them to the fundamentals you know (e.g., “Oh, they don’t want us using long-lived credentials, that aligns with zero-trust best practices”).

  • Keep OAuth best practices front-and-center in your app architecture. We’ve seen that things like audience checks, least-privilege scopes, and secure token storage are not just theoretical – they prevent real attacks (replay, token leakage, abuse across services). This continues to be a key area of focus for application security. It’s easier to build it right from the start than to retrofit security later.

  • Leverage your tools (in the positive sense of “leverage” – use them well!). If you’re on Google Cloud, use IAP or Cloud Identity Platform to save yourself from writing a lot of auth code. If you’re on Okta/Auth0, use their libraries and follow their guides – they’ve embedded a lot of best practices already (like enforcing PKCE and not allowing bad redirect URIs). We, as developers, don’t need to reinvent the wheel; we just need to configure the wheel correctly and understand where it’s taking us.

  • Think like a team: OAuth isn’t just a “backend team” concern or a “security team” concern. Frontend developers need to handle tokens correctly, backend developers need to validate them correctly, and security folks need to monitor and tune policies. Work together (we’ve all seen situations where an SPA dev was storing tokens in localStorage out of convenience – a conversation and some shared guidance can fix that and make the app more secure).

From a security standpoint, identity and access controls are the front door to everything. In our case, Google literally changed the locks (OAuth client) for IAP. By understanding why and how, we were able to calmly walk through what it means and what to do. OAuth is our friend, if we treat it right.

So, whether you’re adjusting to Google’s new approach with IAP, integrating a new app with Okta, or debugging token issues in Auth0, I hope this discussion helped clarify the landscape (and didn’t put you to sleep!). As always, if you have experiences or tips around these topics, I’d love to hear them – we’re all learning and improving together.

Moving forward, we’ll continue to build on these identity fundamentals and keep an eye on changes like these. They’re not just release notes – they’re reminders of why we implement security the way we do. Happy coding, and stay safe out there with those tokens!

Next
Next

Astaroth, stego C2, and why browser security helps — but won’t stop everything