Meet industry peers, ask questions, collaborate to find answers, and connect with Googlers who are making the products you use every day.<\/p>", "imageupload.max_uploaded_images_per_upload" : 100, "imageupload.max_uploaded_images_per_user" : 10000, "integratedprofile.connect_mode" : "", "tkb.toc_maximum_heading_level" : "2", "tkb.toc_heading_list_style" : "disc", "sharedprofile.show_hovercard_score" : true, "config.search_before_post_scope" : "community", "tkb.toc_heading_indent" : "15", "p13n.cta.recommendations_feed_dismissal_timestamp" : -1, "imageupload.max_file_size" : 10024, "layout.show_batch_checkboxes" : false, "integratedprofile.cta_connect_slim_dismissal_timestamp" : -1 }, "isAnonymous" : true, "policies" : { "image-upload.process-and-remove-exif-metadata" : true }, "registered" : false, "emailRef" : "", "id" : -1, "login" : "Former Community Member" }, "Server" : { "communityPrefix" : "/qsqph94282", "nodeChangeTimeStamp" : 1732557918994, "tapestryPrefix" : "/gc", "deviceMode" : "DESKTOP", "responsiveDeviceMode" : "DESKTOP", "membershipChangeTimeStamp" : "0", "version" : "24.7", "branch" : "24.7-release", "showTextKeys" : false }, "Config" : { "phase" : "prod", "integratedprofile.cta.reprompt.delay" : 30, "profileplus.tracking" : { "profileplus.tracking.enable" : false, "profileplus.tracking.click.enable" : false, "profileplus.tracking.impression.enable" : false }, "app.revision" : "2409051714-s8dac8f1df8-b80", "navigation.manager.community.structure.limit" : "1000" }, "Activity" : { "Results" : [ ] }, "NodeContainer" : { "viewHref" : "https://www.googlecloudcommunity.com/gc/Articles-Information/ct-p/cloud-community-articles", "description" : "", "id" : "cloud-community-articles", "shortTitle" : "Articles & Information", "title" : "Articles & Information", "nodeType" : "category" }, "Page" : { "skins" : [ "googlecloud", "theme_hermes", "responsive_peak" ], "authUrls" : { "loginUrl" : "https://www.googlecloudcommunity.com/gc/user/userloginpage?dest_url=https%3A%2F%2Fwww.googlecloudcommunity.com%2Fgc%2FCloud-Product-Articles%2FMaking-sense-of-API-Product-configuration%2Fta-p%2F77416", "loginUrlNotRegistered" : "https://www.googlecloudcommunity.com/gc/user/userloginpage?redirectreason=notregistered&dest_url=https%3A%2F%2Fwww.googlecloudcommunity.com%2Fgc%2FCloud-Product-Articles%2FMaking-sense-of-API-Product-configuration%2Fta-p%2F77416", "loginUrlNotRegisteredDestTpl" : "https://www.googlecloudcommunity.com/gc/user/userloginpage?redirectreason=notregistered&dest_url=%7B%7BdestUrl%7D%7D" }, "name" : "TkbArticlePage", "rtl" : false, "object" : { "viewHref" : "/gc/Cloud-Product-Articles/Making-sense-of-API-Product-configuration/ta-p/77416", "subject" : "Making sense of API Product configuration", "id" : 77416, "page" : "TkbArticlePage", "type" : "Thread" } }, "WebTracking" : { "Activities" : { }, "path" : "Community:Google Cloud Community/Category:Google Cloud/Category:Articles & Information/Board:Cloud Product Articles/Message:Making sense of API Product configuration" }, "Feedback" : { "targeted" : { } }, "Seo" : { "markerEscaping" : { "pathElement" : { "prefix" : "@", "match" : "^[0-9][0-9]$" }, "enabled" : false } }, "TopLevelNode" : { "viewHref" : "https://www.googlecloudcommunity.com/gc/Google-Cloud/ct-p/google-cloud", "description" : "Find answers, ask questions, and connect with our community of experts.", "id" : "google-cloud", "shortTitle" : "Google Cloud", "title" : "Google Cloud", "nodeType" : "category" }, "Community" : { "viewHref" : "https://www.googlecloudcommunity.com/", "integratedprofile.lang_code" : "en", "integratedprofile.country_code" : "US", "id" : "qsqph94282", "shortTitle" : "Google Cloud Community", "title" : "Google Cloud Community" }, "CoreNode" : { "conversationStyle" : "tkb", "viewHref" : "https://www.googlecloudcommunity.com/gc/Cloud-Product-Articles/tkb-p/cloud-articles", "settings" : { }, "description" : "Looking for tips, tricks, and tutorials? Find detailed articles on how to best use Google Cloud products here. To subscribe to notifications, click Topic Options at the top right and click Subscribe.", "id" : "cloud-articles", "shortTitle" : "Cloud Product Articles", "title" : "Cloud Product Articles", "nodeType" : "Board", "ancestors" : [ { "viewHref" : "https://www.googlecloudcommunity.com/gc/Articles-Information/ct-p/cloud-community-articles", "description" : "", "id" : "cloud-community-articles", "shortTitle" : "Articles & Information", "title" : "Articles & Information", "nodeType" : "category" }, { "viewHref" : "https://www.googlecloudcommunity.com/gc/Google-Cloud/ct-p/google-cloud", "description" : "Find answers, ask questions, and connect with our community of experts.", "id" : "google-cloud", "shortTitle" : "Google Cloud", "title" : "Google Cloud", "nodeType" : "category" }, { "viewHref" : "https://www.googlecloudcommunity.com/", "description" : "The official home of Google Cloud and Workspace community forums, learning hub, and community blogs.", "id" : "qsqph94282", "shortTitle" : "Google Cloud Community", "title" : "Google Cloud Community", "nodeType" : "Community" } ] } }; LITHIUM.Components.RENDER_URL = "/gc/util/componentrenderpage/component-id/#{component-id}?render_behavior=raw"; LITHIUM.Components.ORIGINAL_PAGE_NAME = 'tkb/v2_4/ArticlePage'; LITHIUM.Components.ORIGINAL_PAGE_ID = 'TkbArticlePage'; LITHIUM.Components.ORIGINAL_PAGE_CONTEXT = 'LRDuRjENY5s27_ELGEepzGvuiDqPB1qGLg5M7dzsrMbtpjva04Wb0bajH2jophOG40OWFD3D7s-qfY4BiXmnVvKKNPgBeuXdrEImsLzoLLZXg8Q4y16wMV98nZ6ccPIJUw92CTKiVc9wZupNRK46-P1mTNSp9gWEyXikwUmNep1vYnMtsCowcD67YQnz5J0YR7Q2FhZ8vIAqrndJqCksOqoJxrM68_ojaHe8Krs5hiwCCffQykDgBmxQlF1mY5n56MMd0IQVCBLMXwxpZNpIdTaC62lsnkHKblHXrS_SzsTMKOShOEYDbrKcBkQLykmWduVUf2kJLs-npD_pTxrAEEg1nlxPhJBRiMM8wiBtQ7E.'; LITHIUM.Css = { "BASE_DEFERRED_IMAGE" : "lia-deferred-image", "BASE_BUTTON" : "lia-button", "BASE_SPOILER_CONTAINER" : "lia-spoiler-container", "BASE_TABS_INACTIVE" : "lia-tabs-inactive", "BASE_TABS_ACTIVE" : "lia-tabs-active", "BASE_AJAX_REMOVE_HIGHLIGHT" : "lia-ajax-remove-highlight", "BASE_FEEDBACK_SCROLL_TO" : "lia-feedback-scroll-to", "BASE_FORM_FIELD_VALIDATING" : "lia-form-field-validating", "BASE_FORM_ERROR_TEXT" : "lia-form-error-text", "BASE_FEEDBACK_INLINE_ALERT" : "lia-panel-feedback-inline-alert", "BASE_BUTTON_OVERLAY" : "lia-button-overlay", "BASE_TABS_STANDARD" : "lia-tabs-standard", "BASE_AJAX_INDETERMINATE_LOADER_BAR" : "lia-ajax-indeterminate-loader-bar", "BASE_AJAX_SUCCESS_HIGHLIGHT" : "lia-ajax-success-highlight", "BASE_CONTENT" : "lia-content", "BASE_JS_HIDDEN" : "lia-js-hidden", "BASE_AJAX_LOADER_CONTENT_OVERLAY" : "lia-ajax-loader-content-overlay", "BASE_FORM_FIELD_SUCCESS" : "lia-form-field-success", "BASE_FORM_WARNING_TEXT" : "lia-form-warning-text", "BASE_FORM_FIELDSET_CONTENT_WRAPPER" : "lia-form-fieldset-content-wrapper", "BASE_AJAX_LOADER_OVERLAY_TYPE" : "lia-ajax-overlay-loader", "BASE_FORM_FIELD_ERROR" : "lia-form-field-error", "BASE_SPOILER_CONTENT" : "lia-spoiler-content", "BASE_FORM_SUBMITTING" : "lia-form-submitting", "BASE_EFFECT_HIGHLIGHT_START" : "lia-effect-highlight-start", "BASE_FORM_FIELD_ERROR_NO_FOCUS" : "lia-form-field-error-no-focus", "BASE_EFFECT_HIGHLIGHT_END" : "lia-effect-highlight-end", "BASE_SPOILER_LINK" : "lia-spoiler-link", "BASE_DISABLED" : "lia-link-disabled", "FACEBOOK_LOGOUT" : "lia-component-users-action-logout", "FACEBOOK_SWITCH_USER" : "lia-component-admin-action-switch-user", "BASE_FORM_FIELD_WARNING" : "lia-form-field-warning", "BASE_AJAX_LOADER_FEEDBACK" : "lia-ajax-loader-feedback", "BASE_AJAX_LOADER_OVERLAY" : "lia-ajax-loader-overlay", "BASE_LAZY_LOAD" : "lia-lazy-load" }; LITHIUM.noConflict = true; LITHIUM.useCheckOnline = false; LITHIUM.RenderedScripts = [ "Sandbox.js", "DropDownMenu.js", "Auth.js", "jquery.function-utils-1.0.js", "DropDownMenuVisibilityHandler.js", "Loader.js", "jquery.appear-1.1.1.js", "NoConflict.js", "jquery.ui.resizable.js", "Throttle.js", "ActiveCast3.js", "jquery.placeholder-2.0.7.js", "prism.js", "jquery.ui.draggable.js", "jquery.ui.widget.js", "jquery.iframe-transport.js", "jquery.viewport-1.0.js", "Events.js", "jquery.ui.core.js", "jquery.fileupload.js", "jquery.delayToggle-1.0.js", "OoyalaPlayer.js", "Globals.js", "jquery.blockui.js", "PartialRenderProxy.js", "SearchAutoCompleteToggle.js", "jquery.lithium-selector-extensions.js", "LiModernizr.js", "ForceLithiumJQuery.js", "Video.js", "AjaxSupport.js", "jquery.scrollTo.js", "InformationBox.js", "jquery.tools.tooltip-1.2.6.js", "jquery.ui.position.js", "ElementMethods.js", "MessageBodyDisplay.js", "jquery.autocomplete.js", "EarlyEventCapture.js", "json2.js", "DeferredImages.js", "Placeholder.js", "jquery.iframe-shim-1.0.js", "Tooltip.js", "Text.js", "ElementQueries.js", "DataHandler.js", "jquery.js", "Forms.js", "Namespace.js", "SearchForm.js", "AjaxFeedback.js", "jquery.effects.core.js", "jquery.ui.mouse.js", "Link.js", "jquery.clone-position-1.0.js", "Lithium.js", "CustomEvent.js", "jquery.effects.slide.js", "Cache.js", "Dialog.js", "ResizeSensor.js", "jquery.ui.dialog.js", "CookieBannerAlert.js", "PolyfillsAll.js", "jquery.ajax-cache-response-1.0.js", "AutoComplete.js", "HelpIcon.js", "Components.js", "jquery.css-data-1.0.js", "jquery.hoverIntent-r6.js", "jquery.json-2.6.0.js", "jquery.tmpl-1.1.1.js", "jquery.position-toggle-1.0.js", "SpoilerToggle.js" ];(function(){LITHIUM.AngularSupport=function(){function g(a,c){a=a||{};for(var b in c)"[object object]"===Object.prototype.toString.call(c[b])?a[b]=g(a[b],c[b]):a[b]=c[b];return a}var d,f,b={coreModule:"li.community",coreModuleDeps:[],noConflict:!0,bootstrapElementSelector:".lia-page .min-width .lia-content",bootstrapApp:!0,debugEnabled:!1,useCsp:!0,useNg2:!1},k=function(){var a;return function(b){a||(a=document.createElement("a"));a.href=b;return a.href}}();LITHIUM.Angular={};return{preventGlobals:LITHIUM.Globals.preventGlobals, restoreGlobals:LITHIUM.Globals.restoreGlobals,init:function(){var a=[],c=document.querySelector(b.bootstrapElementSelector);a.push(b.coreModule);b.customerModules&&0

