This gist is an example of how you can coordinate between the socket client, socket server, and the
feathers-authentication
plugin to authenticate the socket.io connection using a request header.
This saves you from having to call app.authenticate()
in order to authenticate the socket. Instead,
the socket connection will be ready to make authenticated requests, assuming the stored JWT accessToken
is still valid.
Last active
May 18, 2021 17:08
-
-
Save marshallswain/9e97fdbb8be7d910f0a7da44c669822a to your computer and use it in GitHub Desktop.
Authenticate on feathers-socketio connection with header
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const socketio = require('feathers-socketio') | |
const authOnSocketConnect = require('./authenticate-on-socket-connect') | |
// ... Setup your Feathers app code or use the generator then replace the socketio registration with this | |
// When you register the feathers-socketio plugin, use the utility | |
app.configure(socketio(function (io) { | |
// Get Socket.io headers | |
io.on('connection', function (socket) { | |
authOnSocketConnect({ app, socket }) | |
}) | |
}, { timeout: app.get('socketTimeout') })) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const jwt = require('jsonwebtoken') | |
module.exports = function ({ app, socket, userService = 'users' }) { | |
const accessToken = socket.handshake.headers.authorization | |
if (accessToken) { | |
// Verify the JWT that was passed in | |
jwt.verify(accessToken, app.get('authentication').secret, function (err, payload) { | |
if (err) { | |
console.log('invalid token', err) | |
} | |
// The JWT was valid, so request the user object | |
app.service(userService).get(payload.userId) | |
.then(user => { | |
// This makes the user available at hook.params.user | |
socket.feathers.user = user | |
// Now create a new JWT | |
return app.service('authentication').create({strategy: 'jwt', accessToken}, socket.feathers) | |
}) | |
.then(response => { | |
// And finally initiate a socket message to the client with response of `{accessToken}` | |
socket.emit('authentication created', response) | |
}) | |
// Yeah, yeah. This only handles the happy path. I said it was a hack. ;) | |
.catch(error => { | |
console.log(error) | |
}) | |
}) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const feathers = require('feathers/client') | |
const io = require('socket.io-client') | |
const axios = require('axios') | |
const socketio = require('feathers-socketio/client') | |
const auth = require('feathers-authentication-client') | |
const hooks = require('feathers-hooks') | |
const host = 'http://localhost:3030' | |
var socket = io(host, { | |
transports: ['websocket'], | |
// Send the authorization header in the initial connection request | |
extraHeaders: { | |
Authorization: window.localStorage.getItem('feathers-jwt') | |
} | |
}) | |
const feathersClient = feathers() | |
.configure(socketio(socket, { timeout: 60000 })) | |
.configure(hooks()) | |
.configure(auth()) | |
// Listen to authentication events and set the new token on the feathersClient | |
feathersClient.service('authentication').on('created', response => { | |
feathersClient.passport.setJWT(response) | |
}) | |
return feathersClient |
I'm getting an error saying that:
error TS2345: Argument of type '{ transports: string[]; polling: { extraHeaders: { Authorization: string; }; }; }' is not assignable to parameter of type 'ConnectOpts'. Object literal may only specify known properties, and 'polling' does not exist in type 'ConnectOpts'.
Any idea why this might be happening?
Found the issue!
It actually goes like this:
transportOptions:{
polling: {
extraHeaders: {
'Authorization': window.localStorage.getItem('access_token')
}
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Custom headers will not be sent if the transport is
websocket
. To sendextraHeaders
(at least with current socket.io-client) the socket needs to be configured to init as a polling connection before subsequently upgrading [to a WebSocket]: