Retrieving & storing access_token with Passport - express

I'm using PassportJS along with openid-client and cookie-session in my Express server for authentication. I'm using openid-client's "Usage with Passport" as a starting point, and I'm currently able to authenticate successfully. Since I need an access token for APIs that are proxied through my server, I chose to save the access token in my user session with this bit of code:
passport.use(
'oidc',
new Strategy(
{ client, params, passReqToCallback, sessionKey, usePKCE },
(req, tokenset, userinfo, done) => {
logger.info('Retrieved tokenset & userinfo');
// Attach tokens to the stored userinfo.
userinfo.tokenset = tokenset;
return done(null, userinfo);
}
)
);
My next step is to get and store a new access token. Part of the information that comes with tokenset is the expires_at key, and it's set to expire in one hour. So of course, if it's close to expiring or has expired, I want to get a new token.
The documentation says to use:
client.refresh(refreshToken) // => Promise
.then(function (tokenSet) {
console.log('refreshed and validated tokens %j', tokenSet);
console.log('refreshed id_token claims %j', tokenSet.claims);
});
Okay, I understand that and I have that bit of code working as well. But I have no idea how to save it back into my user session. If I make a subsequent API call that's proxied through my server, I'll still have the old token in req.session.passport.user.tokenset.access_token. So how do I update that?
(I probably should put as a caveat I'm still very new to OpenID / Oauth authentication as well as Passport itself, so some of what I'm doing may be completely obvious.)

Related

nextauth with aspnetcore backend

I need a sanity check on what I'm trying to do here.
I want to build a webapp with nextjs where people can log in with discord and as a backend API I want to use a aspnetcore web api.
I got next-auth to work with discord pretty quickly in the frontend. However I'm struggling on how to identify my frontend to my backend.
My plan at the moment is to have my backend create another JWT token and save that somewhere and then use it as the Authorization header in calls to the backend api.
next-auth has callbacks where I can edit the session and the token. So what I plan to do at the moment is just call the backendapi/createJwtToken endpoint, save it to the already existing next-auth token and then into the next-auth session.
Then I could access it anywhere and don't have to refresh until the session is gone.
I can do that with next-auth callbacks
callbacks: {
async session({ session, token, user }) {
session.backendApiToken = token.backendApiToken;
return session;
},
async jwt({ token, account }) {
if (account) { // this fires only on sign in
token.backendApiToken = "ABC - get it from backend/createToken";
}
return token;
},
Is this okay? I know how to create and validate tokens in an aspnetcore api.
Is something unsecure or strange about saving an encoded apiToken in the next-auth token? Or is this absolutely normal?

React-redux and React Native error handling for expired JWT

I have a system where the authentication is based around JWT. I have a JWT auth token, and a refresh token on the client. The refresh token is stored in the database on the server, and is used to refresh the JWT once the JWT expires every 12 hours - pretty standard setup I believe.
My issue is incorporating it in to my React Native project and handling token expiration. The user will likely be performing many API calls to their "protected" areas which require the JWT, but once the JWT expires it will send back a "UnauthorizedError" like so:
router.use('/protected', ejwt({secret: config.secret}));
//catch any errors if the JWT is not valid
router.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
res.status(401).send({
success: false,
error: "UnauthorizedError"
});
}
});
This will send back to the RN app that the JWT has expired, but at this point I would like the app to send another request back to the server generate a new JWT, but I'm not sure of a way to do this most efficiently for every API call initiated from my app. Each API call could call a function to check the server if the returned value is "UnauthorizedError", but I know this isn't the best way:
return AuthApi.someApiCall(token).then(response => {
if(response.success){
//do some success stuff
}else{
//this seems bad to be repeated for every API call
checkToken(response).then(() => {
dispatch(updateAuthToken());
});
}
})
I've been looking around and Redux middleware looks promising, but I don't know if there's a more established way perhaps, or anyone has any other thoughts?
Thanks!

How to pass Firebase Auth Token from client to server?

