json-server: Default JSON for non-matching route - json-server

I want to fake a login API with the route /api/login/:userName/:password that would return { "success": false, "userName": "", "password": "" } in case userName or password is not found in the database. How can I achieve this with json-server?
So far I have this database JSON:
{
"login": [
{ "success": true, "userName": "zaphod", "password": "galaxy" }
],
"invalidLogin": [
{ "success": false, "userName": "", "password": "" }
]
}
invalidLogin is currently not used and should be used for every login route with unknown userName or password.
This is my server.js:
const jsonServer = require('json-server');
const server = jsonServer.create();
const router = jsonServer.router('db.json');
const middlewares = jsonServer.defaults();
server.use(middlewares);
// Rewrite rules
server.use(jsonServer.rewriter({
'/login/:userName/:password': "/login?userName=:userName&password=:password"
}))
server.use('/api', router);
server.listen(3001, () => {
console.log('JSON Server is running')
});

You can achieve something like this using router.render function:
router.render = (req, res, next) => {
if (req.path === '/login') {
if (res.locals.data.length !== 0) {
res.jsonp(res.locals.data);
} else {
res.jsonp([
{ "success": false, "userName": "", "password": "" }
])
}
} else {
res.jsonp(res.locals.data);
}
}
Basically this router code will check if there are non-empty response to /login/.../... and afterwards replace it with error JSON on empty login request. You can upgrade this mock's logic further.
Aforementioned code can be placed after rewrite rules in server.js (there was a minor error in your code, you have to write it with api, reference issue):
server.use('/api', jsonServer.rewriter({
"/login/:userName/:password": "/login?userName=:userName&password=:password",
}))

Related

node-oidc-provider 7.12.0 (JWT, authorization_code) invalid_token error on UserInfo endpoint (/me)

i have some issues getting the UserInfo endpoint working using JWT AccessTokens, it works fine with default settings when commenting out the resourceIndicators section.
I can get the access token using PostMan without issues, but when posting on the UserInfo (/me) endpoint the Bearer AccessToken, i got an invalid_token error.
here is my code:
const {Provider} = require('oidc-provider');
let hostname = process.env.HOSTNAME;
if (hostname === undefined) {
hostname = "http://localhost"
}
const port = process.env.PORT || 3000;
if (port !== 80 && port !== 443) {
hostname = hostname + ':' + port
}
const users = [
{
"id": "user1",
"email": "user1#example.com",
"authentication_method_reference": "mfa"
}
]
const clients = [
{
"client_id": "client-1",
"client_secret": "client-1-secret",
"redirect_uris": [
"http://localhost:3000"
]
}
]
async function findAccount (ctx, id) {
// This would ideally be just a check whether the account is still in your storage
let account = users.find(user => {
return user.id === id;
})
if (!account) {
return undefined;
}
return {
accountId: id,
async claims() {
return {
sub: id,
email: account.email,
amr: [account.authentication_method_reference]
};
},
};
}
const configuration = {
clients: clients,
conformIdTokenClaims: false,
features: {
devInteractions: {
enabled: true
},
resourceIndicators: {
defaultResource: (ctx, client, oneOf) => {
return hostname;
},
enabled: true,
getResourceServerInfo: (ctx, resourceIndicator, client) => {
console.log('get resource server info', client);
return ({
audience: resourceIndicator,
scope: 'openid',
accessTokenTTL: 2 * 60 * 60,
accessTokenFormat: 'jwt',
});
},
useGrantedResource: (ctx, model) => { return true; }
}
},
claims: {
openid: [
'sub',
'email',
'amr'
]
},
cookies: {
keys: 'super,secret'.split(',')
},
pkce: {
required: () => false
},
// Used to skip the 'approval' page
async loadExistingGrant(ctx) {
const grantId = (ctx.oidc.result
&& ctx.oidc.result.consent
&& ctx.oidc.result.consent.grantId) || ctx.oidc.session.grantIdFor(ctx.oidc.client.clientId);
if (grantId) {
// keep grant expiry aligned with session expiry
// to prevent consent prompt being requested when grant expires
const grant = await ctx.oidc.provider.Grant.find(grantId);
// this aligns the Grant ttl with that of the current session
// if the same Grant is used for multiple sessions, or is set
// to never expire, you probably do not want this in your code
if (ctx.oidc.account && grant.exp < ctx.oidc.session.exp) {
grant.exp = ctx.oidc.session.exp;
await grant.save();
}
return grant;
} else {
const grant = new ctx.oidc.provider.Grant({
clientId: ctx.oidc.client.clientId,
accountId: ctx.oidc.session.accountId,
});
grant.addOIDCScope('openid');
grant.addResourceScope(hostname, 'openid');
await grant.save();
return grant;
}
},
extraTokenClaims: async (ctx, token) => {
return findAccount(ctx, token.accountId).then(account => {
return account.claims()
})
},
findAccount: findAccount
};
const oidc = new Provider(hostname, configuration);
function handleServerError(ctx, err) {
console.log(err);
}
function handleGrantErrors({headers: {authorization}, oidc: {body, client}}, err) {
console.log(err);
}
function handleAccessToken(token) {
console.log(token);
}
oidc.on('grant.error', handleGrantErrors);
oidc.on('introspection.error', handleGrantErrors);
oidc.on('revocation.error', handleGrantErrors);
oidc.on('server_error', handleServerError);
oidc.on('access_token.issued', handleAccessToken);
oidc.listen(port, () => {
console.log(`oidc-provider listening on port ${port}.`)
})
I tried different configurations without success, the generated JWT AccessToken looks fine to me (see bellow), but i'm unable to query the UserInfo endpoint with it.
{
"sub": "user1",
"email": "user1#example.com",
"amr": [
"mfa"
],
"jti": "-7gURc8Y1SXqOXhWR691i",
"iat": 1668777371,
"exp": 1668784571,
"scope": "openid",
"client_id": "client-1",
"iss": "http://localhost:3000",
"aud": "http://localhost:3000"
}
Thanks in advance.
As per the module documentation's userinfo feature.
Its use requires an opaque Access Token with at least openid scope that's without a Resource Server audience.
In essence, this implementation's userinfo endpoint will not work with JWT Access Tokens that are issued for a particular resource server. This is because the userinfo endpoint is a resource for the client and if it was callable with an access token that was sent to a resource server, that resource server can turn around and query userinfo which is not the intended use of the userinfo endpoint.
In cases when JWT Access Tokens are issued the client will get all scope requested userinfo claims in the ID Token it receives, removing the need to call userinfo.