Making sense of API Product configuration

An Apigee Edge API Product lets you restrict access to proxy resources based on some combination of proxies and resource paths. When you add a Product to a Developer App, you can use the keys generated for that App to enforce this restricted access.

When you create a Product, Edge lets you specify zero or more proxies and zero or more resource paths to define the "restrictions" you wish to achieve.

But wait, what does it mean if you do not specify any proxies? Or, if you don't specify any resource paths? Or, for that matter, if you omit both? This is a frequent source of confusion for Edge developers!

API Products 101

Here's a common Product setup. Let's say you have a proxy called StreetCarts, which lets you find information about local food carts. The basepath of this proxy is /streetcarts.

Now, let's say you want to allow access only to certain resources that the proxy knows about. You can do this by adding both the proxy and the resources you wish to allow to an API Product. For example, here's what the Product UI would look like, where the resource paths /reviews, /carts, and /menus are listed and the proxy StreetCarts is specified:



Using an API key from a Developer App associated with this Product, the following call will succeed (assuming ABCD is a valid key and the VerifyApiKey proxy is used in the proxy). It succeeds because the basepath of the StreetCarts proxy is /streetcarts and both the proxy and the resource path /carts are in the Product:

GET /streetcarts/carts?apikey=ABCD


But this will fail, because the resource path /users is not in the Product:

GET /streetcarts/users?apikey=ABCD


This also fails, because the basepath of the proxy StreetCarts is not /foodcarts:

GET /foodcarts/carts?apikey=ABCD


The following path fails as well since the basepath by itself is not allowed in the above configuration. If, however, you do want to allow the basepath, one option would be to add "/" to the list of resources in the product, which (by default) allows the basepath and all subpaths to go through.

GET /streetcarts?apikey=ABCD 


To learn more about how to allow or exclude resources based on wildcards, see Configuring the behavior of a Resource Path of '/', '/*', and '/**'.


Be careful when omitting proxies and/or resource paths

Now, what happens if you omit proxies and/or resources from the Product? You need to be aware of potential risks with these kinds of setups!

When you specify resource paths but omit proxies

If you include paths in the Product, but do not specify any proxies, then keys associated with the Product will work for ANY proxy; however, requests will only succeed for the given paths. Consider the following configuration:



Given this setup, this hypothetical call will succeed:

GET /basepath_A/accounts?apikey=ABCD


And so will this!

GET /basepath_B/accounts?apikey=ABCD


The basepaths don't matter, because no proxies are specified in the Product. So, any proxy basepath will be a potential match for the given key. However, proxy requests will only succeed if a valid resource path is given. For example, this call will fail because /carts is not specified in the Product.

GET /streetcarts/carts?apikey=ABCD


When you specify proxies but omit resource paths

If you specify one or more proxies, but no resource paths, then your API key will work with ALL RESOURCE PATHS in the specified proxy(s), including just the basepath.


The following API call will succeed:

GET /streetcarts/carts?apikey=ABCD


The following call will also succeed because all paths, including the basepath, for the StreetCarts proxy are allowed:

GET /streetcarts?apikey=ABDC 


However, this will fail, because the basepath for the specified proxy (StreetCarts) is /streetcarts, not /foodcarts:

GET /foodcarts/carts?apikey=ABCD


No proxies, no resources: Beware the skeleton key option!

A skeleton key is a key that opens any lock in your house. If you create a product with NO proxies and NO paths, then the key associated with that product can be used with ANY key-protected proxy in your org. In the UI, such a configuration would look like this:



This option may be useful for testing, or for very specific use cases. But be sure you understand the effect of creating a Product with no proxies or paths. Skeleton key!

You can read more about creating products in the Edge documentation.

Comments
terrancedavid
New Member

@wwitman

@dino

@Ricardo

@support

What is the effect of the "revisions" choice when using the "import resource" type of proxy/resourcePath entry?

Also, will selecting the latest revision allow the authority to automatically update when a new revision of an imported resource is created?

