NodeJS + Google Login + Firebase Functions results in decoding firebase session cookie failed - node.js

I require a Google Sign-In for a Firebase application, to get this done I used multiple sources:
auth-sessions provides a good out of the box example with NodeJS + Firebase + Google Login.
manage firebase session provides a sample NodeJS functions (not entire working solution) to work with Firebase Functions.
The problem:
When attempting to sign in into a Google account on production, the following error occurs when executing the code authenticate function below:
admin
.auth()
.verifySessionCookie(sessionCookie, true /** checkRevoked */)
.then((decodedClaims) => {
log("Decode success");
// inbetween checks
log("Successfully decoded and authenticated");
next();
})
.catch((error) => {
log("Error authenticating"); < ---- THIS IS THE PROBLEM
...
});
This error ONLY occurs when on production i.e. deployed to Firebase. When testing locally using firebase emulators emulating hosting and functions only (auth, firestore, database, etc are all production), login is successful. When deploying, the login fails with the error below.
Error:
Decoding Firebase session cookie failed. Make sure you passed the entire string JWT which represents a session cookie. See https://firebase.google.com/docs/auth/admin/manage-cookies for details on how to retrieve a session cookie.
More Details:
Below is a high-level overview of steps/actions performed
Steps overview of actions performed
1. Visit any page e.g. /login
2. Click sign in with Google, execute the popup provider (see [here][3])
2.
1. Sign in with Google account
2. Send token to firebase functions for verification i.e. `POST /sessionLogin`
3. Receive response (assume 200 OK)
4. Redirect to authenticated URL
The error is on the last step i.e. 4
This error occurs after successfully creating a session using the sample /sessionLogin code found here on firebase's site:
const auth = admin.auth();
auth.verifyIdToken(idToken).then(value => {
debug("Token verified")
return auth.createSessionCookie(idToken, {expiresIn})
.then((sessionCookie) => {
// Set cookie policy for session cookie.
const options = {maxAge: expiresIn, httpOnly: true, secure: true};
res.cookie('session', sessionCookie, options);
// res.json(JSON.stringify({status: 'success'}));
res.status(200).send("OK");
}).catch((error) => {
debug(error);
res.status(401).send('UNAUTHORIZED REQUEST!');
});
}).catch(reason => {
debug("Unable to verify token");
debug(reason);
res.status(401).send('INVALID TOKEN!');
});
The logs respond with a Token verified and a status 200 is sent to the client.
The client then redirects to an authenticated URL /user/dashboard which performs the authenticate check (see below) which fails and redirects back to /login:
const authenticate = (req, res, next) => {
log("Authenticating");
// source: https://firebase.google.com/docs/auth/admin/manage-cookies#verify_session_cookie_and_check_permissions
const sessionCookie = req.cookies.session || '';
// Verify the session cookie. In this case an additional check is added to detect
// if the user's Firebase session was revoked, user deleted/disabled, etc.
return admin
.auth()
.verifySessionCookie(sessionCookie, true /** checkRevoked */)
.then((decodedClaims) => {
log("Decode success");
// inbetween checks
log("Successfully decoded and authenticated");
next();
})
.catch((error) => {
log("Error authenticating");
if(error.errorInfo && error.errorInfo.code && error.errorInfo.code === "auth/argument-error") {
debug(error.errorInfo.message);
res.redirect('/user/login');
return;
}
debug(error);
// Session cookie is unavailable or invalid. Force user to login.
req.flash("message", [{
status: false,
message: "Invalid session, please login again!"
}])
res.redirect('/user/login');
});
};
which is middleware for the express app:
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://my-company-default-rtdb.firebaseio.com",
storageBucket: "gs://my-company.appspot.com"
});
const app = express();
app.use(cors({origin: true}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(morgan('dev'));
app.use(cookieParser('0000-0000-0000-0000-0000'))
app.set('trust proxy', 1) // trust first proxy
// Attach CSRF token on each request.
app.use(attachCsrfToken('/', 'csrfToken', (Math.random()* 100000000000000000).toString()));
app.use(session({
secret: '0000-0000-0000-0000-0000',
resave: false,
name: '__session',
store: new FirebaseStore({
database: admin.database()
}),
}));
app.use(flash());
app.use(authenticate);
// routes
exports.app = functions.https.onRequest(app);
Execution Logs:
1:12:02.796 PM app Function execution started
1:12:02.910 PM app Authenticating
1:12:02.910 PM app Attempting to verify session cooking
1:12:02.910 PM app Cookies: {}
1:12:02.911 PM app Error authenticating
1:47:41.905 PM app auth/argument-error
1:12:02.911 PM [app] Decoding Firebase session cookie failed. Make sure you passed the entire string JWT which represents a session
cookie. See https://firebase.google.com/docs/auth/admin/manage-cookies
for details on how to retrieve a session cookie.
1:12:02.937 PM [app] Function execution took 141 ms, finished with status code: 302
Update
Call to backend for auth:
const postIdTokenToSessionLogin = (idToken, csrfToken) => {
return axios({
url: "/user/sessionLogin",
method: "POST",
data: {
idToken: idToken,
csrfToken: csrfToken,
},
}).then(value => {
console.log(value);
if(value.status === 200) {
window.location.assign("/user/dashboard");
}
}).catch(reason => {
console.error(reason);
alert("Failed to login");
});
}
Client side call:
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth()
.signInWithPopup(provider)
.then(async value => {
firebase.auth().currentUser.getIdToken().then(idToken => {
// const idToken = value.credential.idToken;
const csrfToken = getCookie('_csrf');
return postIdTokenToSessionLogin(idToken, csrfToken);
}).catch(reason => {
console.error("Failed to get current user token");
alert(reason.message);
});
})/*.then(value => {
window.location.assign("/user/dashboard")
})*/.catch((error) => {
console.error("Failed to sign in with Google");
alert(error.message);
});
Update 2:
Updated client-side axios request with the following, also added extra req.cookies logging
return axios({
url: "/user/sessionLogin",
method: "POST",
withCredentials: true,
data: {
idToken: idToken,
csrfToken: csrfToken,
},
})
Extra logging:
4:43:23.493 PM app Function execution started
4:43:23.501 PM app Authenticating
4:43:23.501 PM app Creating session
4:43:23.502 PM app /sessionLogin Cookies: {"csrfToken":"19888568527706150","session":"eyJhbGciOiJSUzI1NiIsImtpZCI6InRCME0yQSJ9.eyJpc3MiOiJodHRwczovL3Nlc3Npb24uZmlyZWJ..."}
4:43:23.503 PM app Token verified
4:43:23.503 PM app {"name":redacted ,"picture":"","iss":"","aud":"",...}
4:43:23.503 PM app ==============
4:43:23.503 PM app /sessionLogin#verifyIdToken Cookies: {"csrfToken":"19888568527706150","session":"eyJhbGciOiJSUzI1NiIsImtpZCI6InRCME0yQSJ9.eyJpc3MiOiJodHRwczovL3Nlc3Npb24uZmlyZWJ..."}
4:43:23.634 PM app /sessionLogin#createSessionCookie Cookies: {"csrfToken":"19888568527706150","session":"eyJhbGciOiJSUzI1NiIsImtpZCI6InRCME0yQSJ9.eyJpc3MiOiJodHRwczovL3Nlc3N..."}
4:43:23.634 PM app Cookie:
4:43:23.634 PM app "eyJhbGciOiJSUzI1NiIsImtpZCI6InRCME0yQSJ9.eyJpc3MiOiJodHRwczovL3Nlc3Npb24uZmlyZWJhc2UuZ29vZ..."
4:43:23.634 PM app ==============
4:43:23.643 PM app [0mPOST /user/sessionLogin [32m200[0m 139.036 ms - 2[0m
4:43:23.643 PM app Function execution took 150 ms, finished with status code: 200
4:43:24.131 PM app Function execution started
4:43:24.153 PM app Authenticating
4:43:24.153 PM app Attempting to verify session cooking
4:43:24.153 PM app Cookies: {}
Update 3
Rewrites enabled API & NodeJS access exactly as shown in firebase.json:
{
"database": {
"rules": "database.rules.json"
},
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"hosting": {
"site": "my-company-admin-portal",
"public": "public",
"rewrites": [
{
"source": "/api/**",
"function": "api"
},
{
"source": "**",
"function": "app"
}
],
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
},
"storage": {
"rules": "storage.rules"
},
"emulators": {
"auth": {
"port": 9099
},
"functions": {
"port": 5001
},
"database": {
"port": 9000
},
"hosting": {
"port": 5000
},
"storage": {
"port": 9199
},
"ui": {
"enabled": true
}
}
}

sessionCookie is undefined as in the code provided in the question.
// Authenticate middleware right now
const authenticate = (req, res, next) => {
log("Authenticating");
// No sessionCookie declared
return admin
.auth()
.verifySessionCookie(sessionCookie, true /** checkRevoked */)
// undefined passed here ^^^
}
You must pass the cookie that you set after using createSessionCookie in verifySessionCookie method as shown below:
// updated authenticate middleware
const authenticate = async (req, res, next) => {
try {
log("Authenticating");
// Read the value of cookie here
const sessionCookie = req.cookies.session
// Return unauthorized error if cookie is absent
if (!sessionCookie) return res.sendStatus(401)
const decodedClaims = await admin.auth().verifySessionCookie(sessionCookie, true)
// cookie verified, continue
} catch (e) {
console.log(e)
return res.send("An error occurred")
}
}

I suggest you to check this post in which they have a similar issue.
In summary, if you check the quickstart-nodejs repository you have to add the cookie-parser and body-parser. Here is the example that they provide in the post:
const admin = require("firebase-admin");
const functions = require("firebase-functions");
const next = require("next");
const cors = require("cors");
const express = require("express");
const cookieParser = require("cookie-parser");
const bodyParser = require("body-parser");
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev, conf: { distDir: "next" } });
const handle = app.getRequestHandler();
admin.initializeApp({
credential: admin.credential.cert("Service account key"),
databaseURL: "Path to database"
});
const server = express();
server.use(cors({ origin: true }));
server.use(bodyParser.json());
// Support URL-encoded bodies.
server.use(bodyParser.urlencoded({
extended: true
}));
// Support cookie manipulation.
server.use(cookieParser());
// Attach CSRF token on each request.
server.use(attachCsrfToken('/', 'csrfToken', (Math.random()* 100000000000000000).toString()));
function attachCsrfToken(url, cookie, value) {
return function(req, res, next) {
if (req.url === url) {
res.cookie(cookie, value);
}
next();
}
}
server.post("/login", (req, res) => {
if (req.body && req.body.idToken) {
const idToken = `${req.body.idToken}`;
const expiresIn = 60 * 60 * 24 * 5 * 1000;
admin.auth().createSessionCookie(idToken, { expiresIn }).then((sessionCookie) => {
const options = { maxAge: expiresIn, httpOnly: true, secure: true };
res.cookie("session", sessionCookie, options);
res.end(JSON.stringify({ sessionCookie }));
}, error => {
res.status(401).send(error);
});
} else {
res.status(401).send("Token empty");
}
});
server.post("/profile", (req, res) => {
if (req.cookies && req.cookies.session) {
const sessionCookie = `${req.cookies.session}`;
admin.auth().verifySessionCookie(
sessionCookie, true /** checkRevoked */).then((decodedClaims) => {
res.end(JSON.stringify({ decodedClaims }));
}).catch(error => {
res.status(401).send(error);
});
} else {
res.status(401).send("Session empty");
}
});
exports.next = functions.https.onRequest((req, res) => {
if (req.method === "POST") {
if (!req.path) req.url = `/${request.url}`;
return server(req, res);
}
return app.prepare().then(() => handle(req, res));
});

Related

Unable to get SHOP name

In the previous version I used to get the current shop name is like this:
router.get("/api/app", async (ctx) => {
let shop = ctx.session.shop;
});
but, in the new version, i can't get the current shop name using ctx.session.shop, i don't see any object on the log named name, and also the session token, i do see session token and shop name on the reffer object, but i think there is another way where i can access those directly.
so, how do i get the current shop name ?
here is my code:
import "#babel/polyfill";
import dotenv from "dotenv";
import "isomorphic-fetch";
import createShopifyAuth, { verifyRequest } from "#shopify/koa-shopify-auth";
import Shopify, { ApiVersion } from "#shopify/shopify-api";
import Koa from "koa";
import session from "koa-session";
import next from "next";
import Router from "koa-router";
import koaBody from "koa-body";
dotenv.config();
const port = parseInt(process.env.PORT, 10) || 8081;
const dev = process.env.NODE_ENV !== "production";
const app = next({
dev,
});
const handle = app.getRequestHandler();
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SCOPES.split(","),
HOST_NAME: process.env.HOST.replace(/https:\/\//, ""),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
// Storing the currently active shops in memory will force them to re-login when your server restarts. You should
// persist this object in your app.
const ACTIVE_SHOPIFY_SHOPS = {};
const server = new Koa();
const router = new Router();
router.get("/api/test", async (ctx) => {
return (ctx.body = ctx.session);
});
app.prepare().then(async () => {
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
session(
{
sameSite: "none",
secure: true,
},
server
)
);
server.use(
createShopifyAuth({
async afterAuth(ctx) {
// Access token and shop available in ctx.state.shopify
const { shop, accessToken, scope } = ctx.state.shopify;
const host = ctx.query.host;
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
const response = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: "/webhooks",
topic: "APP_UNINSTALLED",
webhookHandler: async (topic, shop, body) =>
delete ACTIVE_SHOPIFY_SHOPS[shop],
});
if (!response.success) {
console.log(
`Failed to register APP_UNINSTALLED webhook: ${response.result}`
);
}
// Redirect to app with shop parameter upon auth
ctx.redirect(`/?shop=${shop}&host=${host}`);
},
})
);
const handleRequest = async (ctx) => {
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
router.get("/", async (ctx) => {
const shop = ctx.query.shop;
// This shop hasn't been seen yet, go through OAuth to create a session
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
});
router.post("/webhooks", async (ctx) => {
try {
await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
console.log(`Webhook processed, returned status code 200`);
} catch (error) {
console.log(`Failed to process webhook: ${error}`);
}
});
router.post(
"/graphql",
verifyRequest({ returnHeader: true }),
async (ctx, next) => {
await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
}
);
router.get("(/_next/static/.*)", handleRequest); // Static content is clear
router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
router.get("(.*)", verifyRequest(), handleRequest); // Everything else must have sessions
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
Thanks in advance.
Here's how to do it inside your server.js file:
For your index route i.e router.get("/") set the cookie for shop name that can be fetched as shown below. Once the cookie is set when the shop owners opens the app from the admin, now when you open the app from your redirect link which is set in partners portal usually ngrock then the shop name will be set automatically from the cookie set earlier
Full Code:
router.get("/", async ctx => {
const shop = ctx.query.shop
if (shop) {
console.log("setting cookie");
ctx.cookies.set("shop_name", shop, {
secure: true,
sameSite: 'none',
httpOnly: false
})
}
if (ctx.request.header.cookie) {
var cookies_fetched = parseCookie(ctx.request.header.cookie)
// This shop hasn't been seen yet, go through OAuth to create a session
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
ctx.redirect(`/auth?shop=${cookies_fetched.shop_name}`)
} else {
await handleRequest(ctx)
}
} else {
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
ctx.redirect(`/auth?shop=${shop}`)
} else {
await handleRequest(ctx)
}
}
})

Express Session is not working with my localhost React Native App but work fine with Postmon

The problem is that when i login from postman all routes work correctly and express-session also working but when i login from my expo react native app it show cors error.
i also use axios.defaults.withCredential = true but not work.
Please give me solution if possible
OUTPUT
Postman
here login successfull
Here assignment route also work correctly
React Native App
Here when i make http request from react-native app it show this error
CODE
Expo React Native app:
config.js
import axios from 'axios';
const URL = [
'http://localhost:5000/lms',
'https://lms-assignments-tracker.herokuapp.com/lms',
];
axios.defaults.baseURL = URL[0];
axios.defaults.withCredentials = true;
export default axios;
Redux code work perfectly
userAction.js
import {
GET_ASSIGNMENT_RECORD,
} from './types';
export const getAssignment = () => (dispatch) => {
dispatch({
type: LOADING_ASSIGNMENT,
});
axios
.get('crawler/assignment')
.then((data) => {
dispatch({
type: GET_ASSIGNMENT_RECORD,
payload: data.data,
});
})
.catch((err) => {
if (err.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(err.response.data);
console.log(err.response.status);
console.log(err.response.headers);
} else if (err.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(err.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', err.message);
}
console.log(err.config);
});
};
Screen Component Where i dispatch my action
Home.js
import {getAssignment} from '../redux/action/dataAction';
class Home extends Component {
constructor(props) {
super(props);
this.props.getAssignment();
}
//...
}
/**
* Redux Action
** /
const mapDispatchToProps = (dispatch) => {
return bindActionCreators(
{
logout,
auth,
getAssignment,
},
dispatch,
);
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Node.js Code
index.js
//all imports are ok imports
const app = require('express')();
const session = require('express-session')
const cors = require('cors')
app.use(cors());
app.use(
session({
secret: 'Bukc AK',
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 1000 * 60 * 60, // (1000 -> msec * 60 -> sec * 60 -> min * 24 -> hrs * 1 -> days)
},
}),
)
app.get('/lms/crawler/assignment',auth, (req,res) => {
...
}
login route
this route work perfectly give response { loginSuccess: true, userId: ... }
app.post('/lms/users/login',(req,res) => {
...
user.generateToken((err, user) => {
if (err) return res.status(400).send(err)
//here i set session.w_auth
req.session.w_auth = user.token
res.status(200).json({
loginSuccess: true,
userId: user._id,
})
})
}
here session.w_auth not get value
auth.js
let auth = (req, res, next) => {
let token = req.session.w_auth;
// here token result is undefined
console.log(token)
....
next()
})
}
module.exports = { auth }
Based on the comments, it sounds like you have a problem with preserving or sending the cookie. You can start by changing this:
axios.defaults.withCredential = true
to this:
axios.defaults.withCredentials = true
to use the proper spelling for that property.

res.cookie() not working in Nodejs/Angular

Pls help me figure out where I am going wrong.
Problem: When I login and then refresh, I get logged out.
Expected: Upon logging in, a cookie should be set by Node, which should be sent to the server for every request. Which is not happening.
The app runs an initializer function to try and auto authenticate based on the token present in the cookie.
Angular initializer function
export function appInitializer(accountService: AccountService) {
return () => new Promise(resolve => {
// attempt to refresh token on app start up to auto authenticate
accountService.refreshToken()
.subscribe()
.add(resolve);
});
}
Angular Account Service
#Injectable({ providedIn: 'root' })
export class AccountService {
private accountSubject: BehaviorSubject<Account>;
public account: Observable<Account>;
....
constructor(private httpClient: HttpClient, private router: Router) {
this.accountSubject = new BehaviorSubject<Account>(null);
this.account = this.accountSubject.asObservable();
}
login(userName: string, password: string) {
return this.httpClient.post<any>(`${this.reqUrl}/authenticate`, {userName, password})
.pipe(map(account => {
this.accountSubject.next(account);
this.startRefreshTokenTimer();
return account;
}));
}
refreshToken() {
return this.httpClient.post<any>(`${this.reqUrl}/refresh-token`, {})
.pipe(map(account => {
this.accountSubject.next(account);
this.startRefreshTokenTimer();
return account;
}));
}
private refreshTokenTimeout;
private startRefreshTokenTimer() {
// parse json object from base64 encoded jwt token
const jwtToken = JSON.parse(atob(this.accountValue.jwtToken.split('.')[1]));
// set a timeout to refresh the token a minute before it expires
const expires = new Date(jwtToken.exp * 1000);
const timeout = expires.getTime() - Date.now() - (60 * 1000);
this.refreshTokenTimeout = setTimeout(() => this.refreshToken().subscribe(), timeout)
}
Node Route
router.post('/authenticate', AccountController.authenticateSchema, AccountController.authenticate);
router.post('/refresh-token', AccountController.refreshToken);
Node Acct Controller
exports.authenticate = (req, res, next) => {
const { userName, password } = req.body;
const ipAddress = req.ip;
accountService.authenticate({userName, password, ipAddress})
.then(({refreshToken, ...account}) => {
setTokenCookie(res, refreshToken); // <<<<<<<<<<<<<<<<<<<<<<<<<
res.json(account);
})
.catch(next);
}
exports.refreshToken = (req, res, next) => {
const token = req.cookies.refreshToken;
if(!token) {
return res.status(400).json({ message: 'acct ctlr rTkn not found'}); // <<< even after Login and refresh I reach here
}
const ipAddress = req.ip;
accountService.refreshToken({token, ipAddress})
.then(({refreshToken, ...account}) => {
setTokenCookie(res, refreshToken);
res.json(account);
})
.catch(next); // ?
}
function setTokenCookie(res, token) {
// create cookie with refresh token that expires in 1 day
const cookieOptions = {
httpOnly: true,
expires: new Date(Date.now() + 1*24*60*60*1000)
};
res.cookie('refreshToken', token, cookieOptions); ///?
}
I get this error in dev tool upon starting the appand also when i refresh after logging in.
Try adding:
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', true);
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

nuxtjs apollo-client does not set authorization header

I am trying to create a login functionality using nuxtjs with the nuxtjs apollo-module and nodejs in the backend using apollo-server. I would like to pass the token from the frontend (nuxtjs/apollo-client) to the backend (nodejs/apollo-server).
Signin Function (frontend)
async signin () {
const email = this.email
const password = this.password
try {
const res = await this.$apollo.mutate({
mutation: signIn,
variables: {
email,
password
}
}).then(({ data }) => data && data.signIn)
const token = res.token
await this.$apolloHelpers.onLogin(token)
this.$router.push('/feed')
} catch (err) {
// Error message
}
}
nuxtjs.config (frontend)
apollo: {
clientConfigs: {
default: {
httpEndpoint: 'http://localhost:8000/graphql',
wsEndpoint: 'ws://localhost:8000/graphql',
authenticationType: 'Bearer',
httpLinkOptions: {
credentials: 'include'
},
}
}
Cookie in Browser DevTools
Index File (backend)
const app = express()
const corsConfig = {
origin: 'http://127.0.0.1:3000',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS',
credentials: true
}
app.use(cors(corsConfig))
app.use(morgan('dev'))
const getMe = async req => {
const token = req.headers.authorization // <========
console.log(token) // returns 'Bearer undefined'
if (token) {
try {
return await jwt.verify(token, process.env.SECRET)
} catch (e) {
// Error message
}
}
}
const server = new ApolloServer({
introspection: true,
playground: true,
typeDefs: schema,
resolvers,
context: async ({ req }) => {
if (req) {
const me = await getMe(req)
return {
models,
me,
secret: process.env.SECRET,
loaders: {
user: new DataLoader(keys =>
loaders.user.batchUsers(keys, models),
),
},
}
}
},
})
server.applyMiddleware({
app,
path: '/graphql',
cors: false
})
const httpServer = http.createServer(app)
server.installSubscriptionHandlers(httpServer)
const port = process.env.PORT || 8000
sequelize.sync({ force: true }).then(async () => {
createUsers(new Date())
httpServer.listen({ port }, () => {
console.log(`Apollo Server on http://localhost:${port}/graphql`)
})
})
The token is saved in a cookie called 'apollo-token'. However the Authoriation header in the format 'Bearer token' is not set. According to the apollo-client documentation this should be set automatically (https://github.com/nuxt-community/apollo-module#authenticationtype-string-optional-default-bearer).
What am I missing? I would be very thankful for any kind of help!

Authentication to Featherjs using socket.io-client

How can I authenticate to Feathersjs (https://docs.feathersjs.com/api/client/socketio.html#authentication) using Direct Connection (https://docs.feathersjs.com/api/client/socketio.html#direct-connection)? The following code says that my accessToken is malformed, but I suspect there's more than that to get it working. Where do I fetch an accessToken?
app.js (client):
import express from 'express';
const socket = require('socket.io-client')('http://localhost:3030', {
transports: ['websocket']
});
socket.emit('authenticate', {
strategy: 'jwt',
accessToken: 'what to enter here'
}, (message: any, data: any) => {
console.log(message);
console.log(data);
});
const app = express();
app.get('/', (req, res) => res.send('Up and running!'));
app.listen(4390, () => console.log('Example app listening on port 4390!'));
authentication.js (feathers server)
const authentication = require('#feathersjs/authentication');
const jwt = require('#feathersjs/authentication-jwt');
const local = require('#feathersjs/authentication-local');
module.exports = function (app) {
const config = app.get('authentication');
// Set up authentication with the secret
app.configure(authentication(config));
app.configure(jwt());
app.configure(local());
app.service('authentication').hooks({
before: {
create: [
authentication.hooks.authenticate(config.strategies),
],
remove: [
authentication.hooks.authenticate('jwt')
]
}
});
};
I tried using the secret as an accessToken but it didnt work :)
default.json (feathers server config)
"authentication": {
"secret": "r323r32rada86700f18d82ea1d3e74fb58141dbefcd7460ef71736759265e347151a12d68dff50aa59c05e2f18db88661be8ae91a3f12932fddea8891b5ca9f63b5e4fc650edabc59d0d14e8fe4ea54256e7e386845321ab58a320a9ec99438bd058a3fbbda65dadf97bc9585ea82f72201f932beqwdqwdqwd5761a0d0be3e95474db5b9c8a3f4c7303beed0344a1768ba5dad6a1c916d183ea5dd923587768661ff0b08f25ed85dff4ff4e6b58327fe5914e5e7fb2356ee67754b102434f22686444a35fc38c75bcdd6386240a22e0cf62bdc7f227200868da387174b365af2afa7dec378c4ccf22956b134a3ec961fd1ba8d3dc85a7594ab711",
"strategies": [
"jwt",
"local"
],
"path": "/authentication",
"service": "users",
"jwt": {
"header": {
"typ": "access"
},
"audience": "https://yourdomain.com",
"subject": "anonymous",
"issuer": "feathers",
"algorithm": "HS256",
"expiresIn": "1d"
},
"local": {
"entity": "user",
"usernameField": "email",
"passwordField": "password"
}
},
...
Thankful for all replies!
In order to get an accessToken you will typically need to authenticate with a strategy using email/password or oauth. This will return an accessToken which you can then use for jwt authentication.
An alternative approach would be to use a custom authentication strategy which would allow you to have a shared secret that both servers could use to communicate with each other.
Thank you #mchaffe! I managed to solve it with your help. Here is the code used:
import dotenv from 'dotenv';
// Load environments
const config = dotenv.config()
if (config.error) throw config.error
const io = require('socket.io-client');
const feathers = require('#feathersjs/client');
const localStorage = require('localstorage-memory');
const client = feathers();
const socket = io('http://localhost:3030/', {
transports: ['websocket'],
forceNew: true
});
client.configure(feathers.socketio(socket), {
timeout: 10000
});
client.configure(feathers.authentication({
jwtStrategy: 'jwt',
storage: localStorage,
storageKey: 'some-token'
}));
const payload = {
strategy: 'local',
email: process.env.FEATHERS_AUTHENTICATION_EMAIL,
password: process.env.FEATHERS_AUTHENTICATION_PASSWORD
};
client.authenticate(payload).then((response: any) => {
// Do stuff to hooray here
console.log('Access Token: ' + response.accessToken);
// Works!
socket.emit('get', 'logger', 1, (error: any, log: any) => {
console.log('Found log: ' + JSON.stringify(log));
});
}).catch((e: any) => {
console.log('Error: ' + e);
});
I am all ears if you have suggestion on improvements! :) It seems I can access data from the database using the socket.emit method. Do I need to verify the accessToken returned? Thanks again!

Resources