The website that I'm working on uses Firebase authentication and different users that login have different permissions as to which pages they can visit.
The way signing in is setup is similar to this post:
User Logins in with two parameters - "id" and "email"
Server uses these to create a custom "uid", then uses the Firebase Admin SDK to create a custom token that is sent back to the client.
The client logs in with the Javascript Firebase SDK - firebase.auth().signInWithCustomToken()
Now that the user is logged in, they can click different pages - i.e. '/foo', '/bar'
The issue I'm running into is that when they visit new pages, I'm trying to pass the token from the client back to the server (almost identical to how its done in this Firebase Doc ), verify the token & check if it has permission to view the webpage.
I'm trying to figure out the best (& most secure) way to do this. I've considered the following option:
Construct a URL with the token, but I've heard this isn't good practice because the token is getting exposed and session hijacking becomes a lot easier.
I've been trying to pass the token in the request header, but from my understanding you can't add headers when the user clicks on a link to a different page (or if its redirected in javascript). The same issue applies to using POST.
What can I do to securely pass this information to the server and check permissions when a user clicks on a link to a different page?
You can get the accessToken (idToken) on client side by:
var accessToken = null;
firebase.auth().currentUser
.getIdToken()
.then(function (token) {
accessToken = token;
});
and pass it in your request headers:
request.headers['Authorization'] = 'Bearer ' + accessToken;
and on your server side get the token with your prefered method and authenticate the request with Firebase Admin SDK, like (Node.js):
firebaseAdmin.auth()
.verifyIdToken(accessToken)
.then(decodedIdToken => {
return firebaseAdmin.auth().getUser(decodedIdToken.uid);
})
.then(user => {
// Do whatever you want with the user.
});
Nowadays, it looks like we're meant to use httpsCallable() client-side to get an object pre-authorized to talk to your endpoint.
eg:
// # ./functions/index.js
exports.yourFunc = functions.https.onCall((data, context) => {
// Checking that the user is authenticated.
if (!context.auth) {
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}
// ... rest of your method
});
// ./src/models/addMessage.js
const firebase = require("firebase");
require("firebase/functions");
firebase.initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FUNCTIONS PROJECT ID ###'
databaseURL: 'https://### YOUR DATABASE NAME ###.firebaseio.com',
});
var functions = firebase.functions();
// This is the new code:
var yourFunc = firebase.functions().httpsCallable('yourFunc');
yourFunc({foo: bar}).then(function(result) {
// ...
});
From firebase documentation

ember simple auth session, ember data, and passing a Authorization header

I have a working oauth2 authentication process where I get an access token (eg from facebook) using ember simple auth, send it to the back end which calls fb.me() and then uses JWT to create a token. This token is then sent back to the ember app, which then has to send it with every server request, include those requests made by ember-data.
I also need to have this token available after a browser reload.
I have tried many options, where I set a property 'authToken' on the session - I believe that this uses local storage to persist the authenticated session.
But I always seem to have trouble with coordinating the retrieval of this token - either I don't have access to the session, or the token is no longer on the session, or I can't change the ember data headers.
Does anyone have a working simple example of how this can be done - I think it should be easy, but I'm obviously missing something!
Thanks.
Update
The only thing I've been able to get working is to use torii as shown below, but the session content is still lost on refresh - I can see its still authenticated, but its lost the token I set here. So I'm still looking for a real solution.
authenticateWithGooglePlus: function () {
var self = this;
this.get('session').authenticate('simple-auth-authenticator:torii', 'google-oauth2')
.then(function () {
resolveCodeToToken(self.get('session'), self);
});
}
resolveCodeToToken gets the bearer token from the server, sets it on the session and then transitions to the protected page:
function resolveCodeToToken(session, route) {
var authCode = session.content.authorizationCode;
var type = session.content.provider.split('-')[0];
$.ajax({
url: 'http://localhost:4200/api/1/user/auth/' + type,
data: {authCode: authCode}
}).done(function (response) {
// todo handle invalid cases - where user is denied access eg user is disabled
session.set('authToken', response.token);
route.transitionTo('activity', moment().format('DDMMYYYY'));
});
}
And I have a custom authorizer for putting the token (stored in the session) on every request:
import Base from 'simple-auth/authorizers/base';
export default Base.extend({
authorize: function(jqXHR, requestOptions) {
var accessToken = this.get('session.content.authToken');
if (this.get('session.isAuthenticated') && !Ember.isEmpty(accessToken)) {
jqXHR.setRequestHeader('Authorization', accessToken);
}
}
});
I'm not sure why this.get('session.content.authToken') would be undefined after a refresh, I thought by default the session was persisted in local storage. The fact that it is authenticated is persisted, but thats useless without the token since the server will reject calls to protected endpoints.
You'd want to implement your own custom authenticator that first gets a token from Facebook and then sends that to your own server to exchange it for a token for your app. Once you have that you get authorization of ember-data requests as well as session persistence etc. for free.
Have a look at this example: https://github.com/simplabs/ember-simple-auth/blob/master/examples/7-multiple-external-providers.html

socket.io authentication after socket established

I'm working on a small multiplayer game. I'd like to introduce authentication. I'm using Node.js and Socket.io.
When the user arrives that the main page - I want them to join the game whether they are logged in or not - but they will be unable to do anything within it (only watch).
How could I then go about authenticating the user on the already open socket?
Could I maintain the authentication still if they left the site and came back? Can you pass a cookie through a web socket?
EDIT
To further my question. One of the possible thoughts I've had is to provide the websocket connection, then when they try to login in, it passes username and password as a message to the websocket.
client.on('onLogin', loginfunction);
I could then take the username and password, check against the database, then take the session ID of the socket and pass it somewhere to say that session is authenticated to that user.
Is this secure? Could I still implement a cookie on the socket so they could come back? Is there any way within socket.io of stating that the socket is now authenticated instead of manually checking on each message received?
Cheers
This isn't actually too hard, but you're approaching it the wrong way. A couple things:
You cannot set a cookie with socket.io; you can, however, get the cookie values of any connected client at any time. In order to set a cookie, you will have to send a new http response, meaning the user must first send a new http request (aka refresh or go to a new page, which it sounds is not a possibility for you here).
Yes: socket.io is secure (to the extent that any transmitted data can be).
As such, you can do the following:
On the user's initial connection, create a cookie with a unique session ID, such as those generated from Express's session middleware. You will need to configure these not to expire on session end though (otherwise it will expire as soon as they close their browser).
Next you should create an object to store the cookie session IDs. Each time a new connect.sid cookie is set, store in your new object with a default value of false (meaning that the user has been authenticated by session, but not by logon)
On the user's login, send a socket emit to the server, where you can then authenticate the login credentials, and subsequently update the session id object you created to read true (logged in) for the current socket id.
Now, when receiving a new http request, read the cookie.sid, and check if its value in your object is true.
It should look something like the following:
var express = require('express'),
http = require('http'),
cookie = require('cookie');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);
app.use(express.cookieParser());
app.use(express.session({
secret: 'secret_pw',
store: sessionStore,
cookie: {
secure: true,
expires: new Date(Date.now() + 60 * 1000), //setting cookie to not expire on session end
maxAge: 60 * 1000,
key: 'connect.sid'
}
}));
var sessionobj = {}; //This is important; it will contain your connect.sid IDs.
//io.set('authorization'...etc. here to authorize socket connection and ensure legitimacy
app.get("/*", function(req, res, next){
if(sessionobj[req.cookies['connect.sid']]){
if(sessionobj[req.cookies['connect.sid']].login == true){
//Authenticated AND Logged in
}
else{
//authenticated but not logged in
}
}
else{
//not authenticated
}
});
io.sockets.on('connection', function(socket){
sessionobj[cookie.parse(socket.handshake.headers.cookie)['connect.sid'].login = false;
sessionobj[cookie.parse(socket.handshake.headers.cookie)['connect.sid'].socketid = socket.id;
socket.on('login', function(data){
//DB Call, where you authenticate login
//on callback (if login is successful):
sessionobj[cookie.parse(socket.handshake.headers.cookie)['connect.sid']] = true;
});
socket.on('disconnect', function(data){
//any cleanup actions you may want
});
});
Chris, I'm won't be able to answer since I'm not an expert on socket.io, but I can maybe try to point you in another direction that can help you - and take away some development time.
But first, a disclaimer: I work for Realtime.co and am not trying to do any sort of advertising. I work closely with developers and I'm just trying to help you by providing you an out-of-the-box solution for your problem. Also, being a gamer, I can't stay away from trying to help people getting their games out there!
Realtime uses an authentication/authorization layer in which you can provide user read/write permissions to channels. When users enters the website you can give them read only permissions to the game channel and once they login, you can then give them write permissions. This can be easily done by doing an authentication post and reconnecting to the server (it can all be done client side). I would do it server-side, though, to increase security.
Realtime has a Node.js API so you can easily integrate it with your server. Since it also has APIs for many other platforms (including mobile) and they all work the same way, you can actually have your game working in multiple platforms over the same communication layer, while having full control over channels.
Thanks for reading.
Edit:
You can read the documentation here for more info: http://docs.xrtml.org/
Socket.io has an option to pass extraHeaders. One can use that to pass a token from the client. The server would use the desired authentication algorithm to decrypt the token and get the user_id.
socket.js
import io from 'socket.io-client';
export const socket = io('https://remote-url');
export const socketAuth = () => {
socket.io.disconnect(); //This uses the same socket and disconnect with the server.
socket.io.opts.extraHeaders = {
'x-auth-token': JSON.parse(window.localStorage.getItem('auth-token')),
};
socket.io.opts.transportOptions = {
polling: {
extraHeaders: {
'x-auth-token': JSON.parse(window.localStorage.getItem('auth-token')),
},
},
};
socket.io.open(); //Opens up a new connection with `x-auth-token` in headers
};
client.js
import { socket, socketAuth } from 'utils/socket';
socket.on('unauthenticated-messages', () => {
someAction();
});
//After user logs in successfully
socketAuth();
socket.on('authenticated-messages', () => {
someAction();
});