How do I log out of Stormpath ID Site with express-stormpath and stormpath-sdk-angularjs? - stormpath

I have an express-stormpath application that uses Stormpath ID Site. It has this configuration:
app.use(stormpath.init(app, {
web: {
idSite: {
enabled: true,
uri: '/idSiteResult',
nextUri: '/'
},
login: {
enabled: true,
uri: config.login
},
logout: {
enabled: true,
uri: config.logout
},
me: {
expand: {
customData: true,
groups: true
}
}
}
}));
Login works fine, but logout is giving me trouble.
First, I tried logging out with the stormpath-sdk-angularjs built-in endSession()
$auth.endSession();
But I was still logged in.
Digging into express-stormpath, it looks like logout POST requires Accept type text/html for id-site logout. In stormpath-sdk-angularjs, it looks like endSession POST uses application/json.
So I tried logging out with $http.post
$http.post('/logout', null, {
headers: {
'Accept': 'text/html'
}
});
But I get this error:
XMLHttpRequest cannot load https://api.stormpath.com/sso/logout?jwtRequest=[...]. Redirect from 'https://api.stormpath.com/sso/logout?jwtRequest=[...]' to 'http://localhost:9000/idSiteResult?jwtResponse=[...]' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access.
How do I log out of Stormpath ID Site?

I work at Stormpath. ID Site requires that you actually redirect the end user to ID Site. I'm not sure why endSession() isn't working, but I'll reach out to our JS team to see if there might be a bug there.
In the meantime, you can use this code (or the equivalent in Angular-specific primitives) to accomplish a logout:
var form = document.createElement('form');
form.method = "POST";
form.action = "/logout";
form.submit();

This looks like a CORS issue. I believe you need to add at least;
Access-Control-Allow-Origin: https://api.stormpath.com
To the response headers from your server.

Related

AWS Lambda Set-Cookie header not setting in the browser

I'm trying to set a cookie in my aws lambda function response. I don't have any header mapping as I'm using lambda proxy integration with API Gateway. The response code looks like this in the lambda function:
exports.handler = async (event) => {
const response = {
statusCode: 200,
"multiValueHeaders": {
"Set-Cookie": ["gtgm=6c7729687d5ff1a05f1a5dfb15ce3b8fa3f2b590; path=/; expires=Fri, 13-Feb-2032 13:27:44 GMT; secure; HttpOnly; SameSite=None"]
},
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Credentials": true,
},
};
return response;
};
I use fiddler to check the response and I can see the "Set-Cookie..." in the response which leads me to believe that the code above is correct? The issue is that the browser just ignores it and doesn't set any cookies at all except for the AWS DNT cookie. I'm not sure what else to check or if I've missed anything in the cookie config.
This is what my request looks like:
<Button
onClick={() => {
fetch(
"https:mysupercoolapi.com/cookie-test/",
{
// credentials: "include",
headers: {
"Content-Type": "application/json",
// "Access-Control-Allow-Credentials": "true",
},
}
)
.then((response) => response.json())
.then((data) => {
console.log(data);
});
}}
>
Cookie Test
</Button>
Not sure what I'm missing or where I'm going wrong.
I magaed to figure this one out myself. The setup above was fine but the browser requires you to have the following set properly in order for it to actually set the cookie:
In the request: Set "credentials" as include. Depending on what library you're using (fetch would be credentials: "include"). You can see it is commented out in my original post above which is not correct.
In the Response header in the Lambda function you need to set you origin e.g., "Access-Control-Allow-Origin": "http://localhost:3002". After testing I switched it to my actual domain. If there's a way to keep this set to the actual domain but still have it work while testing on localhost please let me know.
In API Gateway you need to set the CORS values as in the screenshot below to ensure the preflight (OPTINOS) call is made correctly as well.
The key is to have both the request and response headers configured correctly. This is harder to do when using something like AWS but much easier with something like express.js where you can use the middleware.

Session cookie is not set in browser

