In the example of oauth2 strategy usage in the Passport's repo, the following function is presented:
passport.use(new OAuth2Strategy({
authorizationURL: 'https://www.example.com/oauth2/authorize',
tokenURL: 'https://www.example.com/oauth2/token',
clientID: EXAMPLE_CLIENT_ID,
clientSecret: EXAMPLE_CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/example/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate({ exampleId: profile.id }, function (err, user) {
return done(err, user);
});
}
));
How does Passport obtains the profile field? is it provided with the token by the oauth endpoint? or does it come from a separate (session-related) request?
When using, for example, the Facebook's oauth API, the user info is loaded automatically with the Passport's Facebook strategy, so I'm trying to figure out how does this happen and how to implement a similar behavior in a custom oauth2 API.
The user profile is typically loaded after the access_token is successfully retrieved:
https://github.com/jaredhanson/passport-oauth2/blob/master/lib/strategy.js#L175
this._oauth2.getOAuthAccessToken(code, { grant_type: 'authorization_code', redirect_uri: callbackURL },
function(err, accessToken, refreshToken, params) {
if (err) { return self.error(self._createOAuthError('Failed to obtain access token', err)); }
self._loadUserProfile(accessToken, function(err, profile) {
if (err) { return self.error(err); }
The function to actually get the user information is often provided by the specific strategy (e.g. Facebook, Twitter, etc)
In Facebook's implementation:
https://github.com/jaredhanson/passport-facebook/blob/master/lib/strategy.js#L137
Related
So I want to make a post request to my nextJS backend and the route i am making the req to a protected route so in my Rest client file (req.rest) I need to tell auth0 im authenticated but i do not know how to do that.
req.rest
POST http://localhost:3000/api/video
Content-Type: application/json
Authorization: Bearer cookie
{
"title": "Video",
"description": "Video description"
}
api/video.js
import { withApiAuthRequired, getSession } from "#auth0/nextjs-auth0";
import Video from "../../database/models/Video";
export default withApiAuthRequired(async function handler(req, res) {
if (req.method === "POST") {
try {
const { user } = getSession(req, res);
const newVideo = new Video({
title: req.body.title,
description: req.body.description,
ownerId: user.sub,
});
await newVideo.save();
res.json(newVideo);
} catch (error) {
res.status(500).json({ error: error.message });
}
}
});
I'm not sure I understand your question. Your API should determine if the user is authenticated by validating the bearer token value you are passing through the Authorization request header, you shouldn't need to pass additional data as separate parameters to authorize the API. If you do need additional data to determine if the user is authorized to consume the API, that should be included inside of the bearer token as a claim.
So I haven't really found a solution but I do have a workaround which is to just make new page on the frontend for requests and send the requests from there.
I am trying to authorize a pre logged in user with a Facebook account. I want to store the auth token of Facebook to later post stuff using my CMS.
I am using Express/NodeJS and Passport JS.
My FacebookStrategy looks like this:
module.exports = new FacebookStrategy(
{
clientID,
clientSecret,
callbackURL: `${config.apiUrl}/v1/auth/connect/facebook/callback`,
passReqToCallback: true
},
async function(req, token, tokenSecret, profile, done) {
console.log("SESSION?", req.session)
console.log("THIS SHOULD BE SET!", req.user) // But is not!
// Stuff is done.
done(null, token, {savedConnectionForLaterUse});
}
I also have two routes:
router.get('/connect/facebook',
API_KEY_OR_JWT_AUTH_MIDDLEWARE,
(req, res, next) => {
// Save authInfo in session
Object.assign(req.session, {account: req.authInfo.account._id, user: req.user._id})
passport.authorize('facebookConnect', {
failureRedirect: `${frontUrl}/settings/connections`,
scope: facebookOAuthScopes, // This is an array of scopes I need
})(req, res, next)
},
);
router.get('/connect/facebook/callback',
passport.authorize('facebookConnect', {
failureRedirect: `${apiUrl}/v1/auth/connect/facebook/failure`,
}),
(req, res) => {
const { session: {connection} } = req;
res.redirect(`${frontUrl}/settings/connections/edit/${connection}`);
}
);
When I am running this on my local machine it works due to the fact that the session is there and in the session I can find my user for later use. As soon as I am deploying this on a server (with kubernetes) the session is gone.
The configuration of the express session looks like this:
app.use(
expressSession({
secret: config.security.secret,
resave: true,
saveUninitialized: true,
cookie: {
sameSite: 'none', // This was something I tried.. didn't help thou
secure: true,
},
})
)
Can anyone point me into the right direction? What am I doing wrong?
Thank you all for your help in advance. I am really at the end of my knowledge. The struggle is real! :D
I am in a next-js app and my auth token is stored in cookies.
For some raisons i use Swr and Api route to fetch my secured api backend.
i am trying to find a way to put my auth token in all api request.
During login cookie is set
res.setHeader(
'Set-Cookie',
cookie.serialize('token', data.access_token, {
httpOnly: true,
secure: process.env.NODE_ENV !== 'development',
maxAge: data.expires_in, // 1 week
sameSite: 'strict',
path: '/',
}),
);
This is an example of a page using swr fetch
//page/test.ts - example of my test route
const { data, error } = useFetchContent(id);
if (error) {
showError('error');
replace('/');
}
return <DisplayContent content={data} />
This is a swrFetchHook
// fetchContentHook
function useFetchContent(id: string): ContentDetail {
return useSWR<any>(`/api/content/${id}`, fetcherApiRoute);
}
const fetcherApiRoute = (url: string): Promise<any> => {
return axios(url)
.then((r) => r.data)
.catch((err) => {
console.info('error is ', err)
throw err
});
};
export default useFetchContent;
inside api route
export default async (req, res): Promise<ContentDetail> => {
const { id } = req.query;
if (req.method === 'GET') {
const fetchRealApi = await apiAxios(url);
if(fetchRealApi) {
// here depending on result of fetchRealApi i add some other fetch ...
return res.status(200).json({ ...fetchRealApi, complement: comp1 });
}
return res.status(500)
}
return res.status(500).json({ message: 'Unsupported method only GET is allowed' });
};
and finally api axios configuration
const apiAxios = axios.create({
baseURL: '/myBase',
});
apiAxios.interceptors.request.use(
async (req) => {
// HERE i am trying to get token from cookies
// and also HERE if token is expired i am trying to refresh token
config.headers.Authorization = token;
req.headers['Content-type'] = 'application/x-www-form-urlencoded';
return req;
},
(error) => {
return Promise.reject(error);
},
);
export default apiAxios;
I am stuck here because i cant find token during apiAxios.interceptors.request.use...
Did you know what i am doing wrong, and am i on a correct way to handle this behavior ?
To allow sending server cookie to every subsequent request, you need to set withCredentials to true. here is the code.
const apiAxios = axios.create({
baseURL: '/myBase',
withCredentials: true,
});
Nilesh's answer is right if your API is able to authorize requests based on cookies. Also it needs the API to be in the same domain as your frontend app. If you need to send tokens to the API (the one which is in the cookie), then you will need a small backend component often called BFF or Token Handler. It can extract the token from the cookie and put in an Authorization header.
At Curity we've created a sample implementation of such a Token Handler, of which you can inspire: https://github.com/curityio/kong-bff-plugin/ You can also have a look at an overview article of the Token Handler pattern.
I am using passport-saml to authenticate users via Google IDP(SAML APP)
My SAML Strategy is configured as below
const samlStrategy = new SamlStrategy({
protocol: PROTOCOL,
entryPoint: SSO_URL, // SSO URL (Step 2)
issuer: SP_ENTITY_ID, // Entity ID (Step 4)
path: CALLBACK_PATH, // ACS URL path (Step 4)
cert: IDP_CERT,
logoutUrl: 'https://accounts.google.com/logout',
logoutCallbackUrl: '/signout'
}, function (profile, done) {
done(null, JSON.parse(JSON.stringify(profile)))
})
passport.use(samlStrategy)
Using the Passport SAML Strategy, I am able to login successfully
On Logout, I am logging out of SAML Strategy as below
server.get('/logout', function (req, res) {
try {
req.user.nameID = req.user.nameID;
req.user.nameIDFormat = req.user.nameIDFormat;
samlStrategy.logout(req, function(err, requestUrl){
if(err){
return res.send({ success: false, error: err });
}
req.logout()
req.session=null
req.user=null
return res.redirect(requestUrl);
});
} catch(error) {
return res.send({ success: false, error });
}
})
This is logging me out of all Google accounts that are logged into the browser.
QUESTIONS:
Is there a way to just logout only from the specific Google account that I have used for SAML Strategy?
Logout callback url is also not called
Trying to get Meteor Facebook login to work. It functions fully in that it uses Facebook API and requests the correct permissions from the users account and then logs in successfully.
The problem is it doesn't save the permission requested information even though its been approved and only the basic name and ID are available in Meteor.user().services.facebook. Is this code not working because it's not saving the users details on login? I can't find a resource that details how to save or extract the other data.
Simply trying to console log the data to see that it's been extracted out of the Facebook user account on log in.
Within Meteor.isClient code:
Template.login.events({
'click #facebook-login': function(event) {
Meteor.loginWithFacebook({ requestPermissions: ['email', 'public_profile', 'user_friends', 'user_likes']}, function(err){
if (err) {
throw new Meteor.Error("Facebook login failed");
}
console.log(Meteor.user().services.facebook.name);
console.log(Meteor.user().services.facebook.id);
console.log(Meteor.user().services.facebook.email);
console.log(Meteor.user().services.facebook.gender);
});
},
'click #logout': function(event) {
Meteor.logout(function(err){
if (err) {
throw new Meteor.Error("Logout failed");
}
});
}
The config code:
ServiceConfiguration.configurations.remove({
service: 'facebook'
});
ServiceConfiguration.configurations.insert({
service: 'facebook',
appId: 'correctAppID',
secret: 'CorrectSecret'
});
For Facebook v2.4 API after you have requested for certain permissions you can then access them by making a graph API call and requesting them with a valid auth token. The code is as follows:
if (user.hasOwnProperty('services') && user.services.hasOwnProperty('facebook') ) {
var result = Meteor.http.get('https://graph.facebook.com/v2.4/' + user.services.facebook.id + '?access_token=' + user.services.facebook.accessToken + '&fields=first_name, last_name, birthday, email, gender, location, link, friends');
console.log(result.data.first_name);
console.log(result.data.last_name);
console.log(result.data.birthday);
console.log(result.data.email);
console.log(result.data.gender);
console.log(result.data.location);
console.log(result.data.link);
console.log(result.data.friends);
}