I'm seeing some odd behavior with a cookie on the server side, and would like to understand why.
On the client:
document.cookie = 'test_cookie=' + '[AB]cd|ef-gh[IJ]' + '; path=/;';
document.cookie = 'test_cookie2=' + 'cd|ef-gh' + '; path=/;';
On the server:
headers = httpServletRequest.getHeaders()
// iterate and print headers
cookies = httpServletRequest.getCookies();
// iterate and print headers
Output:
// Both are there on the header, so tomcat doesn't block it:
...
header: cookie: test_cookie=[AB]cd|ef-gh[IJ]; test_cookie2=cd|ef-gh
// Only one shows up from getCookies()
...
cookie: test_cookie2=cd|ef-gh
// no test_cookie ???
Why am I not able to see the test_cookie2?
I could uri-encode before I set it on the client, but I thought '[' and ']' were allowed cookie characters?
Is there a more correct way to set it?
Here's the way to set the cookie correctly on the frontend:
document.cookie = 'test_cookie="[AB]cd|ef-gh[IJ]"; path=/';
Not the double quotes around the cookie value that contains the special characters.
Related
From the development in Windev I use Oauth 2.0 for authorization to get access to the outlook mail from a user.
The application is registered at https://apps.dev.microsoft.com without the Implicit workflow.
After the user enters the credentials, an Authorization Code is returned.
With the new code the Bearer Token is requested with a HTTP Post command.
So far, so good.
Only that the response gives an error message that makes no sense to me.
In code:
m_sHTTPUrl = "client_id=" + m_sClientID + "&client_secret=" ...
+ m_sClientSecret ...
+ "&redirect_uri=" + m_sRedirectURL + "&code=" + m_sAuthToken ...
+ "&grant_type=authorization_code"
m_sHTTPres = ""
LogLocalFile("GetAccessToken - " + m_sTokenURL + " // " + m_sHTTPUrl)
cMyRequest is httpRequest
cMyRequest..Method = httpPost
cMyRequest..URL = m_sTokenURL
cMyRequest..ContentType = "application/x-www-form-urlencoded"
cMyRequest..Header["grant_type"] = "authorization_code"
cMyRequest..Header["code"] = m_sAuthToken
cMyRequest..Header["client_id"] = m_sClientID
cMyRequest..Header["client_secret"] = m_sClientSecret
cMyRequest..Header["scope"] = m_sScope
cMyRequest..Header["redirect_uri"] = m_sRedirectURL
//cMyRequest..Content = m_sHTTPUrl
cMyResponse is httpResponse = HTTPSend(cMyRequest)
m_sHTTPres = cMyResponse.Content
In a logfile I requested the used parameters and the content of the httpResponse:
GetAccessToken - https://login.microsoftonline.com/common/oauth2/v2.0/token // grant_type=authorization_code
&code=xxxxxxx
&scope=openid+offline_access+User.Read+Email+Mail.Read+Contacts.Read
&redirect_uri=http://localhost/
&client_id=xxxxxxx
&client_secret=xxxxxxx
GetAccessToken - error = invalid_request
GetAccessToken - error_description = AADSTS90014: The request body must contain the following parameter: 'grant_type'.
The grant_type is in the header as it is supposed to be.
Does anybody have any clue of what is needed to get the OAUTH2 working ?
You shouldn't send grant_type neither in params nor in headers. Those should be sent in body params then only it will work.
Url: https://login.microsoftonline.com/common/oauth2/v2.0/token
client_id, scope and redirect_uri params can be sent as query params.
where as grant_type, code and client_secret should sent in body params.
grant_type:authorization_code,
code: {code you got from the authorization step},
client_secret: ****
You need to pass everything in body as form-data:
curl --location --request POST 'https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token' \
--form 'grant_type=authorization_code' \
--form '<the code you have got from the authorization endpoint' \
--form 'client_secret=****' \
--form 'client_id=********' \
--form 'scope=m_sScope' \
--form 'redirect_uri=http://localhost/'
you should change the content type as : application/x-www-form-urlencoded
the body must to be formated as bellow:
client_id=8cfbe8ac-8775-4c56-9302-k9d5a42cbf98
&client_secret=BOy7Q~pGvXF.SWshX72mmMnQeAkvN5elHWiYT
&grant_type=client_credentials
&resource=https://miurl.com
I finally got this right after referring to multiple answers.
POST https://login.microsoftonline.com//oauth2/token
--make sure to enter the ID directly without <,>
Use 'x-www-form-urlencoded' format for the Body.
Enter Keys & Values for the below parameters
client_id - Client_ID on your Azure App
client_secret - client_secret value and not the key. Note that this value is available only for the first time upon the client secret key creation
grant_type - client_credentials (static words, don't try to look for the value)
resource - App ID URI
reference link - https://learn.microsoft.com/en-us/previous-versions/azure/dn645543(v=azure.100)?redirectedfrom=MSDN
when providing "Default Scope" value must be full name example , "User.Read" correct value can get from azure AD APP -> Api Permission
I've been banging my head against a wall for 3 days now trying to get this to work.
POST /oauth2/v3/token HTTP/1.1
Host: www.googleapis.com
Content-length: 495
Content-type: application/x-www-form-urlencoded
Authorization: Bearer ya29.cgEcY6meBrvaH6oe0nD_PtsFyMVqskiUYi7iJxapKHeEgPoIw8gMt0BJdIvRn1MfcEgzTS3_gTwI1w
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI5MDgyOTgxNjA1NTktc2R1bGFpbWhsaGpxOTY5M2s1Z2E4c25pZjhh%0D%0ANzhlZ3BAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0%0D%0AdHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvYW5hbHl0aWNzLnJlYWRvbmx5%0D%0AIiwiYXVkIjoiaHR0cHM6Ly9hY2NvdW50cy5nb29nbGUuY29tL28vb2F1dGgyL3Rv%0D%0Aa2VuIiwiZXhwIjoxNDMxNTE0MDUyLCJpYXQiOjE0MzE1MTEwNTJ9.[Cert]
HTTP/1.1 400 Bad Request
Content-length: 67
X-xss-protection: 1; mode=block
X-content-type-options: nosniff
Expires: Wed, 13 May 2015 10:08:00 GMT
Vary: Origin,X-Origin
Server: GSE
Cache-control: private, max-age=0
Date: Wed, 13 May 2015 10:08:00 GMT
X-frame-options: SAMEORIGIN
Content-type: application/json; charset=UTF-8
{
"error_description": "Bad Request",
"error": "invalid_grant"
}
So, I'm creating this in PL/SQL. Oracle 12c. I've managed to get the JWT header and and JWT Claim set to produce an output the same as the google documentation. When i create the cert I think is were the problem occurs.
Do I need the "[-----BEGIN PRIVATE KEY-----" and -----END PRIVATE KEY-----\n] as part of the SHA-256 encryption. Should I do anything with the "New lines"? \n Should I surround it with brackets?
At what point should I be URL encoding?
Is the code in the following example sufficient for the encryption?: http://jastraub.blogspot.co.uk/2009/07/hmacsha256-in-plsql.html
I've attached the a function below to see if you can identify any issues?
Thanks for your help!
FUNCTION get_JWT (p_token_id ga_app_user.ID_TOKEN%TYPE)
RETURN VARCHAR2
IS
--Plain text
baseJWTheader VARCHAR2 (20000);
baseclaimSet VARCHAR2 (20000);
baseSigKey VARCHAR2 (20000);
--Seconds
sysSeconds NUMBER;
--Base64 Encoded
JWTheader VARCHAR2 (20000);
claimSet VARCHAR2 (20000);
sigKey VARCHAR2 (20000);
sigContent VARCHAR2 (20000);
--Returned value
output RAW (20000);
BEGIN
SELECT JWT_HEADER, JWT_CLAIM_SET, SIGNATURE
INTO baseJWTheader, baseclaimSet, baseSigKey
FROM dwman.ga_app_user au
WHERE AU.ID_TOKEN = p_token_id;
--DBMS_OUTPUT.PUT_LINE ('Base claim Set ' || baseclaimSet);
JWTheader :=
TRANSLATE (
UTL_RAW.cast_to_varchar2 (
UTL_ENCODE.BASE64_ENCODE (UTL_RAW.CAST_TO_RAW (baseJWTheader))),
'+/',
'-_');
SELECT ( SYSDATE
- TO_DATE ('01-01-1970 00:00:00', 'DD-MM-YYYY HH24:MI:SS'))
* 24
* 60
* 60
INTO sysSeconds
FROM DUAL;
baseclaimSet :=
REPLACE (baseclaimSet, '#EXPIRE#', ROUND (sysSeconds + 3000));
baseclaimSet := REPLACE (baseclaimSet, '#START#', ROUND (sysSeconds));
--DBMS_OUTPUT.PUT_LINE ('Claim Set ' || baseclaimSet);
claimSet := UTL_RAW.cast_to_varchar2 (
UTL_ENCODE.BASE64_ENCODE (UTL_RAW.CAST_TO_RAW (baseclaimSet)));
sigKey := baseSigKey;
sigContent := JWTheader || '.' || claimSet;
--DBMS_OUTPUT.PUT_LINE('Sig Content '||sigContent);
sigContent := REPLACE (sigContent, CHR (10), '');
sigContent := REPLACE (sigContent, CHR (13), '');
/*
FOR V_TR in 1..length(sigContent)
LOOP
DBMS_OUTPUT.PUT_LINE (substr(sigContent,V_TR,1)||'='||to_char(ASCII(substr(sigContent,V_TR,1))));
END LOOP;
*/
sigContent :=
sigContent
|| '.'
|| google_signature (sigContent, sigKey);
RETURN UTL_URL.ESCAPE(sigContent, TRUE, 'UTF-8');
END get_JWT;
Your code snippet might only be a portion of what you did, but it seemed to be lacking much of the OAUTH steps required by Google to connect.
You can get more detail on what those steps are by looking at this URL:
https://developers.google.com/accounts/docs/OAuth2WebServer#offline
For the remainder of this answer, I have described my own experiences, doing something similar (downloading data from GA, and uploading it to a database using SQL Statements).
Start by getting a consumerKey and consumerSecret for your Google project. You'll need to have a URL that Google can redirect to, both when you request your consumer Key, but also to supply to Google during your OAuth calls. They have to match.
The next step is to send a GET request to Google. Here is an example in c# which you can build using SQL string concatenation.
String URL_AUTH_FIRST = "https://accounts.google.com/o/oauth2/auth";
String URL_TOKEN_ENDPOINT_SECOND = "https://accounts.google.com/o/oauth2/token";
String url = String.Format(
"{0}?client_id={1}&redirect_uri={2}&access_type=offline&scope={3}&response_type=code&state={4}&approval_prompt=force",
URL_AUTH_FIRST,_consumerKey_web_app,redir_url,scope,state);
You will need to have an embedded browser to do this. Google.com will redirect that browser to a site that's under their control so that the user has to be able to login or refuse to authorize your app. After Google has the information they need, they redirect back to your embedded browser. You can do some of the steps using copy/paste in your own browser, but at some point (described below) you have to POST back some data which I wouldn't know if you can do that from a browser application.
Google will respond by redirecting your embedded browser to a url. The URL has data on it. You have to parse the parameters on the URL and look for the parameter "code". If you get a url with "code" as a parameter, you have to POST a format in a certain format back to Google.
WebClient client = get_WebClient(); // proprietary to include things like proxy info
try {
NameValueCollection values = new NameValueCollection();
values.Add("client_id", _consumerKey_web_app);
values.Add("client_secret", _consumerSecret_web_app_offline);
values.Add("grant_type", "authorization_code");
values.Add("redirect_uri", URL_GOOGLE_REDIRECTS_TO_THIS_URL_AFTER_URL_AUTH);
values.Add("code", authorization_code);
Byte[] responseBytes = client.UploadValues(URL_TOKEN_ENDPOINT_SECOND, values);
}
Google will return "responseBytes" which would be a json formatted string, resembling:
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer",
"refresh_token":"1/6BMfW9j53gdGImsixUH6kU5RsR4zwI9lUVX-tqf8JXQ"
}
The access_token gets appended to your REST API calls.
You can supply a GA Query via REST API, get the data back, and upload it into your database using SQL statements. That's what my App does.
You can save that refresh_token and supply it in future connections. Indeed, this whole sequence needs to be done with a browser or browser control, interactively with a user logging in. After it's done, and you've received the refresh_token, then your SQL can use and re-use the refresh_token basically in-definitely, at least until the user's password changes.
Google will also return an error 401 on a regular basis. This just means you have to re-request your access token by Posting a new set of values to Google:
NameValueCollection values = new NameValueCollection();
values.Add("client_id", _consumerKey_web_app);
values.Add("client_secret", _consumerSecret_web_app_offline);
values.Add("refresh_token", refresh_token);
values.Add("grant_type", "refresh_token");
Byte[] responseBytes = client.UploadValues(URL_TOKEN_ENDPOINT_SECOND, values);
I've set a cookie and can see it in my browser. I couldn't find anyway to remove it. What I've tried was:
deleteCookie, _ := r.Cookie("login")
deleteCookie.Value = ""
deleteCookie.MaxAge = -1
http.SetCookie(w, deleteCookie)
But the cookie is still there with it's original value after running this code.
Try this:
http.SetCookie(w, &http.Cookie{
Name: "login",
MaxAge: -1,
Expires: time.Now().Add(-100 * time.Hour),// Set expires for older versions of IE
Path: pathUsedToSetCookie,
})
where pathUsedToSetCookie is whatever path you used to create the original cookie.
Do not reuse the request cookie. The Name field is the only field you need from the request cookie, but you know that already.
I am trying to inject a new request header in the proxy request flow using JS policy to be sent to the backend server. When I look at the debug trace, I see that the json data in the request header is distorted.
I am trying to inject some string like
{"scope":"","time_till":2264,"id_1":"hUXLXVqpA1J4vA9sayk2UttWNdM","custom_data":{"c_id":"test_data"}}
But when I look at the trace window I see this
{"scope":"","time_till":2264,id_1":"hUXLXVqpA1J4vA9sayk2UttWNdM,"custom_data":{"c_id":"test_data"}}
what am I doing wrong?
var obj = {"scope":"","time_till":2264,"id_1":"hUXLXVqpA1J4vA9sayk2UttWNdM","custom_data":{"c_id":"test_data"}};
var header_str = JSON.stringify(obj);
context.setVariable('json-header',header_str);
request.headers['x-json-hedar']= header_str;
I tested your code and it seems to work. Here's an example response where I set the header string as a response:
HTTP/1.1 200 OK
User-Agent: curl/7.30.0
Accept: */*
x-json-header: {"scope":"","time_till":2264,"id_1":"hUXLXVqpA1J4vA9sayk2UttWNdM","custom_data":{"c_id":"test_data"}}
Content-Length: 0
It appears this is only an issue with the Apigee debug session / trace tool as the header value was set correctly. Here was the JSON download of the debug session showing this header value:
{
"name": "x-json-header",
"value": "{\"scope\":\"\",\"time_till\":2264,id_1\":\"hUXLXVqpA1J4vA9sayk2UttWNdM,\"custom_data\":{\"c_id\":\"test_data\"}}"
}
You can see that the value passed to the UI for displaying the debug info has the malformed json:
id_1\":\"hUXLXVqpA1J4vA9sayk2UttWNdM,
This does not appear to be a problem with the Apigee debug/trace UI. I see the malformed JSON trickle down to my backend service.
Here is the header I'm trying to send -
{"timeStamp":"2349218349381274","latitude":"34.589","longitude":"-37.343","clientIp":"127.0.0.0","deviceId":"MOBILE_TEST_DEVICE_AGAIN","macAddress":"23:45:345:345","deviceType":"phone","deviceOS":"iOS","deviceModel":"iPhone 5S","connection":"5G","carrier":"Vodafone","refererURL":"http://www.google.com","xforwardedFor":"129.0.0.0","sessionId":"kfkls498327ksdjf","application":"mobile-app","appVersion":"7.6.5","serviceVersion":"1.0","userAgent":"Gecko"}
But Apigee reads the header as below. Note the missing start quotes from some fields.
{"timeStamp":"2349218349381274",latitude":"34.589,longitude":"-37.343,clientIp":"127.0.0.0,deviceId":"MOBILE_TEST_DEVICE_AGAIN,macAddress":"23:45:345:345,deviceType":"phone,deviceOS":"iOS,deviceModel":"iPhone 5S,connection":"5G,carrier":"Vodafone,refererURL":"http://www.google.com,xforwardedFor":"129.0.0.0,sessionId":"kfkls498327ksdjf,application":"mobile-app,appVersion":"7.6.5,serviceVersion":"1.0,"userAgent":"Gecko"}
The header is used in a service callout to a backend service which parses it. And rightly so, I get the below error -
com.fasterxml.jackson.core.JsonParseException: Unexpected character ('l' (code 108)): was expecting double-quote to start field name
at [Source: java.io.StringReader#22549cdc; line: 1, column: 35]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1378)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:599)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:520)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleUnusualFieldName(ReaderBasedJsonParser.java:1275)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._parseFieldName(ReaderBasedJsonParser.java:1170)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:611)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:301)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2796)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1942)
I encounter strange behaviour when adding JSON to a context variable for example like the following:
var header_str = JSON.stringify(obj);
context.setVariable('json-header',header_str);
I appreciate this is an example so you may not have included the full extent of the problem but this normally works (now it is not added to a variable first):
request.headers['x-json-header'] = JSON.stringify(obj);
Code like this also works if you can send the request from JavaScript
var headers = {"Accept": "application/json", "Accept-Language": "en"};
var sessionRequest = new Request(url, 'POST', headers, body);
var exchange = httpClient.send(sessionRequest);
exchange.waitForComplete()
if (exchange.isSuccess()){
var responseObj = exchange.getResponse().content.asJSON;
if (responseObj.error){
request.content += JSON.stringify(responseObj);
}
}
Also, I have had success with using an AssignMessage policy to build a request, followed by a Callout policy to read the stored request and then make that request and store the result in a response object which can then be read by an Extract Variables policy.
I'm writing a flex application that polls an xml file on the server to check for updated data every few seconds, and I'm having trouble preventing it from caching the data and failing to respond to it being updated.
I've attempted to set headers using the IIS control panel to use the following, without any luck:
CacheControl: no-cache
Pragma: no-cache
I've also attempted adding a random HTTP GET parameter to the end of the request URL, but that seems like it's stripped off by the HttpService class before the request is made. Here's the code to implement it:
http.url = "test.xml?time=" + new Date().getMilliseconds();
And here's the debug log that makes me think it failed:
(mx.messaging.messages::HTTPRequestMessage)#0
body = (Object)#1
clientId = (null)
contentType = "application/x-www-form-urlencoded"
destination = "DefaultHTTP"
headers = (Object)#2
httpHeaders = (Object)#3
messageId = "AAB04A17-8CB3-4175-7976-36C347B558BE"
method = "GET"
recordHeaders = false
timestamp = 0
timeToLive = 0
url = "test.xml"
Has anyone dealt with this problem?
The cache control HTTP header is "Cache-Control" ... note the hyphen! It should do the trick. If you leave out the hyphen, it is not likely to work.
I used the getTime() to make the date into a numeric string that did the trick. I also changed GET to POST. There were some issues with different file extensions being cached differently. For instance, a standard dynamic extension like .php or .jsp might not be cached by the browser and
private var myDate:Date = new Date();
[Bindable]
private var fileURLString:String = "http://www.mysite.com/data.txt?" + myDate.getTime();
Hopefully this helps someone.
I also threw a ton of the header parameters at it but they never fully did the trick. Examples:
// HTTPService called service
service.headers["Pragma"] = "no-cache"; // no caching of the file
service.headers["Cache-Control"] = "no-cache";