Add extra HTTP Request Headers

Pavol Drotar
Pavol Drotar

HTTP requests contain headers such as User-Agent or Content-Type. Apart from headers attached bybrowsers, Android apps may add extra headers, like Cookie or Referrer through theEXTRA_HEADERS Intent extra. For security reasons, Chrome filters some of the extra headersdepending on how and where an intent is launched.

Cross-origin requests require an additional layer of security as the client and server arenot owned by the same party. This guide discusses launching such requests through Chromecustom tabs, i.e. intents launched from apps that open a URL in the browser tab. Until Chrome83, developers could add any headers when launching a Custom Tab. From version 83 onward, Chromestarted filtering all exceptapprovelisted cross-origin headers, since non-approvelisted headersposed a security risk. Starting with Chrome 86, it is possible to attach non-approvelisted headers tocross-origin requests, when the server and client are related using adigital asset link.This behaviour is summarised in the following table:

Chrome versionCORS headers allowed
before Chrome 83approvelisted, non-approvelisted
Chrome 83 to Chrome 85approvelisted
from Chrome 86 onwardsapprovelisted, non-approvelisted when a digital asset link is set up

Table 1.: Filtering of non-approvelisted CORS headers.

This article shows how to set up a verified connection between the server and client and use thatto send approvelisted as well as non-approvelisted http headers. You can skip toAdding Extra Headers to Custom Tab Intents for the code.

Background

approvelisted vs. Non-approvelisted CORS Request Headers

Cross-Origin Resource Sharing (CORS) allows a web application from one origin to requestresources of a different origin. The list ofCORS-approvelisted headers is maintained in theHTML Standard. Example approvelisted headers are shown in the next table:

HeaderDescription
accept-languageadvertises natural languages the client understands
content-languagedescribes language intended for the current audience
content-typeindicates the media type of the resource

Table 2.: Example approvelisted CORS headers.

The approvelisted headers are considered safe because they don't contain sensitive user information and are unlikely to cause the server to perform potentially damaging operations.

Examples of non-approvelisted headers are shown in the following table:

HeaderDescription
bearer-tokenauthenticates client at a server
originindicates origin of request
cookiecontains cookies set by server

Table 3.: Example non-approvelisted CORS headers.

Attaching non-approvelisted headers to CORS requests is discouraged by the HTML standard and servers assume that cross-origin requests contain only approvelisted headers. Sending non-approvelisted headersfrom cross-origin domains would allow malicious third-party apps to craft headers that misuse usercookies that Chrome (or another browser) stores and attaches to requests. The cookies could authenticate malicious server transactions that would otherwise not be possible.

Attaching CORS approvelisted headers to Custom Tabs requests

Custom Tabs are a special way of launching web pages in a customised browser tab. Custom Tabintents can be created usingCustomTabsIntent.Builder(). You can also attach headers to theseintents using aBundle with theBrowser.EXTRA_HEADERS flag:

CustomTabsIntentintent=newCustomTabsIntent.Builder(session).build();Bundleheaders=newBundle();headers.putString("bearer-token","Some token");headers.putString("redirect-url","Some redirect url");intent.intent.putExtra(Browser.EXTRA_HEADERS,headers);intent.launchUrl(Activity.this,Uri.parse("http://www.google.com"));

We can always attach approvelisted headers to custom tabs CORS requests. However, Chrome filters non-approvelisted headers by default. Although other browsers may have different behaviour, developers should expect non-approvelisted headers to be blocked in general.

The supported way of including non-approvelisted headers in custom tabs is to first verify thecross-origin connection using a digital access link. The next section shows how to set theseup and launch a Custom Tabs intent with the required headers.

Adding Extra Headers to Custom Tab Intents

Set up digital asset links

To allow non-approvelisted headers to be passed through Custom Tab intents, it is necessary to setup a digital asset link between the android and web application that verifies that the authorowns both applications.

Follow theofficial guide to set up a digital asset link. For the link relation use "delegate_permission/common.use_as_origin"` which indicates that both apps belong to the same origin once the link is verified.

Create Custom Tab Intent with Extra Headers

There are multiple ways to create aCustom Tabs intent. You can use the builder availablein androidX by adding the library to the build dependencies:

implementation'androidx.browser:browser:1.2.0'

Build the intent and add extra headers:

CustomTabsIntentconstructExtraHeadersIntent(CustomTabsSessionsession){CustomTabsIntentintent=newCustomTabsIntent.Builder(session).build();// Example non-cors-approvelisted headers.Bundleheaders=newBundle();headers.putString("bearer-token","Some token");headers.putString("redirect-url","Some redirect url");intent.intent.putExtra(Browser.EXTRA_HEADERS,headers);returnintent;}

Set up a Custom Tabs Connection to Validate the Asset Link

A Custom Tabs connection is used for setting up aCustomTabsSession between the app and theChrome tab. We need the session to verify that the app and web app belong to the same origin.The verification only passes if the digital asset links were set up correctly.

It is encouraged to callCustomTabsClient.warmup(). It allows the browser application topre-initialize in the background and speed up the URL opening process.

// Set up a connection that warms up and validates a session.CustomTabsServiceConnectionconnection=newCustomTabsServiceConnection(){@OverridepublicvoidonCustomTabsServiceConnected(@NonNullComponentNamename,@NonNullCustomTabsClientclient){// Create session after service connected.mSession=client.newSession(callback);client.warmup(0);// Validate the session as the same origin to allow cross origin headers.mSession.validateRelationship(CustomTabsService.RELATION_USE_AS_ORIGIN,Uri.parse(url),null);}@OverridepublicvoidonServiceDisconnected(ComponentNamecomponentName){}};

Set up a Callback that Launches the Intent after Validation

TheCustomTabsCallback was passed into the session. We set up itsonRelationshipValidationResult() to launch the previously createdCustomTabsIntentonce the origin verification succeeds.

// Set up a callback that launches the intent after session validated.CustomTabsCallbackcallback=newCustomTabsCallback(){@OverridepublicvoidonRelationshipValidationResult(intrelation,@NonNullUrirequestedOrigin,booleanresult,@NullableBundleextras){// Launch custom tabs intent after session was validated as the same origin.CustomTabsIntentintent=constructExtraHeadersIntent(mSession);intent.launchUrl(MainActivity.this,Uri.parse(url));}};

Bind the custom tabs service connection

Binding the service launches the service and the connection'sonCustomTabsServiceConnected()will be called eventually. Don't forget to unbind the service appropriately. Binding and unbindingis commonly done in theonStart() andonStop() activity lifecycle methods.

// Bind the custom tabs service connection.// Call this in onStart()CustomTabsClient.bindCustomTabsService(this,CustomTabsClient.getPackageName(MainActivity.this,null),connection);// …// Unbind the custom tabs service.// Call this in onStop().unbindService(connection);

Demo application code

You can find more details about Custom Tabs Servicehere. See theandroid-browser-helper GitHub repository for a working example app.

Summary

This guide demonstated how to add arbitrary headers to custom tabs CORS requests.approvelisted headers can be attached to every custom tabs CORS request. Non-approvelisted headers aregenerally considered unsafe in CORS requests and chrome filters them by default. Attaching them isallowed only for clients and servers of the same origin, verified by a digital asset link.

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2020-08-12 UTC.