mdunker
Staff

Great article, Will!

mdunker
Staff

Revisions are just used to provide the resource path dropdown list when you use the "Import Resource" functionality. They are not saved as part of the product. @wwitman - maybe add a section about the Import Resource box?

willwitman
Staff

Thanks, @Mike Dunker -- I'll update the doc to make sure this is clear.

maivizhiarunagi
New Member

@wwitman @Mike Dunker

Who do i specify the resource for below scenario?

Proxy 1 (StreetCarts)- Access to all resource path (/**)

Proxy 2 (foodcarts)- Access to specific resource path (/carts)

mdunker
Staff

Hi @Maivizhi A -- sorry for the very late response. That is a quirk of products -- unfortunately the proxy and resource paths are two separate lists. Even though you think you can specify them together using the UI, there is no connection between the two. That means that specifying /** would give that wildcard (full) access to any of the specified proxies.

mxr576
Bronze 3
Bronze 3

If no API proxy is specified but "/**" is used in API resources, is it also generates a skeleton API key?

aroy2
Bronze 1
Bronze 1

Is there any way I can allow access to all paths but restrict access to just one path without creating a separate developer app.

For example: I have few apis registered with a proxy named say 'testProxy' :https://example.com/{paths}

1. I have created an apiproduct (say 'A') with path '/**' and proxy 'testProxy'- allow access to all paths and subpaths

2. I have created another apiproduct (say 'B') with path '/restrictedPath' and proxy 'testProxy' - access to /restrictedPath only

 

I have created a single developerApp and approve/revoke access to a particular apiProduct when required. So, If I revoke an app's access to apiProduct 'B' - the /restrictedPath is still accessible because the request goes through apiProduct 'A' that allows access to all paths and subpaths. 

Is there any way I can specify path in apiProduct 'A' that excludes just '/restrictedPath' and let all the access go through. Can I specify a regex in path that achieves this? I am using ApigeeX and not Apigee Edge.

soosmarci
Bronze 4
Bronze 4

Hi @mdunker@willwitman ,
Could u pls answer @aroy2 's question?
Thx!
M

mdunker
Staff

I don't think there is a way to do this with just the API product configuration without listing all of the allowed paths (giving product A all paths except /restrictedPath).

If it is truly sensitive content, I'd separate the content in the proxies. The best solution might be to place the restricted content in a new proxy. Alternatively, you might be able to validate the API product in the /restrictedPath flow, but I'm guessing the separate proxy solution is safer.

The docs provide good examples of resource paths. They cannot use regular expressions.

Jared1234
Bronze 1
Bronze 1

This feels like a major elevation of privilege risk. Can someone explain why this isn't considered a bug and hasn't been addressed as such? Because of this behavior we have to implement our own pipeline controls and logic to check for this scenario and prevent a deploy if the scenario is identified.

Version history
Last update:
‎02-24-2017 06:42 AM
Updated by: