Skip to content

Instantly share code, notes, and snippets.

@homerjam
Last active December 3, 2021 14:41
Show Gist options
  • Save homerjam/4bdd2afa4af5f712fcc7b4dc2391583e to your computer and use it in GitHub Desktop.
Save homerjam/4bdd2afa4af5f712fcc7b4dc2391583e to your computer and use it in GitHub Desktop.
Nuxt/Feathers/Vuex SSR
const authentication = require('@feathersjs/authentication');
const jwt = require('@feathersjs/authentication-jwt');
const local = require('@feathersjs/authentication-local');
const oauth2 = require('@feathersjs/authentication-oauth2');
const session = require('express-session');
const Auth0Strategy = require('passport-auth0');
module.exports = function (app) {
const config = app.get('authentication');
app.use(session({
secret: config.secret,
resave: false,
saveUninitialized: false,
}));
// Set up authentication with the secret
app.configure(authentication(config));
app.configure(jwt());
app.configure(local());
app.configure(oauth2(Object.assign({
name: 'auth0',
Strategy: Auth0Strategy,
}, config.auth0)));
const reverifyToken = async (Verifier, options = {}, context) => {
if (context && context.params.authenticated &&
!context.params.user._id) {
const verifier = new Verifier(app, options);
const profile = Object.assign({}, context.params.payload);
if (!profile.id) {
profile.id = context.params.payload.sub;
}
const { payload } = await new Promise((resolve, reject) => {
verifier.verify({}, context.result.accessToken, context.result.refreshToken, profile, (error, user, payload) => {
if (error) {
reject(error);
return;
}
resolve({ user, payload });
});
});
const token = await app.passport.createJWT(payload, app.get('authentication'));
context.result.accessToken = token;
}
return context;
};
// The `authentication` service is used to create a JWT.
// The before `create` hook registers strategies that can be used
// to create a new valid JWT (e.g. local or oauth2)
app.service('authentication').hooks({
before: {
create: [
authentication.hooks.authenticate(config.strategies),
],
remove: [
authentication.hooks.authenticate('jwt'),
],
},
after: {
create: [
(context) => {
if (context.params.payload.iss && /auth0/i.test(context.params.payload.iss)) {
return reverifyToken(
oauth2.Verifier,
{
name: 'auth0',
idField: 'auth0Id',
service: config.service,
entity: config.entity,
},
context,
);
}
return context;
},
],
},
});
};
module.exports = {
'host': 'localhost',
'port': 3030,
'public': '../public/',
'paginate': {
'default': 10,
'max': 50,
},
'authentication': {
'secret': process.env.AUTH0_CLIENT_SECRET,
'strategies': [
'jwt',
'local',
],
'path': '/authentication',
'service': 'users',
'entity': 'user',
'jwt': {
'header': {
'typ': 'access',
},
'audience': process.env.AUTH0_CLIENT_ID,
'subject': 'anonymous',
'issuer': 'https://' + process.env.AUTH0_DOMAIN + '/',
'algorithm': 'HS256',
'expiresIn': '1d',
},
'local': {
'usernameField': 'email',
'passwordField': 'password',
},
'auth0': {
'clientID': process.env.AUTH0_CLIENT_ID,
'clientSecret': process.env.AUTH0_CLIENT_SECRET,
'domain': process.env.AUTH0_DOMAIN,
'scopes': [
'profile',
'openid',
'email',
],
'successRedirect': 'http://localhost:3000/restricted',
},
'cookie': {
'enabled': true,
'name': 'feathers-jwt',
'httpOnly': false,
'secure': false,
},
},
};
import auth from '@feathersjs/authentication-client';
import feathers from '@feathersjs/feathers';
import socketio from '@feathersjs/socketio-client';
import io from 'socket.io-client';
export const host = process.env.API_HOST || 'http://localhost:3030';
export default (storage) => {
const socket = io(host, {
transports: ['websocket'],
});
const feathersClient = feathers()
.configure(socketio(socket))
.configure(auth({ storage }));
return feathersClient;
};
import auth from '~/plugins/auth';
export default async (context) => {
if (process.server) {
return;
}
auth(context);
};
require('dotenv').config();
module.exports = {
...
/*
** Router
*/
router: {
middleware: [
'auth',
],
},
/*
** Plugins
*/
plugins: [
'~/plugins/auth',
],
...
};
import { CookieStorage, parseCookies } from 'cookie-storage';
import jwt from 'jsonwebtoken';
const cookieStorage = new CookieStorage();
const getHashParam = (param) => {
const params = {};
window.location.hash.replace(/([^(?|#)=&]+)(=([^&]*))?/g, ($0, $1, $2, $3): any => {
params[$1] = $3;
});
return params[param];
};
// https://github.com/feathersjs/feathers/issues/892
let isInitialised = false;
export default async ({ store, redirect, route, req }) => {
if (store.state.auth.payload && isInitialised) {
return;
}
isInitialised = true;
// Get token from lock login
if (process.client) {
const hashToken = getHashParam('id_token');
if (hashToken) {
cookieStorage.setItem('feathers-jwt', hashToken);
}
}
let cookieToken;
if (process.client) {
cookieToken = cookieStorage.getItem('feathers-jwt');
}
if (process.server && req.headers.cookie) {
cookieToken = parseCookies(req.headers.cookie)['feathers-jwt'];
}
if (cookieToken) {
try {
const { accessToken } = await store.dispatch('auth/authenticate', {
strategy: 'jwt',
accessToken: cookieToken,
});
const { payload } = jwt.decode(accessToken, { complete: true });
store.commit('users/setCurrent', payload.userId);
store.commit('auth/setAccessToken', accessToken);
store.commit('auth/setPayload', payload);
store.commit('auth/setUser', store.getters['users/current']);
} catch (error) {
if (process.client) {
cookieStorage.removeItem('feathers-jwt');
}
return redirect(`/login?error=${error.name}`);
}
}
// If it's a private page and there's no payload, redirect.
if (!store.state.auth.publicPages.includes(route.name) && !store.state.auth.payload) {
return redirect('/login?error=NotAuthenticated');
}
};
import { CookieStorage } from 'cookie-storage';
import feathersVuex, { initAuth } from 'feathers-vuex';
import Vuex from 'vuex';
import feathersClient from './feathers-client';
let storage;
// Use dummy storage for SSR
if (process.server) {
const store = {};
storage = {
getItem(key) {
return store[key];
},
setItem(key, value) {
store[key] = value;
},
removeItem(key) {
delete store[key];
},
};
} else {
storage = new CookieStorage();
}
const { service, auth } = feathersVuex(feathersClient(storage), { idField: '_id' });
const createStore = () => {
return new Vuex.Store({
state: {
counter: 0,
},
mutations: {
increment(state) {
state.counter++;
},
},
actions: {
nuxtServerInit({ commit, dispatch }, { req }) {
return initAuth({
commit,
dispatch,
req,
moduleName: 'auth',
cookieName: 'feathers-jwt',
});
},
},
plugins: [
service('messages'),
service('users'),
auth({
state: {
publicPages: [
'index',
'login',
],
},
}),
],
});
};
export default createStore;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment