Cache-Control private in kiosk mode - security

Suppose I expose a REST service (over HTTPS) that uses bearer token authentication (JWT) and responds to a GET request with the Cache-Control: private header.
Now suppose my application is used in kiosk mode (multiple users use same browser session as same OS user, think internet cafe or something). User1 makes an authenticated request to a resource.
GET /api/resource
Authorization: bearer <token1>
The response starts with:
HTTP/1.1 200 OK
Cache-Control: private
Now, User1 signs out of my application and User2 signs in. The browser makes a request to the same resource on her behalf (but with a different JWT token).
GET /api/resource
Authorization: bearer <token2>
Now my question is, would the browser consider serving this from cache as it's the same request from the same OS user? Or would the browser consider the Authorization value in that decision?
If the former, would a Vary: Authorization header in the original response change that behavior.

Per RFC 2616, Section 14.9.1, the Cache-Control: private response header will indeed mean that your multiple kiosk users, sharing the same browser session, will all get the same cached response.
And yes, adding a Vary: Authorization response header would help, as indicated by Section 13.6 of RFC 2616; it tells the cache to keep/select from among different "representations" of the resource, based on the request headers listed in the Vary response header value.
Hope this helps!

Related

C# to NodeJS APi - Correct workflow of Token and Refresh Token

I have created a C# application connecting to a MYSQL DB via NodeJS API. (all written by myself).
I'm learning this and I'm trying to get the most secure way of organising the Token and Refresh Token.
Currently I just have the API token with no expiry, so I'm implementing:
5 min token
1 year ref token
My confusion lies with the workflow. Two options I can fathom:
1. Don't use Ref token in header "always":
Send Token for a request.
Token Expired - response to app
App sends refresh token
check refresh token exists (in mysql)
check refresh is not expired
New token created, sent back to app.
App now sends the token for the initial request.
2. Send ref token in header always:
Send token and refresh for a request.
If token expired, check for refresh (error on no refresh)
check refresh token exists (in mysql)
check refresh is not expired
New token created
run the request using the newly generated token
return original request response but with the new access token connected
Now the Option 1 seems... like every time there is a new token generation I'm looking at 3 calls to the server.
The benefit is that the request doesn't always have that refresh key in it
Option 2 is all based in Node JS, but it means every request response would have something like
{ newTokenCreated: True,
newToken: 123345456567567567,
actualRequestResponse: blahblah }
I'm unsure what is the best way to do this as I have half written the Option 2, but to a degree option 1 seems more appropriate except then every time a token is expired there are then 3 calls just to get the new token from the API, instead of option 2 which is just one call and the app really doesnt have to do that much think, just check if it got given a new access token!
Thanks for any advice. I've watched about 10 youtube videos but can't seem to get this bit down.
In our application, we always send the authorization token in the authorization field, whenever authorization is required.
Below is the sample request headers:
:authority: 24n3z33o7e.execute-api.ap-southeast-2.amazonaws.com
:method: GET
:path: /dev/user/profile
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9,kn;q=0.8
authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYxZjRmNGE2ODIxNWVhMDAwOTg2Y2UwZiIsImlhdCI6MTY0NTUwOTYzNSwiZXhwIjoxNjQ1NTEwNTM1fQ.5PcJPNliuYO0n3RTD0gjWwPZ3wW_MQsug0gP0Tr9B9Q
content-type: application/json
if-none-match: W/"a8a-NVC3K4F3Ht14zY2oA/6nJxM78uo"
locale: en
origin: https://spark.dynasty-dev.com
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36
The authorization field contains the token.
We also follow the work flow option 1 where we 'Don't use Ref token in header "always":'.
Our work flow followed:
Send token in request headers.
Token Expired - response from app.
User needs to login again.
Once user will successfully login, App sends new token
User will use this for further API calls with authorization

Why is rate limit similar with authorized and anonymous access to GitHub API?

When I send request with Postman and check response header I can see this:
When I try with old PAT I created, or with OAuth token after validation (I created app and validated user with OAuth flow from my DB, so I used this token in Postman just to check) to call GitHub REST API like this: https://api.github.com/repos/djordjeviclazar/rep/branches
and set access_token in header like in documentation, I can see in headers X-RateLimit-Limit is 60, and I could see that X-RateLimit-Remaining is less than 60.
From documentation:
For API requests using Basic Authentication or OAuth, you can make up to 5,000 requests per hour.
Authenticated requests are associated with the authenticated user, regardless of whether Basic Authentication or an OAuth token was used. This means that all OAuth applications authorized by a user share the same quota of 5,000 requests per hour when they authenticate with different tokens owned by the same user.
So I guess that means I can't make more tokens and expect more than 5000 requests per hour, but why only 60, why API treats my requests as anonymous? Also I think that Search API is more limiting. What is the right way to access GitHub REST API?
The issue is that this call is not authenticated since you've specified:
Add To Headers
Key: access_token
Value: {{PAT}}
It will add an HTTP header with the following value: access_token: [PAT value] which is not processed by Github.
Checkout the headers sent in the headers tab it should print Authorization: Token YOUR_TOKEN or Authorization: Bearer YOUR_TOKEN
The following configuration will work correctly:
Add To Headers
Key: Authorization
Value: Token {{PAT}} (also Bearer {{PAT}} works)
You can also use Authorization of Type Bearer Token which is the same as Bearer XXXX:
Also, you can also disable Authorization (to the value No), and in the headers, just append the Authorization header:
Note that the usage of access_token in the url query parameters has been deprecated since end 2019

What value to give for "scope" option when I want to access an Azure function API that is protected with Azure AD in Postman

I am working on an Azure Function API that is protected by Azure AD, it's working fine when I request it with browser once I logged in with a valid account, but when I tried with postman it still shows unauthorized access,
I have got an access token to send with the request, but for the scope property I have given is this value, https://graph.microsoft.com/.default which I think might be the problem. But not sure what I am doing wrong and what to put in the scope field in the get new access tokenoption in postman to authenticate to my API.
HTTP Request Postman Making
GET /api/events/active HTTP/1.1
Host: moya-backend-ascentic.azurewebsites.net
Authorization: Bearer ****IHqMgweN86fDnyL4jvz9P6ZllpjjD9t***
User-Agent: PostmanRuntime/7.13.0
Accept: */*
Cache-Control: no-cache
Postman-Token: 655e0672-4928-409e-a709-841a92ee6f14,22cbf978-f196-4099-ae10-d162d3068507
Host: moya-backend-ascentic.azurewebsites.net
accept-encoding: gzip, deflate
Connection: keep-alive
cache-control: no-cache
The scope should be: your-api-client-id/.default.
Replace your-api-client-id with the client id/application id for your API app in Azure AD.
Now since browser-based login seems to be working, you may have setup the wrong kind of authentication on the API.
If the token still doesn't work, you'll need to setup JWT authentication on the app instead of browser/cookie based.
If you are using v2.0 endpoint, the scope should be {your_client_id}/.default. Just like #juunas said.
If you are using v1.0 endpoint, you only need resource parameter. The resource should be {your_client_id}
Note: If you are using the 'get new access token' function in postman, the Access Token URL should be https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token

Passing an access and refresh token in the header with IdentityServer4

It seems IdentityServer4 will only pass access tokens as a cookie in the header unless I do resourceownerpassword which will pass an access token, but no refresh token. I need the access and refresh token directly passed in the header something like this
HTTP/1.1 200 OK Content-Type: application/json
{ "access_token": "eyJz93a...k4laUWw",
"token_type": "bearer"
"refresh_token": "sd4rR68..."
"expires_in":86400
}
I've read the entire docs and scoured the internet have found nothing. Is this possible with IdentityServer4?
Edit:
So I misunderstood what was happening with the token. Now I see it is in the body, I need this in the header.
With this specific need is it better for me to switch to nodejs oauth2?
IdentityServer4 does not pass access tokens as a cookie in the header.
If you use the authorization code, hybrid, or client credentials flow, then access tokens are returned from the token endpoint as JSON in the response body.
Refresh tokens are only supported by the authorization code, hybrid, and ROPC flows. To request one include the offline_access scope.

"Authorization has been denied for this request" when accessing Web API deployed in Azure

Following on from this question:
AADSTS50013: Assertion audience claim does not match the required value
I've now successfully got the web apps running with this security model:
SPA application using adal.js/adal_angular.js to authenticate via AAD.
Returned token is passed to web api [API1] that runs on the same machine.
That web api gets a new token on behalf of the user to access a downstream API [API2].
The downstream api gets a new token on behalf of the user to access another downstream API [API3].
Now, when I have [API2] running locally, this is all working.
However, when I deploy that web app to my Azure subscription, and attempt to call it (without changing anything else other than the url in the REST API call from [API1]), I get the following:
{"Message":"Authorization has been denied for this request."}
There doesn't appear to be any other error details returned or in the Fiddler trace. Comparing the jwt token payloads between the call that works and the one that doesn't, doesn't reveal much. They appear the same other than the expiry claims and the "aio" (not sure what that is).
The only change is the URL of the deployed web app (from http://localhost:8080/ to http://mywebappname.azurewebsites.net/)
Note that the web app is deployed into a different AD tenant to the one where the app registrations and [API3] are located, but I didn't think this mattered.
Any thoughts out there on what I might need to change when I deploy, or how to troubleshoot this further?
Update: Request works with Curl
Making the same request using curl is working:
curl -H "Authorization: Bearer ey..." http://mywebapi.azurewebsites.net/api/resource
So the issue appears to be how I'm making the request in my C# code? Comparing the headers in Fiddler, I don't see any difference.
This is the Fiddler trace from curl that is working:
GET http://mywebapi.azurewebsites.net/api/resource HTTP/1.1
Host: mywebapi.azurewebsites.net
User-Agent: curl/7.46.0
Accept: */*
Connection: Keep-Alive
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJ<snip>
This is the Fiddler trace from my code that is not working:
GET http://mywebapi.azurewebsites.net/api/resource HTTP/1.1
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJ<snip>
Accept: */*
User-Agent: RestSharp/100.0.0.0
Host: mywebapi.azurewebsites.net
Accept-Encoding: gzip, deflate
Here is the request from C#:
var restClient = new RestClient(serviceUrl) { Timeout = timeout};
var restRequest = new RestRequest(apiEndpoint, Method.GET);
var bearerToken = $"Bearer {securityToken}";
restRequest.AddParameter("Authorization", bearerToken, ParameterType.HttpHeader);
var response = restClient.Execute(restRequest);
On the Azure web site logs, I can see that the authentication type for the successful curl request is "JWT", however for the failed requests from my code they are "anonymous".
Somehow the header must be being stripped despite it showing up correctly in the Fiddler trace? Is this possible?
In an unrelated issue, I had to delete all untrusted certificates from my machine (Internet Options->Content->Certificates).
And I noticed after doing this, my problem was resolved. It was a very long list of certificates, so I don't know which one(s) were causing the problem, or why.
Given the lack of responses, its obviously a very obscure issue, but unfortunately we didn't get to the bottom of it. If it occurs again, I can be a bit more methodical when I do it.

Resources