Passport and SailsJS, how to get isAuthenticated() test to pass - sails.js

My code keeps failing here when the user tries to login:
isAuthenticated: function (req, res) {
if (req.isAuthenticated()) { return res.json(req.user); }
else { return res.send(401); }
},
It FAILS and I get GET http://localhost:1337/user/authenticated 401 (Unauthorized) in the console, even though the user has entered in a correct email and password.
Where in the code makes that test pass?
I have the related StackOverflow question with more info HERE.

The problem was that my frontend application has a different origin than my backend application, so the AJAX requests will not include the session cookie and req.isAuthenticated() will never return true.
Use the withCredentials options to force it.
$http({ withCredentials: true, ... })

Related

next-auth getSession serverside doesn't trigger visibilityChange event to refresh token

Using "next-auth": "^4.2.1", with CredentialsProvider. Already implemented token rotation and is working alright. On the client side, useSession hook refreshes the session token when it expires. I need to obtain the token to use in my GraphQL query in getServerSideProps like this;
const session = await getSession({
req,
broadcast: true,
triggerEvent: true,
event: 'visibilitychange',
});
if (!session) {
return {
redirect: {
destination: '/login',
permenant: false,
},
};
}
const token = await getToken({
req,
secret: process.env.TOKEN_SECRET,
raw: true,
});
As long as I keep the same browser open, this works fine. However, If I close the browser, and come back after token expiresIn period, open a new browser and navigate directly to my page http://localhost:3000/cities, old token, which was in the session cookie when I've closed the browser, returns from getToken. I'm expecting calling getSession with trigger & event parameters would force jwt process and creates a new token, as in client side. Am I missing anything? What are the purpose of event, triggerEvent & broadcast parameters then?
Thanks in advance

Saml Strategy Verify callback not getting called

As per the passportjs documentation, the verifier function is supposed to be called.
But I am not seeing this call back.
I am testing this with okta. In okta the app is configured to send the saml response to /fromokta
The okta is redirecting to /fromokta
What am I missing?
const saml = require('passport-saml').Strategy;
var strategy = new saml({
entryPoint: "<some oktka entiry point>",
issuer: "http://www.okta.com/foobar",
protocol: "http://"
},(profile, done) => {
console.log(">>>> callback from okta");
console.log(profile);
done(profile)
})
app.post('/login', function(req, res) {
passport.authenticate(
'saml',
{
//successRedirect: '/success',
failureRedirect: '/'
}
)}
)
app.post('/fromokta', function(req, resp){
// this gets called
})
This most likely means your POST /login route never gets the request. Look at the network requests and see if there is indeed a POST /login to the URL, make sure the protocol is the same too (http and https)

How to specify redirectUrl after logout for Ambassador OAuth2 Filter with Keycloak?

I'm using the Ambassador OAuth2 Filter to perform OAuth2 authorization against Keycloak.
For the logout I use the the RP-initiated logout as described in the Docs of Ambassador
The logout works fine. However I could not figure out how to provide the redirect url needed for Keycloak to redirect to the Login page after successfully logged out. As a result the user stays on the blank logout page of keycloak.
The RP-initiated logout looks as follows
const form = document.createElement('form');
form.method = 'post';
form.action = '/.ambassador/oauth2/logout?realm='+realm;
const xsrfInput = document.createElement('input');
xsrfInput.type = 'hidden';
xsrfInput.name = '_xsrf';
xsrfInput.value = getCookie("ambassador_xsrf."+realm);
form.appendChild(xsrfInput);
document.body.appendChild(form);
form.submit();
I expected that Ambassador provides a way to add the redirect url as a query param or something, but I couldn't find a solution.
Are there any suggestions or workarounds?
I found this in the Ambassador documentation that could be overlooked as I did several times:
Ambassador OAuth2 Settings
protectedOrigins: (You determine these, and must register them with your identity provider) Identifies hostnames that can appropriately set cookies for the application. Only the scheme (https://) and authority (example.com:1234) parts are used; the path part of the URL is ignored.
You will need to register each origin in protectedOrigins as an authorized callback endpoint with your identity provider. The URL will look like {{ORIGIN}}/.ambassador/oauth2/redirection-endpoint.
So it looks like ambassador hard codes the redirection-endpoint (redirect_uri) that you need add to your OAuth2 client in Keycloak.
I found a solution for that, is not the best solution but you will logout using a button.
async function logout() {
const data = new URLSearchParams("realm=keycloak-oauth2-filter.ambassador")
data.append('_xsrf', getCookie("ambassador_xsrf.keycloak-oauth2-filter.ambassador"));
fetch('/.ambassador/oauth2/logout', {
method: 'POST',
body: data
})
.then(function (response) {
if (response.ok) {
return response.text()
} else {
throw "err";
}
})
.then(function (text) {
console.log(text);
})
.catch(function (err) {
console.log(err);
});
}

how to reset password of users whose email is not verified in loopback?

So I ran into this issue.
I have a user who has emailedVerified as false.
So, when I try to reset password for that user as follows it gives me user unverified error.
Person.resetPassword({
email: email
}, function (err) {
if (err) return res.status(422).send(err);
});
So if user has emailVerified as false I created a token for the user with token data as follows:
const tokenData = {
ttl: 900,
scopes: ['reset-password'],
};
user.createAccessToken(tokenData, req, function (err, token) {
//email link with token
});
Now when I try to change password with following request.
/api/people/reset-password?access_token=generated-token and data message as {newPassword: “newPassword”}
I’m getting Access Denied for POST /api/people/reset-password?access_token=token
--Context scopes of Person.setPassword()
This happening only for generated token (either for verified user or non-verified user). If verified user request for password-change its successful which is done by following code.
Person.resetPassword({
email: email
}, function (err) {
if (err) return res.status(422).send(err);
});
I have following settings in person model, which i removed, but still it says access denied.
"restrictResetPasswordTokenScope": true,
"emailVerificationRequired": true,
I found this code in loopback/common/models/user.js:
User.resetPassword = function(options, cb) {
...
if (UserModel.settings.emailVerificationRequired && !user.emailVerified) {
err = new Error(g.f('Email has not been verified'));
err.statusCode = 401;
err.code = 'RESET_FAILED_EMAIL_NOT_VERIFIED';
return cb(err);
}
...
}
Looks like email verification validation only depends on the emailVerificationRequired setting. The value should be false if you want to enable reset password for not verified users:
"emailVerificationRequired": false, // The deletion of this property should also work as I don't see a default value in user.json
If it will not help, I suggest just debug the method above. I think it should be easy to find the problem, when you know the place to search.

Disable redirect in fetch request using React native

I'm trying to crawl a web using React Native which has no API. It's written in PHP.
To log an user, a POST request must be sent. The response returns a cookie with a PHPSessid cookie which I must capture to use in subsequent requests.
I would like to capture the cookie value, buy the POST response is a 302 and the redirection is followed automatically, so I can't see the cookie. In node I was able to do it with redirect:manual, but it does not work in react native.
The cookie is sent automatically in subsequent requests, buy I'm trying to manage cookies by hand with react-native-cookie and I'd like to know if it's possible.
Do you know a way to stop the redirection?
I've been checking the code and what I did was the following:
Clear all cookies
Launch an empty login request
Capture the PHPSessID coookie
Launch a login request with that PHPSessID
After that, the subsequent fetch requests would have automatically a PHPSessID cookie with a valid logged in user, so we can use the site with simple fetchs
Here is some code, but the important thing is that you do a first empty login request, capture the PHPSessid and launch the real login request with that PHPSessid.
This would be the main function:
import Cookie from 'react-native-cookie';
// I think this is used only to clear the cookies
function login(user, pass){
// clear all cookies for all domains
// We need to start withouth authorization token
Cookie.clear();
const makeLoginRequest = (sessid) =>
makeLoginRequestForUserAndPass(user,pass,sessid);
return makeInitialRequest()
.then(getSessionIDFromResponse)
.then(makeLoginRequest)
.then(checkIfLoggedAndGetSessionID);
}
The initial request is a request to the login script. Note that I used GET because it worked with my site, perhaps an empty post would be necessary:
function makeInitialRequest() {
const INIT_PATH = '/index.php?r=site/login';
const INIT_URL = site + INIT_PATH;
const request = new Request(INIT_URL, options....);
return fetch(request);
}
We have the session ID in the response. I used a simple regex to extract it. Note that we are not logged in; PHP has created a session and that's what we have here:
function getSessionIDFromResponse(response) {
return getPHPSessIdFromCookie(response.headers.get('set-cookie'));
}
function getPHPSessIdFromCookie(header) {
const regex = /PHPSESSID=(\w*)/;
const match = regex.exec(header);
return match ? match[1] : '';
}
Now the login request. Note that I can't stop redirection here, but I't have to do it because we can have PHPSessid later. Redirection must be set to manual in POST request:
function makeLoginRequestForUserAndPass(user, pass, sessid) {
const request = buildLoginRequest(user, pass, sessid);
return fetch(request);
}
// This is where we build the real login request
function buildLoginRequest(user, pass, sessid) {
const LOGIN_PATH = '/index.php?r=site/login';
const LOGIN_URL = site + LOGIN_PATH;
const fields = [
{name: 'LoginForm[username]', value: user},
{name: 'LoginForm[password]', value: pass},
etc...
];
const data = translateFieldsToURLEncodedData(fields);
const headers = {
'Content-type': 'application/x-www-form-urlencoded',
Cookie: `PHPSESSID=${sessid}`, // HERE is where you put the data
};
const options = { method: 'POST',
headers: headers,
mode: 'cors',
cache: 'default',
agent: proxy,
body: data,
redirect: 'manual' // VERY IMPORTANT: if you don't do it, the cookie is lost
};
return new Request(LOGIN_URL, options);
}
// Simple utility function
function translateFieldsToURLEncodedData(fields){
let pairs = fields.map( (field) => {
return encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value);
});
return pairs.join('&');
}
This is the last part. To see if I was logged in I checked if the response had text belonging to login error's page. I also got the PHPSessid (I think it changed after login, not sure, it was a year ago) but I don't know if I used it, I believe it was included automatically in subsequent requests. I think this part could be simplified an improved:
function checkIfLoggedAndGetSessionID(response) {
return (
checkIfLoggedOK(response)
.then(() => getSessionIDFromResponse(response))
);
}
function checkIfLoggedOK(response){
return getTextFromResponse(response)
.then(throwErrorIfNotLogedOk);
}
function getTextFromResponse(response) {
return response.text();
}
function throwErrorIfNotLogedOk(page) {
if(isErrorPage(page)) throw new Error("Login failed");
}
function isErrorPage(text) {
const ERROR_MESSAGE = 'Something that appears in login failed page of your site';
let n = text.search(ERROR_MESSAGE);
return n !== -1;
}
Hope this can be useful.