FARM Stack - SameSite="none"; Secure can't be set on logout API call - mongodb

I have a backend based on the FARM stack by MongoDB (https://www.mongodb.com/developer/how-to/FARM-Stack-FastAPI-React-MongoDB/), with authentication (https://www.mongodb.com/developer/how-to/FARM-Stack-Authentication/).
Authentication works as it should on Fast API docs and Postman. When executing a successful user log in, it automatically sets the JWT HttpOnly cookie on the browser. When logged in, it removes the cookie again on logout.
When calling /login on front-end, it said following error in the Network tab:
As the error states, I managed to fix this by setting the cookie options on the backend as following, and I could afterwards successfully login from a react front-end:
Unfortunately, this doesn't work for the /logout call. This error occurs again, as the SET-COOKIE header tries to set an empty cookie, yet it can't due to the cookie having a SAMESITE="lax" as default, but I don't see a way to find out how to configure it for the /logout call in the backend.
My front-end /logout call looks like this, the same as /login:
How do I configure my front-end/back-end, so my front-end can successfully delete my cookie and successfully log out?

Related

CORS issue with Ambassador accessing AzureAD

What I have:
a WebApp (ReactJs), an Api (Spring-Boot) each running on a single pod in Kubernetes and an Ambassador wrapped around these (WebApp and Api are having the same domain), my Identity Provider is Azure AD.
In Azure AD I configured a Web-redirect URI for my WebApp.
My Problem:
Accessing the WebApp the first time directs me to microsoft login screen, after entering my credentials I get redirected to my WebApp. When I let the browser tab with my WebApp open, don't use it for 1/1,5 hours and go back and click a button in my WebApp (which triggers a call to my API) I get this CORS error:
Access to XMLHttpRequest at
'https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize?client_id={clientId}&redirect_uri={ambassador-redirectUri}&response_type=code&scope=openid&state={state}'
(redirected from 'https://mydomainname/api/webapp/people') from origin 'https://mydomainname'
has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource
Obviously my access token expired and my application tries to reach the identity provider to get a new one, but my Idp does not allow this.
I followed these steps for setting up Ambassador with Azure AD: https://www.getambassador.io/docs/edge-stack/latest/howtos/sso/azure/ but it seems like I missed something. Anyone a clue what it could be?
The logon flow via the https://login.microsoftonline.com page that you initially experience works only because the URL you want to access (that is, the URL of your WebApp) is in the browser's address bar, hence is used to load a page into the browser tab.
You cannot repeat the same logon flow for an XMLHttpRequest, because this has no address bar and browser tab. Even if the redirection were allowed, the Javascript code of your WebApp could not do anything with the HTML response it would receive from https://login.microsoftonline.com.
If one of your API endpoints (such as https://mydomainname/api/webapp/people) detects that the session is expired, it should not respond with a redirection to https://login.microsoftonline.com, but rather with a specific error code like
HTTP/1.1 403 Forbidden
Content-Type: application/json
{"error": "Session expired",
"redirect": "https://mydomainname/login"}
and when your WebApp receives this, it should present a button to the user labeled
Session expired, press to login again
and pressing this button should open https://mydomainname/login in a new browser tab, which will then automatically redirect to https://login.microsoftonline.com and so on. After the user has closed that tab again, your WebApp can repeat the API request.

Logout from Keycloak does not logout Active Directory User

We have integrated KeyCloak server with Azure Active Directory as Identity Provider for SSO Login.
Log-in is working fine. However we facing problem with log-out, When user logs-out from web application, from our backend server side code we are making REST call to Keycloak server for below API.
https://keycloaktest:8443/auth/realms/<realmName>/protocol/openid-connect/logout
for this REST call we are getting 204 status code as response.
However when User tries to log-in in application again from browser it does not ask to enter credentials (active directory credentials).
In order to log-out User. We have access below URL
https://portal.azure.us/#home
and click log-out there.
https://portal.azure.us/Account/SignOut
Is there anyway to achieve this in backend i.e. when User clicks logout from browser
Just ran across this myself. The answer provided here: Logout user via Keycloak REST API doesn't work worked for me. Try adding client_id and refresh_token to your /logout request.

Use Keycloak Gatekeeper in front of backend API

On a single page app (SPA) that runs on DOMAIN calls to DOMAIN/graphql are rerouted to the backend. Both the frontend and backend are secured via a Keycloak Gatekeeper instance.
The idea is that the frontend and backend share the kc-access token.
Now, the access token expires in the backend Gatekeeper. If the SPA is refreshed in the browser the frontend is rerouted to Keycloak and a fresh access token is required. But if there's no refresh, the POST requests to DOMAIN/graphql fail with a 307 status code when the token has expired. The browser does not know how to handle this. The browser logging gives an "{"error":"RESTEASY003065: Cannot consume content type"}". If the content-type header of the POST is removed the error is "no client_id provided", while the client_id is included in the query string.
Redirecting a POST request to Keycloak would probably not be the best solution. Cleaner would be if the backend refreshes it's access token itself.
This is what we tried by adding a session state store to the backend's Gatekeeper. We are using the following configuration:
- --discovery-url=DISCOVERY_URL
- --client-id=CLIENT_ID
- --client-secret=****
- --enable-refresh-tokens=true
- --encryption-key=0123456789012345
- --store-url=boltdb:///boltdb
- --listen=0.0.0.0:3001
- --verbose=true
- --redirection-url=REDIRECTION_URL
- --upstream-url=http://127.0.0.1:3000
This does create a /boltdb file in the Gatekeeper, but it does not seem to be used since the file does not change.
The backend's Gatekeeper gives the following logging:
|1.5716729131430433e+09|debug|keycloak-gatekeeper/session.go:51|found the user identity|{"id": "b5b659cd-148e-4f23-bf2f-28e6f207f6c7", "name": "piet", "email": "", "roles": "offline_access,dashboard_viewer,uma_authorization,account:manage-account,account:manage-account-links,account:view-profile", "groups": ""}|
|1.5716729131462774e+09|info|keycloak-gatekeeper/middleware.go:154|accces token for user has expired, attemping to refresh the token|{"client_ip": "****", "email": ""}|
|1.5716729131463811e+09|error|keycloak-gatekeeper/middleware.go:161|unable to find a refresh token for user|{"client_ip": "**", "email": "", "error": "no session state found"}|
So we are "unable to find a refresh token for user" because there is "no session state found" according to the logging.
Anybody any idea how to enable token refresh?
It doesn't look like a good design. Keycloak Gatekeeper uses grant code flow, which is not the best flow for SPA as you have discovered (it seems to be very hackish to read user identity provided by Gatekeeper in SPA case).
SPA uses Code Flow with PKCE or Implicit Flow and these flows use silent token renewal (and not refresh token). IMHO the best option will be to use the same client id in the frontend (SPA) and in the backend (e.g. API). But frontend will be protected by Code Flow with PKCE and it will handle own token renewal. Only backend will be protected by Gatekeeper (+ --no-redirects setting makes sense for API protection)
By also setting enable-refresh-tokens=true with the same encryption key in the Gatekeeper on the frontend the design works.
The user retrieves the frontend and is redirected to Keycloak. There an authorization code is obtained. This authorization code is exchanged by the frontend Gatekeeper for an access and refresh token that are put in a cookie on the frontend. When the backend is called with an expired access token the refresh token is decrypted and used to get a new access token.
The refresh token can expire or be invalidated. When a 401 is returend the frontend should refresh the page so the user is redirected to Keycloak.
More secure would be to store the tokens not in the frontend cookies, but in a shared store.

Understanding HTTP Session security mechanism in JHipster

I am trying to figure out how the JHipster uses HTTP session for securing application and logging the user.
So far I’ve managed to understand the flow like this:
1) on landing page home.component sends request to api/account
with session cookies (if there are ones)
if there is a valid session >> uses credentials from stored cookies and then login the user
if no >> backend server responds HTTP 401 and sends XSRF cookie in response
As I understand responsible for this is Spring Security
2) when there was no acitve session we can fill the login form and log in
that sends request to api/authentication (XSRF cookie from step 1 required)
if all ok >> responds HTTP 200 and sends new XSRF cookie
if no >> HTTP 403 and sends new XSRF cookie
3) when step 2 is successful then frontend sends credentials from login form to api/account
one more time Spring Security checks XSRF cookie value (from step 2 this time)
if all ok >> business logic for log in the user is invoked
But in all these steps we need to intercept the cookies sent by backend and send them back to be able to pass through CSRF protection. Which part of JHipster project is responsible for that? Does it use webpack/browsersync or there is some Angular code created by JHipster which I am missing?
EDIT
My problem appeared when I've made a lot of changes to generated project as I want to use custom templates/styling and the services generated by JHipster. I've linked the templates with jhipster services.
When I try to log in the user I get in spring console output like this:
Forbidden: Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-XSRF-TOKEN'
From previuos request to api/authentication I get response with
Set-Cookie: XSRF-TOKEN=a129cb47-ec96-47be-aaae-69f90848c466; path=/
in browser network.
So it sets the cookie with wrong name I guess. I've change the cookie name manually in browser to: X-XSRF-TOKEN and resent the request. The new error looks like this:
Forbidden: Could not verify the provided CSRF token because your session was not found.
I don't understand the flow enough so I can't spot where is the problem. Maybe I've messed up and maybe there is some code generated by JHipster which is responsible for that. There are some classes in JHipster Angular frontend like StateStorageService. Do they take part in my problem or they are irrelevant to that issue?
I was facing the same issue while connecting a mobile app to the backend of spring boot. So basically angular has a method under the core functionalities called interceptors. Under the interceptors the response is gather and X-XSRF-TOKEN is captured and updated in Browser Cookie or in my case Ionic Local Storage as Cookie.

