OAuth 2.0 policy is not verifying the access token when CORS issue occur from the browser to access the apigee api

https://community.apigee.com/questions/3138/cors-policy-in-my-api-proxy-when-using-oauth-20.html

As I came across Above thread can someone please help me to solve the issue I am facing right now, if the apigee API is call from web this generates the CROS issue which is handled in Proxy as told by @Barahalikar Siddharth

My requirement is that I need to pass the Token in from of Header not on query parameter which apigee is accepting right now. If I pass through the access_token in Header its fails and gives 500 server error.

Since the browser generates the CROS issue and OptionPreFlight request doesn't validate this API request. And If I pass the same access_token in query parameter then this gives me success 200OK. @Dino @Anil Sagar @kbouwmeester @Maruti Chand @Zhongli Wu @JennyB can you guide me to solve this problem would be really helpful.

Solved Solved
0 11 4,475
1 ACCEPTED SOLUTION

Hi Mohsin,

Seems like when the preflight OPTIONS HTTP verb call is invoked by the trusted agent, the OAuthV2 policy is being invoked on Apigee. That shows that the conditional to CORS policy is not in effect.

In other words OAuthV2 policy should not be invoked for OPTIONS (preflight) calls. This can be done with a conditional like this:

<Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>

Refer the https://docs.apigee.com/api-services/content/adding-cors-support-api-proxy for more details:

Hope that solves it.

Thanks

Naseer

View solution in original post

11 REPLIES 11

I guess your CORS Preflight must allow the Authorization header, or whatever header you are using to pass the token. The CORS preflight response should look something like this:

Access-Control-Allow-Headers: Authorization, Date, Whatever

HI @Dino

In Header section I get this output Access-Control-Request-Headers: authorization

and in Response I get, {"fault":{"faultstring":"Invalid access token","detail":{"errorcode":"oauth.v2.InvalidAccessToken"}}}

I think the preflight response header must be Access-Control-Allow-Headers , not

Access-Control-Request-Headers. This is how CORS works. Please check.


But.... hang on. Originally you suggested the issue was a CORS issue. If you are getting

 {"fault":{"faultstring":"Invalid access token","detail":{"errorcode":"oauth.v2.InvalidAccessToken"}}}

...then you are not experiencing a CORS problem.

CORS is enforced by the trusted user-agent, aka the browser. The browser - Safari, Chrome, Firefox, MS Edge, etc - is smart enough to send CORS preflight requests when the API target is not the origin server. In other words, when a webpage served from www.example.com tries to contact api.domain.com , then the browser will do the CORS preflight to check, "hey, does this API server want to allow me to call it?" Using response headers like Access-Control-Allow-Headers and etc (See the CORS link I provided above), the API server can respond with "YES, I want you to be able to call me." Only after receiving a CORS response from the API server will the browser allow JavaScript code to invoke APIs (via XHR) on the non-origin server. If you did not get an appropriate CORS response, then the API request will never be sent from the browser to the desired API endpoint.

But if you are receiving the error you noted - Invalid Access Token - then the browser has allowed the API call to be sent out, and Apigee has also received it. So CORS is not an issue. Does this make sense?

I'm not clear why you thought it was CORS.


The Invalid Access Token message is pretty clear. You are using the OAuthV2/VerifyAccessToken policy, and asking Apigee Edge to verify a token. By default, that policy looks for the token in the request header named "Authorization". If you pass the access token in a different place, in other words in a query parameter or in a header that is not named "Authorization". You must tell the Apigee policy specifically where to find the token, using the AccessToken element, like so:

<OAuthV2 name="OAuthV2-Verify">
  <Operation>VerifyAccessToken</Operation>
  <AccessToken>request.queryparam.token</AccessToken>
</OAuthV2>

Then an API request passing a token might look like this:

POST /myapi/foo?token=ABABABABABAB

Also, if the token is passed in a header, the policy by default expects the prefix to be "Bearer ". If you have no prefix or a different prefix, then you can specify that as well

<OAuthV2 name="OAuthV2-Verify">
  <Operation>VerifyAccessToken</Operation>
  <AccessToken>request.header.token</AccessToken>
  <AccessTokenPrefix>MyPrefix</AccessTokenPrefix>
</OAuthV2>

In this case a curl command invoking this API and passing the token might look like:

curl -X POST -d '{...}' \
  -H content-type:application/json \
  -H "Token: MyPrefix ABABABBABABABA" \
  https://example.com/myapi/foo 

You can read the reference docs on the OAuthV2 policy here.

I think we might be able to help you if you provide to us:

  • the configuration of the OAuthV2 policy
  • A clear description of the API request you are sending, including exactly how the token is being passed.

Hello @Dino ,

The Response I got is as below,

1.Request URL:   					https://api.companyname.com:9030/webex/recordingdetails?IsSearch=false&usrname=mohsin_khan&IsMobileA...
2.Request Method:       OPTIONS
3.Status Code: 		401 Unauthorized
4.Remote Address: 	10.44.216.31:9030
5.Referrer Policy:	no-referrer-when-downgrade

2.Response Headersview source
	1.Connection:	keep-alive
	2.Content-Length: 101
	3.Content-Type:	application/json
        4.WWW-Authenticate: Bearer realm="null",error='invalid_token",error_description='oauth.v2.InvalidAccessToken:Invalid access token"

3.Request Headersview source
	1.Accept:	   */*
	2.Accept-Encoding: gzip, deflate, br
	3.Accept-Language: en-US,en;q=0.9
	4.Access-Control-Request-Headers: authorization
	5.Access-Control-Request-Method:  GET
	6.Connection:	keep-alive
	7.Host: 	api.companyname.com:9030
	8.Origin:       https://companyname.co.in
	9.User-Agent:	Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36


4.Query String Parametersview sourceview URL encoded
	1.IsSearch: 	false
	2.usrname:	mohsin_khan
	3.IsMobileApp:	1
Response
{"fault":{"faultstring":"Invalid access token",
 "detail":{"errorcode":"oauth.v2.InvalidAccessToken"}}}


----------------------------------------------------------------------------------------

And the oAuth policy is as below, the Apigee token is passed in

Authorization : Bearer <"access_token">

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 async="false" continueOnError="false" enabled="true" name="Verifyaccesstoken">
    <DisplayName>Verifyaccesstoken</DisplayName>
    <FaultRules/>
    <Properties/>
    <Attributes/>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <SupportedGrantTypes/>
    <GenerateResponse enabled="true"/>
    <Tokens/>
</OAuthV2>

If this information is not enough then I can arrange a call to discuss to solve this issue, DM me please.

WIth Apigee URL, if we send the token in query parm then all the security is gone since if an application is hit through browser then in debugging the console part our URL is accessible and all the data can get so how do we overcome this.


But if I pass the token through the Header the apigee say invalid token since the OPTION as a method is pass from browser and token is invalide at apigee end

From this description, I'm not clear at all on what's happening.

I think I need

  • a clear description of the requests that are being sent. like CURL commands or something else that is precise and complete. A description like "if we send the token in the query param" is not complete or precise enough.
  • the exact configuration of the API proxy, including the exact configuration of the OAuthV2 policy as well as the exact description of the Proxy endpoint preflow and all the conditional flows.
  • the actual results you see
  • the results you expect to see

If you have tried multiple things, then I'll need all those 4 items for each different trial.

Hi Mohsin,

Seems like when the preflight OPTIONS HTTP verb call is invoked by the trusted agent, the OAuthV2 policy is being invoked on Apigee. That shows that the conditional to CORS policy is not in effect.

In other words OAuthV2 policy should not be invoked for OPTIONS (preflight) calls. This can be done with a conditional like this:

<Condition>request.verb == "OPTIONS" AND request.header.origin != null AND request.header.origin != null AND request.header.Access-Control-Request-Method != null</Condition>

Refer the https://docs.apigee.com/api-services/content/adding-cors-support-api-proxy for more details:

Hope that solves it.

Thanks

Naseer

THANKS @Naseer Mohammad for help will try and let you know

Not applicable

<OAuthV2name="InvalidateToken">

<Operation>InvalidateToken</Operation>

<Tokens>

<Tokentype="accesstoken"cascade="true">flow.variable</Token>

</Tokens></OAuthV2>

Google Chrome Customer Support

cbmohit
Participant I

I also faced this issue. But after removing CORS policy from flow it worked.

jovaniac
Participant II
hey guys, I implemented something like that and it served me correctly.
In the proxy enpoint we must place in the preflow the next call of a Flowcallout to invoke a sharedflow which will have the policy of CORS

<PreFlow name="PreFlow">
<Request>
<Step>
<Name>FC-CORS</Name>
</Step>
<Step>
<Name>FC-OAuth2</Name>
</Step>
</Request>
<Response/>
</PreFlow>

Definition of flowcallout, where we invoke the sharedflow

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FlowCallout async="false" continueOnError="false" enabled="true" name="FC-CORS">
<DisplayName>FC-CORS</DisplayName>
<FaultRules/>
<Properties/>
<SharedFlowBundle>OPTIONS-CORS-Headers-Response</SharedFlowBundle>
</FlowCallout>

definition of sharedflow

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SharedFlow name="default">
<Step>
<Name>OPTIONS-CORS-Headers-Response</Name>
<Condition>request.verb == "OPTIONS"</Condition>
</Step>
</SharedFlow>

definition of the policy of raisefull, where we will indicate the headers of Access-Control-Allow-Origin with * that will allow the invocation from our browser

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RaiseFault async="false" continueOnError="false" enabled="true" name="OPTIONS-CORS-Headers-Response">
<DisplayName>OPTIONS CORS Headers Response</DisplayName>
<Properties/>
<FaultResponse>
<Set>
<Headers>
<Header name="Access-Control-Allow-Origin">*</Header>
<Header name="Access-Control-Allow-Headers">origin, x-requested-with, accept, ucsb-api-key, ucsb-api-version, authorization</Header>
<Header name="Access-Control-Max-Age">3628800</Header>
<Header name="Access-Control-Allow-Methods">GET, PUT, POST, DELETE</Header>
</Headers>
<Payload contentType="text/plain"/>
<StatusCode>200</StatusCode>
<ReasonPhrase>OK</ReasonPhrase>
</Set>
</FaultResponse>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</RaiseFault>

angular:

const httpOptions2= { headers:newHttpHeaders({ 'Authorization':'Bearer token' }) };

obtenerCatalogos():Observable<any> { return this.httpClient.get<any>(uriApigee+'endpointapigee',httpOptions2); }

Regars