How to return the a formatted response from a mongo query/projection?

I'm trying to create an API to validate a promocode. I have minimal experience with mongo and the backend in general so I'm a bit confused in what is the best approach to do what I'm trying to accomplish.
I have this PromoCode form in the client. When a user types a promocode I would like for my backend to
verify if the code exists in one of the docs.
if it exists then return that code, the value for that code and the couponId
if the code doesn't exist then return an error.
My db is structured like this. The user will type one of those codes inside the codes: []
{
"_id": {
"$oid": "603f7a3b52e0233dd23bef79"
},
"couponId": "rate50",
"value": 50,
"codes": ["K3D01XJ50", "2PACYFN50", "COKRHEQ50"]
},
{
"_id": {
"$oid": "603f799d52e0233dd23bef78"
},
"couponId": "rate100",
"value": 100,
"codes": ["rdJ2ZMF100", "GKAAYLP100", "B9QZILN100"]
}
My route is structure like this:
router.post('/promoCode', (req, res, next) => {
const { promoCode } = req.body;
console.log('this is the req.body.promoCode on /promoCode', promoCode)
if (!promoCode) {
throw new Error('A promoCode needs to be passed')
}
promoCodesModel
.validatePromoCode(req.body.promoCode)
.then((response) => {
console.log('response inside /promoCode', response)
res.status(200).json({ data: response })
})
.catch((error) => {
res.status(400).json({ result: 'nok', error: error })
})
})
The validatePromoCode function is the following:
const validatePromoCode = async (code) => {
try {
let promoCode = await PromoCodesModel.find(
{"codes": code},
{_id: 0, codes: { $elemMatch: { $eq: code }} })
console.log('This is the promocode', promoCode)
return promoCode
} catch (err) {
throw new Error (err.stack)
}
}
All this seems to sort of work since I get the following response when the code is typed correctly
{
"data": [
{
"codes": [
"COKRHEQ50"
]
}
]
}
when typed incorrectly I get
{
"data": []
}
What I would like to get back is. (How can I accomplish this ?). Thanks
// when typed correctly
{
"data": { value: 50, couponId: "rate50", code: "COKRHEQ50" }
}
// when typed incorrectly
{
"error": "this is not valid code"
}
TL;DR: I would like to return a formatted query with specific values from a mongo query or an error object if that value does not exist on the document object.
Ok just figured it out
To be able to get the this responsed (what I wanted):
{
"data": [
{
"codes": [
"K3D01XJ50"
],
"couponId": "rate50",
"value": 50
}
]
}
I ended up having to do this on validatePromoCode
onst validatePromoCode = async (code) => {
try {
let promoCode = await PromoCodesModel.find(
{ codes: code },
{ _id: 0, codes: { $elemMatch: { $eq: code } }, couponId: 1, value: 1 },
)
return promoCode
} catch (err) {
throw new Error(err.stack)
}
}
But is there a better way on doing this ? Thanks

Loopback 4 - POST request dtasource template

I am having issue to declare POST operation in Loopback 4 datasource file.
My template is as follows:
{
"template": {
"method": "POST",
"url": "https://reqres.in/api/login"
},
"functions": {
"login": []
}
}
My service interface
login(email: string, password: string): Promise<any>;
My Controller
#post('/loginTest')
async testingLogin(
#requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(TestModel, {
title: 'Post',
}),
},
},
})
testModel: TestModel, )
: Promise<any> {
// TEST MODEL CONTAIN JSON OBJECT {email: "" , password: ""}
console.log("Test Model Representation: ", testModel)
try {
var response = await this.loginService.login(testModel.email, testModel.password);
} catch (error) {
console.log("error", error)
}
console.log("Fake POST response", response)
return response;
};
I am using this fake API : https://reqres.in/api/login
I am getting following error:
Test Model Representation: { email: 'string', password: 'string' }
error Error: {"error":"Missing email or username"}
at callback (D:\loginApp\node_modules\loopback-connector-rest\lib\rest-builder.js:541:21)
at D:\loginApp\node_modules\loopback-datasource-juggler\lib\observer.js:269:22
at doNotify (D:\loginApp\node_modules\loopback-datasource-juggler\lib\observer.js:157:49)
at RestConnector.ObserverMixin._notifyBaseObservers (D:\loginApp\node_modules\loopback-datasource-juggler\lib\observer.js:180:5) {
statusCode: 400,
message: '{"error":"Missing email or username"}'
}
Fake POST response undefined
It look like my email and password is not passed ? Thanks for any help.
The login function you defined in the datasource file should match with the service interface. That means it would be something like:
"functions": {
"login": ["email", "password"]
}

Unable to update Data

Am trying to update the json data through an api call.
I was able to GET the data without any issues, as am not passing any Options in the request.
For UPDATE,
//saga.js
export function* BlurideaTitler(opt) {
const id = opt.id; // 4
const updatedTitle = opt.newTitle; // "title changed"
let options = {
crossDomain: true,
method: 'PUT',
json: true,
headers: {'Content-Type': 'application/json'},
body: {
title: updatedTitle
}
};
const requestURL = `http://localhost:3000/ideas/${id}`;
try {
yield call(request, requestURL, options);
} catch (err) {
console.log(err);
}
}
// request.js
export default function request(url, options) {
return fetch(url, options)
.then(checkStatus)
.then(parseJSON);
}
//db.json
JSON am trying to update.,
{
"ideas": [
{
"id": 4,
"title": "My fourth Idea",
"body": "Description of my fourth idea",
"created_date": "14-Apr-2019"
}
]
}
This is supposed to update the value of title. But it throws error'Bad request' . Can someone please let me know what am missing here.

`mongolab` - getting error on post, not able to post a data

I have a account with mongolab(mlab). I am trying to post a data for the users using postman add-on from chrome browser. I am getting error always. I could not able to post a data. I have tried with other diffrent ways. but no luck.
any one help me to sort this issue?
here is my api.js :
var
User = require('../models/user'),
config = require('../../config'),
secretKey = config.secretKey;
module.exports = function( app, express ) {
var api = express.Router();
api.post('/signup', function (req, res) {
var user = new User({
name:req.body.name,
username:req.body.username,
password:req.body.password
});
user.save(function(err){
if(err){
res.send(err);
return;
}
res.json({message:'User has been Created!'});
});
});
api.get('/users', function(req, res) {
User.find({}, function( req, users){
if(err) {
res.send(err);
return;
}
res.json(users);
})
});
return api;
}
config.js :
module.exports = {
"database":"mongodb://xxx:xxxx#ds015700.mlab.com:15700/arifstory",
"port" : process.env.PORT || 3000,
"secretKey" : "YourSecretKey"
}
And the user.js :
var
mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt-nodejs');
var UserSchema = new Schema({
name : String,
userName:{ type:String, required:true, index : { unique: true }},
password : { type:String, required : true, select : false }
});
UserSchema.pre('save', function(next) {
var user = this;
if(!user.isModified('password')) return next();
bcrypt.hash( user.password, null, null, function(err, hash) {
if(err) return next(err);
user.password = hash;
next();
});
});
UserSchema.methods.comparePassword = function( password ) {
var user = this;
return bcrypt.compareSync(password, user.password);
}
module.exports = mongoose.model('User', UserSchema);
I really unable to understand the issue here. please any one help me?
error
{
"message": "User validation failed",
"name": "ValidationError",
"errors": {
"userName": {
"message": "Path `userName` is required.",
"name": "ValidatorError",
"properties": {
"type": "required",
"message": "Path `{PATH}` is required.",
"path": "userName"
},
"kind": "required",
"path": "userName"
},
"password": {
"message": "Path `password` is required.",
"name": "ValidatorError",
"properties": {
"type": "required",
"message": "Path `{PATH}` is required.",
"path": "password"
},
"kind": "required",
"path": "password"
}
}
}
The data sent from chrome could be undefined.
Print it in console by including the below 2 lines above user.save function in api.js file:
console.log(req.body.username);
console.log(req.body.password);
if it shows as undefined, then make sure to check "x-www-form-urlencoded" under "body" in Postman and provide username and password under that.