I have frontend client running on custom Next.js server that is fetching data with apollo client.
My backend is graphql-yoga with prisma utilizing express-session.
I have problem with picking correct CORS settings for my client and backend so cookie would be properly set in the browser, especially on heroku.
Currently I am sending graphql request from client with apollo-client having option
credentials: "include" but also have tried "same-origin" with no better result.
I can see cookie client in response from my backend in Set-Cookie header, and in devTools/application/cookies. But this cookie is transparent to browser and is lost on refresh.
With this said I also tried to implement some afterware to apollo client as apolloLink that would intercept cookie from response.headers but it is empty.
So far now I'm thinking about pursuing those two paths of resolving the issue.
(I'm only implementing CORS because browser prevents fetching data without it.)
CLIENT
ApolloClient config for client-side:
const httpLink = new HttpLink({
credentials: "include",
uri: BACKEND_ENDPOINT,
});
Client CORS usage and config:
app
.prepare()
.then(() => {
const server = express()
.use(cors(corsOptions))
.options("*", cors(corsOptions))
.set("trust proxy", 1);
...here goes rest of implementation
const corsOptions = {
origin: [process.env.BACKEND_ENDPOINT, process.env.HEROKU_CORS_URL],
credentials: true,
allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With", "X-Forwarded-Proto", "Cookie", "Set-Cookie", '*'],
methods: "GET,POST",
optionsSuccessStatus: 200
};
My atempt to get headers from response in apolloClient(but headers are empty and data is not fetched afterwards):
const middlewareLink = new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
const context = operation.getContext();
const {response: {headers}} = context;
if (headers) {
const cookie = response.headers.get("set-cookie");
if (cookie) {
//set cookie here
}
}
return response;
});
});
BACKEND
CORS implementaion (remeber that is gql-yoga so I need first to expose express from it)
server.express
.use(cors(corsOptions))
.options("*", cors())
.set("trust proxy", 1);
...here goes rest of implementation
const corsOptions = {
origin: [process.env.CLIENT_URL_DEV, process.env.CLIENT_URL_PROD, process.env.HEROKU_CORS_URL],
credentials: true,
allowedHeaders: ["Content-Type", "Authorization", "X-Requested-With", "X-Forwarded-Proto", "Cookie", "Set-Cookie"],
exposedHeaders: ["Content-Type", "Authorization", "X-Requested-With", "X-Forwarded-Proto", "Cookie", "Set-Cookie"],
methods: "GET,HEAD,PUT,PATCH,POST,OPTIONS",
optionsSuccessStatus: 200
};
Session settings, store is connect-redis
server.express
.use(
session({
store: store,
genid: () => uuidv4(v4options),
name: process.env.SESSION_NAME,
secret: process.env.SESSION_SECRET,
resave: true,
rolling: true,
saveUninitialized: false,
sameSite: false,
proxy: STAGE,
unset: "destroy",
cookie: {
httpOnly: true,
path: "/",
secure: STAGE,
maxAge: STAGE ? TTL_PROD : TTL_DEV
}
})
)
I am expecting session cookie to be set on the client after authentication.
Actual result:
Cookie is visible only in Set-Cookie response header but is transparent to browser and not persistent nor set (lost on refresh or page change). Funny enough I can still make authenticated requests for data.
This may not be a CORS issue, it looks like a third-party cookie problem.
Behaviour could be different across browsers so I recommend testing several ones. Firefox (as of version 77) seems to be less restrictive. In Chrome (as of version 83) there is an indicator on the far right of the URL bar when a third party cookie has been blocked. You can confirm whether third party cookies is the cause of the problem by creating an exception for the current website.
Assuming your current setup is as follows:
frontend.com
backend.herokuapp.com
Using a custom domain for your backend that is a subdomain of your frontend would solve your problem:
frontend.com
api.frontend.com
The following setup wouldn't work because herokuapp.com is included in the Mozilla Foundation’s Public Suffix List:
frontend.herokuapp.com
backend.herokuapp.com
More details on Heroku.

API Gateway, blocked by CORS policy: No 'Access-Control-Allow-Origin' header

I know this question might be duplicated, but none of the existing question point to anything I'm not doing...
I've deployed an API using the serverless framework, but I'm having trouble with CORS.
I'm doing a get request using axios:
axios.get('https://test.execute-api.us-west-1.amazonaws.com/dev/test?from=2012-01-09T21:40:00Z')
.then(response => {
this.data = response.data;
})
.catch(error => console.log(error))
And I'm getting the following error:
Access to XMLHttpRequest at 'https://test.execute-api.us-west-1.amazonaws.com/dev/test?from=2012-01-09T21:40:00Z' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
What I've already done:
Made sure there's an OPTIONS method in API Gateway with a method response that looks like this:
Made sure I deployed those changes.
Also, the response of my Lambda function is returning the following headers:
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{
"Access-Control-Allow-Origin": "http://localhost:8080",
"Access-Control-Allow-Credentials": "true",
},
Body: string(jsonEvents),
}, nil
I also tried setting Access-Control-Allow-Origin to '*'
My serverless.yml file has cors: true on each of the function events:
functions:
deploymentFrequency:
handler: bin/update/deployment-frequency
events:
- http:
path: deployment-frequency
method: post
cors: true
fetchDeploymentFrequency:
handler: bin/fetch/deployment-frequency
events:
- http:
path: deployment-frequency
method: get
cors: true
What am I missing? Nothing seems to work. The request works fine from Postman and it looks to be including the headers, so this seems to be an issue with the OPTIONS method.
My configuration is:
(event, context, callback) => {
callback(null, {
statusCode: (code || 200),
body: JSON.stringify(resp),
headers: { 'Access-Control-Allow-Origin': '*'},
});
}
and it works fine for me. I use to have the same issue as you before, but as long as you define your function with CORS: true and your response contains the header, you should be fine.
Note: Im didnt understand the sintax "map[string]string" and credentials should not be necessary at this case.
It turns out I was ignoring the status code from the response :(
I realized I was actually getting two errors:
A 406 status code for a missing Content-Type header
The CORS error
The first error was caused because I wasn't passing the Content-Type header to the request (I had a check in my code I completely forget that expects that header).
The second error was caused because I didn't add the Access-Control-Allow-Origin header to the error response of my function.
Enable Lamba proxy integration
return events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
},
Body: string(jsonEvents),
}, nil
In your terminal, go to the root project path and run:
npm i cors
And, after you need put this code in your index.js:
const cors = require("cors");
app.use(cors());

Cookie is "undefined" in Koa/Express request

I'm using Koa JS framework for jwt authentication.
So basically when the user signs in, I set a jwt token (signed) to user's browser cookie, which seems to work fine as shown below (Chrome cookie settings):
(www.localhost.com instead of localhost is because I edited my hostfile, but this should have no effect in setting/getting cookies)
The problem, however, is when I send a POST request to my local Koa server, the jwt cookie is undefined. All I'm doing to verify the token is this:
routes.js
const Router = require("koa-router");
const router = new Router();
router.post(`api/authenticate`, function* () {
const jwt = this.cookies.get("jwt", { signed: true }); //jwt is undefined!!
if (!jwt)
this.throw("Invalid or expired token!");
this.status = 200;
});
//...
app.use(router.routes());
app.use(router.allowedMethods());
Here, this.cookies.get("jwt") returns undefined. The POST request is sent using AXIOS library with "withCredentials: true" header and a valid CSRF token:
authenticate.js
axios.post("api/authenticate", {}, {
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-Token": "A VALID CSRF TOKEN GENERATED BY SERVER",
"Content-Type": "application/json",
},
withCredentials: true,
});
Can anyone help me find out why this.cookies.get fails to fetch cookie from a simple POST request? I'm simply posting to my localhost, so I believe this is not a CORS problem.
What is more strange is that when I check my chrome developer tool, the "jwt" and "jwt.sig" tokens are successfully included in the request header..
Any help would be appreciated.
Update: Setting the cookie
//...
this.cookies.set("jwt", "SOME JWT GENERATED BY SERVER", {
httpOnly: true,
signed: true,
});
//...

CORS: Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true

I have a setup involving
Frontend server (Node.js, domain: localhost:3000) <---> Backend (Django, Ajax, domain: localhost:8000)
Browser <-- webapp <-- Node.js (Serve the app)
Browser (webapp) --> Ajax --> Django(Serve ajax POST requests)
Now, my problem here is with CORS setup which the webapp uses to make Ajax calls to the backend server. In chrome, I keep getting
Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
doesn't work on firefox either.
My Node.js setup is:
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:8000/');
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
};
And in Django I'm using this middleware along with this
The webapp makes requests as such:
$.ajax({
type: "POST",
url: 'http://localhost:8000/blah',
data: {},
xhrFields: {
withCredentials: true
},
crossDomain: true,
dataType: 'json',
success: successHandler
});
So, the request headers that the webapp sends looks like:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: "Origin, X-Requested-With, Content-Type, Accept"
Access-Control-Allow-Methods: 'GET,PUT,POST,DELETE'
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: csrftoken=***; sessionid="***"
And here's the response header:
Access-Control-Allow-Headers: Content-Type,*
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE
Content-Type: application/json
Where am I going wrong?!
Edit 1: I've been using chrome --disable-web-security, but now want things to actually work.
Edit 2: Answer:
So, solution for me django-cors-headers config:
CORS_ORIGIN_ALLOW_ALL = False
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
'http://localhost:3000' # Here was the problem indeed and it has to be http://localhost:3000, not http://localhost:3000/
)
This is a part of security, you cannot do that. If you want to allow credentials then your Access-Control-Allow-Origin must not use *. You will have to specify the exact protocol + domain + port. For reference see these questions :
Access-Control-Allow-Origin wildcard subdomains, ports and protocols
Cross Origin Resource Sharing with Credentials
Besides * is too permissive and would defeat use of credentials. So set http://localhost:3000 or http://localhost:8000 as the allow origin header.
If you are using CORS middleware and you want to send withCredential boolean true, you can configure CORS like this:
var cors = require('cors');
app.use(cors({credentials: true, origin: 'http://localhost:3000'}));
Expanding on #Renaud idea, cors now provides a very easy way of doing this:
From cors official documentation found here:
"
origin: Configures the Access-Control-Allow-Origin CORS header.
Possible values:
Boolean - set origin to true to reflect the request origin, as defined by req.header('Origin'), or set it to false to disable CORS.
"
Hence we simply do the following:
const app = express();
const corsConfig = {
credentials: true,
origin: true,
};
app.use(cors(corsConfig));
Lastly I think it is worth mentioning that there are use cases where we would want to allow cross origin requests from anyone; for example, when building a public REST API.
try it:
const cors = require('cors')
const corsOptions = {
origin: 'http://localhost:4200',
credentials: true,
}
app.use(cors(corsOptions));
If you are using express you can use the cors package to allow CORS like so instead of writing your middleware;
var express = require('express')
, cors = require('cors')
, app = express();
app.use(cors());
app.get(function(req,res){
res.send('hello');
});
If you want to allow all origins and keep credentials true, this worked for me:
app.use(cors({
origin: function(origin, callback){
return callback(null, true);
},
optionsSuccessStatus: 200,
credentials: true
}));
This works for me in development but I can't advise that in production, it's just a different way of getting the job done that hasn't been mentioned yet but probably not the best. Anyway here goes:
You can get the origin from the request, then use that in the response header. Here's how it looks in express:
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', req.header('origin') );
next();
});
I don't know what that would look like with your python setup but that should be easy to translate.
(Edit) The previously recomended add-on is not available any longer, you may try this other one
For development purposes in Chrome, installing
this add on will get rid of that specific error:
Access to XMLHttpRequest at 'http://192.168.1.42:8080/sockjs-node/info?t=1546163388687'
from origin 'http://localhost:8080' has been blocked by CORS policy: The value of the
'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'
when the request's credentials mode is 'include'. The credentials mode of requests
initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
After installing, make sure you add your url pattern to the Intercepted URLs by clicking on the AddOn's (CORS, green or red) icon and filling the appropriate textbox. An example URL pattern to add here that will work with http://localhost:8080 would be: *://*
Though we have many solutions regarding the cors origin, I think I may add some missing part. Generally using cors middlware in node.js serves maximum purpose like different http methods (get, post, put, delete).
But there are use cases like sending cookie response, we need to enable credentials as true inside the cors middleware Or we can't set cookie. Also there are use cases to give access to all the origin. in that case, we should use,
{credentials: true, origin: true}
For specific origin, we need to specify the origin name,
{credential: true, origin: "http://localhost:3000"}
For multiple origins,
{credential: true, origin: ["http://localhost:3000", "http://localhost:3001" ]}
In some cases we may need multiple origin to be allowed. One use case is allowing developers only. To have this dynamic whitelisting, we may use this kind of function
const whitelist = ['http://developer1.com', 'http://developer2.com']
const corsOptions = {
origin: (origin, callback) => {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error())
}
}
}
Had this problem with angular, using an auth interceptor to edit the header, before the request gets executed. We used an api-token for authentification, so i had credentials enabled. now, it seems it is not neccessary/allowed anymore
#Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
req = req.clone({
//withCredentials: true, //not needed anymore
setHeaders: {
'Content-Type' : 'application/json',
'API-TOKEN' : 'xxx'
},
});
return next.handle(req);
}
Besides that, there is no side effects right now.
CORS ERROR With NETLIFY and HEROKU
Actually, if none of the above solutions worked for you then you might wanna try this.
In my case, the backend was running on Heroku and the frontend was hosted on netlify.
in the .env file, of the frontend, the server_url was written as
REACT_APP_server_url = "https://ci-cd-backend.herokuapp.com"
and in the backend, all my api calls where written as,
app.get('/login', (req, res, err) => {});
So, Only change you need to do is, add /api at the end of the routes,
so, frontend base url will look like,
REACT_APP_server_url = "https://ci-cd-backend.herokuapp.com/api"
and backend apis should be written as,
app.get('/api/login', (req, res, err) => {})
This worked in my case, and I believe this problem is specifically related when the front end is hosted on netlify.

Resources