I am trying to implement Firebase authentication with server-side sessions using koa, koa-session, koa-redis.
I just can't grasp it. When reading the koa-session readme, this is particularly cryptic to me (link):
You can store the session content in external stores (Redis, MongoDB or other DBs) by passing options.store with three methods (these need to be async functions):
get(key, maxAge, { rolling }): get session object by key
set(key, sess, maxAge, { rolling, changed }): set session object for key, with a maxAge (in ms)
destroy(key): destroy session for key
After asking around, I did this:
// middleware/installSession.js
const session = require('koa-session');
const RedisStore = require('koa-redis');
const ONE_DAY = 1000 * 60 * 60 * 24;
module.exports = function installSession(app) {
app.keys = [process.env.SECRET];
app.use(session({
store: new RedisStore({
url: process.env.REDIS_URL,
key: process.env.REDIS_STORE_KEY,
async get(key) {
const res = await redis.get(key);
if (!res) return null;
return JSON.parse(res);
},
async set(key, value, maxAge) {
maxAge = typeof maxAge === 'number' ? maxAge : ONE_DAY;
value = JSON.stringify(value);
await redis.set(key, value, 'PX', maxAge);
},
async destroy(key) {
await redis.del(key);
},
})
}, app));
};
Then in my main server.js file:
// server.js
...
const middleware = require('./middleware');
const app = new Koa();
const server = http.createServer(app.callback());
// session middleware
middleware.installSession(app);
// other middleware, which also get app as a parameter
middleware.installFirebaseAuth(app);
...
const PORT = parseInt(process.env.PORT, 10) || 3000;
server.listen(PORT);
console.log(`Listening on port ${PORT}`);
But then how do I access the session and its methods from inside other middlewares? Like in the installFirebaseAuth middleware, I want to finally get/set session values:
// installFirebaseAuth.js
...
module.exports = function installFirebaseAuth(app) {
...
const verifyAccessToken = async (ctx, next) => {
...
// trying to access the session, none work
console.log('ctx.session', ctx.session);
console.log('ctx.session.get():'
ctx.session.get(process.env.REDIS_STORE_KEY));
console.log('ctx.req.session', ctx.req.session);
const redisValue = await ctx.req.session.get(process.env.REDIS_STORE_KEY);
...
}
}
ctx.session returns {}
ctx.session.get() returns ctx.session.get is not a function
ctx.req.session returns undefined
Any clues?
Thanks!!
It works in my case, hope it helps you
const Koa = require('koa')
const app = new Koa()
const Router = require('koa-router')
const router = new Router()
const static = require('koa-static')
const session = require('koa-session')
// const ioredis = require('ioredis')
// const redisStore = new ioredis()
const redisStore = require('koa-redis')
const bodyparser = require('koa-bodyparser')
app.use(static('.'))
app.use(bodyparser())
app.keys = ['ohkeydoekey']
app.use(session({
key: 'yokiijay:sess',
maxAge: 1000*20,
store: redisStore()
}, app))
app.use(router.routes(), router.allowedMethods())
router.post('/login', async ctx=>{
const {username} = ctx.request.body
if(username == 'yokiijay'){
ctx.session.user = username
const count = ctx.session.count || 0
ctx.session.code = count
ctx.body = `wellcome ${username} logged in`
}else {
ctx.body = `sorry, you can't login`
}
})
router.get('/iflogin', async ctx=>{
if(ctx.session.user){
ctx.body = ctx.session
}else {
ctx.body = 'you need login'
}
})
app.listen(3000, ()=>{
console.log( 'app running' )
})
Related
I want to save a token in the redis cache after user sign-in in my app.
The cache config.service.ts file:
#Injectable()
export class CacheConfigService {
constructor(private configService: ConfigService) {}
get url(): string {
console.log(this.configService.get<string>('redis.url'));
return this.configService.get<string>('redis.url') as string;
}
}
The cacheModule file:
#Module({
imports: [
CacheModule.register<RedisClientOptions>({
// #ts-ignore
store: createRedisStore,
socket: {
host: 'localhost',
port: 6379,
},
isGlobal: true,
// url: 'redis://' + process.env.REDIS_HOST + ':' + process.env.REDIS_PORT,
}),
],
})
The user signin method:
emailPasswordSignInPOST: async (input: any) => {
if (
originalImplementation.emailPasswordSignInPOST === undefined
) {
throw Error('Should never come here');
}
let response: any =
await originalImplementation.emailPasswordSignInPOST(input);
const formFields: any = input.formFields;
const inputObject: any = {};
for (let index = 0; index < formFields.length; index++) {
const element = formFields[index];
inputObject[element.id] = element.value;
}
const { email } = inputObject;
const user = await this.userService.findOneByEmail(email);
const id = user?.id;
const payload = { email, id };
const secret = 'mysecret';
const token = jwt.sign(payload, secret, {
expiresIn: '2h',
});
response.token = token;
const cacheKey = 'Token';
await this.redisClient.set(cacheKey, token, {
EX: 60 * 60 * 24,
});
return response;
},
Please note that this is a different module.
When I send signin request from postman than it logs this error in the console: "Error: The client is closed"
I ran the app with the command DEBUG=* npm run start:dev to see the logs about redis but there is no log about redis.
I have built a simple koa framework application.After adding routes am trying to hit the /health GET route. It throws the below error:
TypeError: next is not a function
at cookieParser (c:\Userxxs\x\gimt\dp-my-app\node_modules\cookie-parser\index.js:46:14)
at dispatch (c:\Users\B748806a\gimt\dp-my-app\node_modules\koa-compose\index.js:42:32)
at bodyParser (c:\Users\B748806a\gimt\dp-my-app\node_modules\koa-bodyparser\index.js:95:11)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
Below are the files and their order of execution:
server.js
const app = require("./app.js");
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Listening on port: ${PORT}`));
app.js
"use strict";
const koa = require('koa');
const koaRouter = require('koa-router');
const app = new koa();
const router = new koaRouter();
const middleware = require("./src/main/middlewares/middlewares");
const routes = require("./src/main/middlewares/route-path");
const init = async () => {
try {
/**
* Step 2: load endpoint routes for the application
*/
routes(router)
} catch (err) {
logger.error({
err
});
}
};
/**
* Step 1: load middleware setup - cors,helmet from KP Spring cloud service
*/
middleware(app);
init();
module.exports = app
middleware.js
const koa = require("koa");
const koaRouter = require('koa-router');
const router = new koaRouter();
const cors = require("koa-cors");
const compression = require("koa-compress");
const helmet = require("koa-helmet");
const cookieParser = require("cookie-parser");
const bodyParser = require('koa-bodyparser')
const ActuatorRouter = require('pm-js-actuator').koa //internal library
const ACTUATOR_OPTIONS = {
health: {
enabled: true
},
livenessprobe: {
enabled: true
},
env: {
enabled: false
},
info: {
enabled: true,
secure: false
}
}
function middleware(app) {
// Use the CORS for the time being.
app.use(cors())
// Let's don the security helmet
app.use(helmet())
app.use(helmet.frameguard())
app.use(helmet.ieNoOpen())
app.use(helmet.frameguard({
action: 'sameorigin'
}))
app.use(helmet.noSniff())
app.use(helmet.referrerPolicy({
policy: 'same-origin'
}))
app.use(helmet.xssFilter())
//app.disable('x-powered-by')
app.use(ActuatorRouter.getRouter(ACTUATOR_OPTIONS).routes())
app.use(bodyParser())
app.use(cookieParser());
// Set up compression
app.use(compression());
}
module.exports = middleware;
route-path.js
const RootHeathController = require("../controller/root-health-controller")
const routes = (router) => {
router.get("/health", RootHeathController.handler)
};
module.exports = routes
root-health-controller.js
const handler = async (ctx, next) => {
ctx.body="Hi";
}
module.exports = {
handler
};
The application is started successfully on port 3000. But when i hit, /health from postman, it throws the mentioned error. Any solution?
The problem here is, that cookie-parser seems to be an express - thing (see also repo url: https://github.com/expressjs/cookie-parser). So to test this I created a minimal version of your code:
const koa = require('koa');
const koaRouter = require('koa-router');
const bodyParser = require('koa-bodyparser');
const cookieParser = require("cookie-parser");
const app = new koa();
app.use(bodyParser());
app.use(cookieParser()); // <-- comment this line
const router = new koaRouter();
router.get("/health", async (ctx, next) => {
ctx.body = 'hi';
});
app.use(router.routes());
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Listening on port: ${PORT}`));
Calling the localist:3000/health endpoint throws the same error. But if you comment the app.use(cookie-parser()) line all works fine.
The question is, why you would need this library? You should be able to set and get cookies in koa with ctx.cookies.get and ctx.cookies.set
I am also trying different network connections it returns the same error. Please help I am stuck last 15 days in this error. Oh! last one thing my laptop IP is dynamically changing so what can I do know.
this is my mongoose connecter
const mongoose = require('mongoose')
const connection = () =>{
const str = 'mongodb://localhost:27017/524'
mongoose.connect(str , () =>{
console.log("Connection is successfull")
} )
}
module.exports = {connection }
this is server js
const koa = require('koa')
const cors = require('koa-cors')
const bodyParser = require('koa-parser')
const json = require('koa-json')
const {connection} = require('./dal')
const userRouter = require('./Router/userRouter')
const app = new koa()
const PORT = 8000
app.use(cors())
app.use(bodyParser())
app.use(json())
app.use(userRouter.routes()).use(userRouter.allowedMethods())
app.listen(PORT, ()=>{
console.log(`Server is running on port ${PORT}`)
connection();
})
this is modle class
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const UserSchema = new Schema ({
name:{
type:String,
required:true
},
email:{
type:String,
required:true,
unique:true
},
password:{
type:String,
required:true
},
role:{
type: Number,
default: 0
}
},{
timestamps:true
})
const User = mongoose.model('User', UserSchema)
module.exports = User
this is login route
const KoaRouter = require('koa-router')
const { register, login ,getAll } = require('../API/userAPI')
const userRouter = new KoaRouter({prefix: '/user'})
userRouter.post('/register', register)
userRouter.post('/login', login)
userRouter.get ('/get' , getAll)
module.exports = userRouter;
this is my contraller
const UserModel = require('../models/user.model')
const bcrypt = require('bcrypt')
const register = async (ctx) => {
try{
const user = ctx.request.body
const {name, email, password, role} = user
if (!name || !email || !password){
return (ctx.body = { message: "fill" });
}
else{
const exist = await UserModel.findOne({email})
if(exist){
return (ctx.body = { message: "User already exists" });
}else{
const salt = await bcrypt.genSalt();
const hashpassword = await bcrypt.hash(password, salt)
const newUser = new UserModel({
name,
email,
password : hashpassword,
role
})
await newUser.save()
return (ctx.body = { message: "User Registered" });
}
}
}catch(err){
console.log(err.message)
return (ctx.body = { message: err.message });
}
}
const login = async (ctx) => {
try{
const {email, password} = ctx.request.body
const user = await UserModel.findOne({email})
if (!user){
return ( ctx.body = {message:"User does not exist"})
}
else {
const isMatch = await bcrypt.compare(password, user.password)
if (!isMatch) {
return (ctx.body = { message:"Wrong Password"})
}else{
return (ctx.body = { user})
}
}
}catch(err){
return (ctx.body= {err})
}
}
const getAll = async (ctx) => {
try{
const users = await UserModel.find()
return (ctx.body = users)
}catch(err){
return (ctx.body= {err})
}
}
module.exports = {register, login ,getAll}
.
how to fix this.any ideas.Can any body guide me with this scenario.
I am attempting to call the increment in the counter contract here, which is deployed to my account on testnet, using the following script:
const nearAPI = require("near-api-js");
require("dotenv").config();
const {parseSeedPhrase} = require("near-seed-phrase");
async function call() {
const mneumonic = process.env.nearmneumonic0?.trim() || "";
const ACCOUNT_ID = process.env.nearacct0?.trim() || "";
const keyStores = nearAPI.keyStores;
const keyPair = nearAPI.KeyPair;
const connect = nearAPI.connect;
const keyStore = new keyStores.InMemoryKeyStore();
const PRIVATE_KEY = parseSeedPhrase(mneumonic);
const keyPair_ = keyPair.fromString(PRIVATE_KEY.secretKey);
await keyStore.setKey("testnet", ACCOUNT_ID, keyPair_);
const config = {
keyStore,
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
};
const near = await connect(config);
const account = await near.account(ACCOUNT_ID);
const contract = new nearAPI.Contract(
account,
ACCOUNT_ID,
{
changeMethods: ["increment", "decrement", "reset"],
viewMethods: ["get_num"],
sender: account,
}
);
let response = await contract.increment(
{
args: {
//arg_name: "value"
},
gas: 300000000000000
}
);
console.log(response);
}
call();
However, the response thrown is al followed:
Failure [acct.testnet]: Error: Contract method is not found
...
ServerTransactionError: Contract method is not found
I have looked through some of the docs, which mention to add changeMethod/viewMethod, however it seems there is still errors thrown.
Any help much appreciated!
I want to store some data other than userId or accessToken to store in a session, in after save or before save operation hook in Loopback application using express-session.
I have this in my server/server.js :
....
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
....
app.use(session({
name:'session-name',
secret: 'keyboard cat',
store: new MongoStore({url: 'mongodb://localhost/test', ttl:1}),
resave: false,
saveUninitialized: true
}));
And as I'm defining the remote-method with some parameters it actually passing the parameter and not the req object, so I can't do it the express way.
How can I use the session to store and get value?
EDIT :
I have found a way to set the session in remote method, by adding this to my model.json's remote-method :
"accepts": [
{
"arg": "req",
"type": "object",
"http": {
"source": "req"
}
}
]
And, adding the req parameter to the remote-method function,
Model.remoteMethod = function (req, callback) {
req.session.data = { 'foo': 'bar' }
callback(null)
};
Now, the issue is I want to get this session value in operation hook
Model.observe('before save', function (ctx, next) {
//How to get the session here?
})
try this now :
you can set ctx value :
var LoopBackContext = require('loopback-context');
MyModel.myMethod = function(cb) {
var ctx = LoopBackContext.getCurrentContext();
// Get the current access token
var accessToken = ctx && ctx.get('accessToken');
ctx.set('xx', { x: 'xxxx' } );
}
it's get ctx value :
module.exports = function(MyModel) {
MyModel.observe('access', function(ctx, next) {
const token = ctx.options && ctx.options.accessToken;
const userId = token && token.userId;
const modelName = ctx.Model.modelName;
const scope = ctx.where ? JSON.stringify(ctx.where) : '<all records>';
console.log('%s: %s accessed %s:%s', new Date(), user, modelName, scope);
next();
});
};
loopback context store userId and accesTokan. in whole web you can access using ctx it's work like session in loopback.