Nexus OSS Remote User Token (RUT) for SSO

Hello I am using Nexus OSS, and wanted to simulate SSO, using Remote User Token. Currently the Nexus is configured to LDAP authentication, and is working fine.
As per the instructions found here https://books.sonatype.com/nexus-book/reference/rutauth.html
Basically enabled Remote User Token and added header field Name "REMOTE_USER". This user is ldap and has access.
This instance is behind apache, so from apache, To test this RUT, I can set the header value REMOTE_USER, whoever, I don't see passed in user getting logged nor I see cookie being generated. I even tried firefox rest api client and set header, but with the same results. I can see that HTTP header is being set right.
Am I missing something?
Is there a way to debug that? Appreciate any help.
Thanks
S
RUT handles authentication, but the authenticated user still needs to be authorized to access the web UI. What this means is that you need an LDAP user or group mapping in Nexus which assigns the necessary roles and privileges to the user.
I had a similar issue with Nginx, the header was not set using the correct value.
This can be quite confusing, as the reverse proxy does not complain and simply sends a blank request header to Nexus.
Using Keycloak and Nginx (Lua), instead of a preferred_username field in the IdP response:
-- set headers with user info: this will overwrite any existing headers
-- but also scrub(!) them in case no value is provided in the token
ngx.req.set_header("X-Proxy-REMOTE-USER", res.preferred_username)
I had to use the preferred_username field returned in the response's id_token element:
-- set headers with user info: this will overwrite any existing headers
-- but also scrub(!) them in case no value is provided in the token
ngx.req.set_header("X-Proxy-REMOTE-USER", res.id_token.preferred_username)