i am trying to lock up my swagger documentation, i am using express basic auth for this, when i try to access the documentation endpoint it just throws a HTTP ERROR 401 without asking me to provide a username and password.
app.use("/api-docs",basicAuth({
challenge: true,
users: { 'me': 'openforme' } }),
swaggerUi.serve,
swaggerUi.setup(yaml.parse(swaggerfile))
);
when i remove the basicauth middleware i can access the endpoint, what i want is a popup to into the username and password.
use the Nub-auth package
const express = require('express');
const nubAuth = require('nub-auth');
app = express();
app.use(['/v1'], nubAuth({
challenge: true,
users: { 'admins': 'admin', 'users': 'user', 'guest': 'guested' }
}));
app.get('/api', (req, res) => {
res.send('Hello World welcome to API endpoints free !');
});
app.get("/v1", (req, res) => {
res.send("Hello World welcome to API endpoints closed !");
});
from my investigation edge does not have the basic auth feature enabled.
Related
I know there already are a lot of questions here like this, but none really helped me really since I'm a Junior Developer.
I am currently trying to completely just reproduce the following article: https://medium.com/devops-dudes/secure-front-end-react-js-and-back-end-node-js-express-rest-api-with-keycloak-daf159f0a94e
In there One tries to secure a Frontend with React JS and a Node.js Backend (Express Rest API) with Keylcoak.
But somehow if I try to start the node-microservices app my console keeps showing me "Initializing Keycloak" and if I try to access an endpoint in my browser it says:
TypeError: Cannot read property 'keycloak-token' of undefined
What did I do wrong?
Node-Microservices Code:
index.js
const express = require('express');
var router = express.Router();
var app = express();
const keycloak = require('./keycloak-config.js').initKeycloak();
app.use(keycloak.middleware());
router.get('/user', keycloak.protect('user'), function (req, res) {
res.send("Hello User");
});
router.get('/admin', keycloak.protect('admin'), function (req, res) {
res.send("Hello Admin");
});
router.get('/all', keycloak.protect(['user', 'admin']), function (req, res) {
res.send("Hello All");
});
app.get('/', function (req, res) {
res.send("Server is up!");
});
app.listen(8081);
keycloak-config.js
var session = require('express-session');
var Keycloak = require('keycloak-connect');
const chalk = require('chalk');
let keycloak;
var keycloakConfig = {
"realm": "Demo-Realm",
"bearer-only": true,
"auth-server-url": "http://localhost:8080/auth/",
"ssl-required": "external",
"resource": "node-microservice",
"verify-token-audience": true,
"use-resource-role-mappings": true,
"confidential-port": 0,
"realmPublicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1SrR985UGLhBlCReW1p9ypcKbQhjHXDqS3DK78ihqGxLiwNWWsG/ilwD54HbxMjcVIl6hLXZbpd85oAr6HCSjRm8D6HGP6AVfva7s6pQcXmNWIeFGhwOSM/k2rjXkVGpCu3Yhg+Fnx03zA/nSlybhyYJrt/EftbjhCVO58WnAhEY8ytBHZQws+I85BzstClm3dSXj9EiWea6pqB7vpYkMy/nGUBgfOFn30Hqa2Pp7Dxkgd7+G/xRN2JDbg09etgZzt9kXVs1X6LbwAWG60qeNF2ZMCHTZeiHi0FRM7d7irRrc68orrFiEg2Gp7u3urr2ss4JOwSWe9QK/l3eZ3XS6QIDAQAB"
};
function initKeycloak() {
if(keycloak) {
console.log("Returning existing Keycloak instance!");
return keycloak;
}
else {
console.log("Initializing Keycloak...");
var memoryStore = new session.MemoryStore();
keycloak = new Keycloak({
store: memoryStore,
secret: 'any_key',
resave: false,
saveUnitialized: true
}, keycloakConfig);
return keycloak;
}
}
module.exports = {
initKeycloak
};
EDIT:
You need to do it like the solution the documentation proposes, as
otherwise the middleware will not find the keycloak connection.
--
I had a brief look on the medium article you linked and it seems like the article uses a complicated approach to securing your express routes.
Have a look at the Official documentation of the NodeJS adapter for Keycloak (keycloak-connect), it's much, much simpler than described in the article to configure and use that way. What the initKeycloak() is doing is unneccessarily mimicking the behaviour of the frontend keycloak.js, which indeed ships a initKeycloak method to initialize Keycloak authorization services at the frontend. For Node applications, just initialize keycloak once in your index.js and you're good to go.
I have my service running with injected Kubernetes secrets, that's why all the environment variables and am setting up keycloak at the top of my index.ts like that:
Example
import express from 'express';
import pgSession from 'connect-pg-simple';
import Keycloak from 'keycloak-connect';
const app = express();
const pgSessionInit = pgSession(session);
const pgSessionStore = new pgSessionInit({
pool: pool,
tableName: 'session',
schemaName: 'foo',
createTableIfMissing: true,
});
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: pgSessionStore
}))
const keycloak = new Keycloak({
store: pgSessionStore
});
// RequestHandler and Route definitions after this point
On a side note: It helps us a lot to include stack traces, so that we can better understand your problem and point you in the right direction.
Node has an additional property on its Error object called "stack".
try{
// some code producing an Error
}catch(e: unknown){
if(e instanceof Error){
console.error(e, e.stack); // Retrieve Error Message and stack
}
}
I am trying to get the data my nodeJS server is receiving from a form on the front end to send that data to my email. I have tried to use nodemailer and haven't succeeded much. Can someone tell me perhaps what I am doing wrong with the following code?
const express = require("express");
const app = express();
const nodemailer = require("nodemailer");
var smtpTransport = require("nodemailer-smtp-transport");
const PORT = process.env.PORT || 4000;
app.use(express.static(__dirname + "/front-end"));
app.get("/", (req, resp) => {
resp.sendFile(__dirname + "/front-end/index.html");
});
app.use(express.json());
app.use(express.urlencoded());
app.post("/formData", (req, resp) => {
const data = req.body;
var transport = nodemailer.createTransport(
smtpTransport({
service: "Gmail",
auth: {
user: "user#gmail.com",
pass: "123456",
},
})
);
transport.sendMail(
{
//email options
from: "Sender Name <email#gmail.com>",
to: "Receiver Name <receiver#email.com>", // receiver
subject: "Emailing with nodemailer", // subject
html: data, // body (var data which we've declared)
},
function (error, response) {
//callback
if (error) {
console.log(error);
} else {
console.log("Message sent:");
resp.send("success!");
}
transport.close();
}
);
});
app.listen(PORT, () => {
console.log(`server running on port ${PORT}`);
});
Your code, at a glance, looks fine to me. I think the problem is (since you’re not stating you have set that up), that you want to send email with GMail. If you want to send email from your own app or web service via Gmail, you should set up a project in the Google Cloud Platform. Read more here.
Alternatively, you could use a service like Postmark, which you can configure to send emails via a domain that you own. There’s a free trial. Mailgun is a similar service. (I’m not affiliated to either).
I am building a simple app with React as frontend and Node/Express/MongoDB as backend. I am authenticating user using Passport. Local authentication is working, as well as Google authentication.
I just seem to not able to load the google login page through the app. I am getting CORS error. I have shared the error below.
On React Login page:
const onClick = async () => {
await Axios.get('/auth/google');
};
Proxy Middleware:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
app.use(createProxyMiddleware('/auth', { target: 'http://localhost:4000' }));
};
Node Server.js:
app.use('/auth', require('./routes/auth'));
routes/auth file:
const cors = require('cors');
var corsOptions = {
origin: 'http://localhost:3000',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
preflightContinue: false,
optionsSuccessStatus: 204,
};
router.get(
'/google',
cors(corsOptions),
passport.authenticate('google', {
scope: ['profile', 'email'],
}),
);
router.get('/google/redirect',cors(corsOptions), passport.authenticate('google'), (req, res) => {
res.send(req.user);
});
passportconfig.js:
passport.use(
new GoogleStrategy(
{
clientID: ClientID,
clientSecret: ClientSecret,
callbackURL: '/auth/google/redirect',
proxy: true,
},
(accessToken, refreshToken, profile, done) => {
// passport callback function
//check if user already exists in our db with the given profile ID
User.findOne({ googleId: profile.id }).then((currentUser) => {
if (currentUser) {
//if we already have a record with the given profile ID
done(null, currentUser);
} else {
//if not, create a new user
new User({
googleId: profile.id,
})
.save()
.then((newUser) => {
done(null, newUser);
});
}
});
},
),
);
Error:
Access to XMLHttpRequest at 'https://accounts.google.com/o/oauth2/v2/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth%2Fgoogle%2Fredirect&scope=profile%20email&client_id=<clientID>.apps.googleusercontent.com' (redirected from 'http://localhost:3000/auth/google') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
If I click on the above XMLHttpRequest link, I am able to authenticate and an account is created on my DB with googleID.
I have tried different options suggested throughout internet, but none of them is working for me. I am not sure what is going wrong here.
According to the documentation, try removing the corsOptions entirely and just use the cors() function in your express middle-ware before any router is declared. Like so:
app.use(cors());
Let me know if this works.
// step 1:
// onClick handler function of the button should use window.open instead
// of axios or fetch
const loginHandler = () => window.open("http://[server:port]/auth/google", "_self")
//step 2:
// on the server's redirect route add this successRedirect object with correct url.
// Remember! it's your clients root url!!!
router.get(
'/google/redirect',
passport.authenticate('google',{
successRedirect: "[your CLIENT root url/ example: http://localhost:3000]"
})
)
// step 3:
// create a new server route that will send back the user info when called after the authentication
// is completed. you can use a custom authenticate middleware to make sure that user has indeed
// been authenticated
router.get('/getUser',authenticated, (req, res)=> res.send(req.user))
// here is an example of a custom authenticate express middleware
const authenticated = (req,res,next)=>{
const customError = new Error('you are not logged in');
customError.statusCode = 401;
(!req.user) ? next(customError) : next()
}
// step 4:
// on your client's app.js component make the axios or fetch call to get the user from the
// route that you have just created. This bit could be done many different ways... your call.
const [user, setUser] = useState()
useEffect(() => {
axios.get('http://[server:port]/getUser',{withCredentials : true})
.then(response => response.data && setUser(response.data) )
},[])
Explanation....
step 1 will load your servers auth url on your browser and make the auth request.
step 2 then reload the client url on the browser when the authentication is
complete.
step 3 makes an api endpoint available to collect user info to update the react state
step 4 makes a call to the endpoint, fetches data and updates the users state.
I am new in meteor js and web app is created in meteor. I need to create API's for mobile app and native and web app
will share the same database. This is not clear to me from where I need to start to create API
for the native app? This is my login route which I am using for the web app.
Path of web app login route
socialapp\socialappv1\app\lib\routes.js
Router.route('login', {
name: 'login',
controller: 'LoginController',
where: 'client'
});
and to create API I have created a server.js file in socialapp\socialappv1\app\server\ directory and I am trying to create API to register a user.
Router.route('/register/',{where: 'server'})
.post(function(){
//console.log(this.request.body);
//return false;
let user = {
email : this.request.body.email,
username : this.request.body.username,
password : this.request.body.password,
};
});
const userId = Accounts.createUser(user);
if(userId)
{
console.log("Register");
}else{
console.log("Not Register");
}
});
Is there any other way to create rest API e.g. to call controllers or is this correct to start API?
I think your code may be trying to set up client side routes (not sure which router you are using).
You need to add server side routes (and you can use express for this), and the handler needs to attach to the Meteor environment
This is some code I have written to handle payment confirmations coming to the server: (server side code of course)
import { Meteor } from 'meteor/meteor'
import express from 'express'
import bodyParser from 'body-parser'
const debug = require('debug')('b2b:server-payments')
async function acceptPayment(req, res) {
// We need to bind to the Meteor environment for this to work.
Meteor.bindEnvironment(() => {
debug('/payment hook', req.body)
try {
// Handle the data that's in req.body ...
:
:
} catch (e) {
console.error(e)
}
})()
res.status(200).json({ status: 'ok' }) // Change this if your data isn't JSON
}
export function setupPaymentsApi() {
debug('Setting up payment hooks')
const app = express()
app.use(bodyParser.json({ extended: false }))
app.post('/payment', acceptPayment)
app.get('/api', (req, res) => {
res.status(200).json({ message: 'B2B Payments API' }) // Shouldn't call this, just for testing for now
})
WebApp.connectHandlers.use(app)
}
I am logging users in via their domain Google accounts using passport.js. This works great, but now I need to give this application access to a few Google API's (drive, sheets, etc).
When a user logs in, a message appears in the logs, that makes it seem like passport has all the required info:
info: [06/Jun/2019:21:24:37 +0000] "302 GET /auth/callback?code=** USER ACCESS TOKEN HERE **&scope=email%20profile%20https://www.googleapis.com/auth/drive.file%20https://www.googleapis.com/auth/spreadsheets%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile%20https://www.googleapis.com/auth/drive HTTP/1.1" [46]
This is achieved by passing the appended scopes via passport.authenticate(), which presents the user with the "Grant access to these things on your Google account to this app?" screen :
//Initial auth call to Google
router.get('/',
passport.authenticate('google', {
hd: 'edmonds.wednet.edu',
scope: [
'email',
'profile',
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/spreadsheets'
],
prompt: 'select_account'
})
);
However, when I go and try to call an API with something like:
const {google} = require('googleapis');
const sheets = google.sheets({version: 'v4', auth});
router.post('/gsCreate', function(req,res,next){
sheets.spreadsheets.create({
// Details here.....
});
});
I get nothing but errors (the current one is debug: authClient.request is not a function)
My question is: Is it possible for me to use a setup like this, asking the user to log in and grant permissions once, and then somehow save that to their user session via passport?
I had the same question, but I was able to access Google Gmail API functionalities along with Passport.js user authentication by specifying 'scopes' using the following process.
First, create a file to setup the passport-google-strategy in nodejs as follows.
passport_setup.js
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20')
const fs = require("fs");
const path = require('path');
//make OAuth2 Credentials file using Google Developer console and download it(credentials.json)
//replace the 'web' using 'installed' in the file downloaded
var pathToJson = path.resolve(__dirname, './credentials.json');
const config = JSON.parse(fs.readFileSync(pathToJson));
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
const query = { _id: id }
Users.findOne(query, (err, user) => {
if (err) {
res.status(500).json(err);
} else {
done(null, user)
}
})
})
//create a google startergy including following details
passport.use(
new GoogleStrategy({
clientID: config.installed.client_id,
clientSecret: config.installed.client_secret,
callbackURL: config.installed.redirect_uris[0]
}, (accessToken, refreshToken,otherTokenDetails, user, done) => {
//in here you can access all token details to given API scope
//and i have created file from that details
let tokens = {
access_token: accessToken,
refresh_token: refreshToken,
scope: otherTokenDetails.scope,
token_type: otherTokenDetails.token_type,
expiry_date:otherTokenDetails.expires_in
}
let data = JSON.stringify(tokens);
fs.writeFileSync('./tokens.json', data);
//you will get a "user" object which will include the google id, name details,
//email etc, using that details you can do persist user data in your DB or can check
//whether the user already exists
//after persisting user data to a DB call done
//better to use your DB user objects in the done method
done(null, user)
})
)
Then create your index.js file in nodejs for API route management and to call send method of Gmail API.
Also, run the following command to install "google-apis"
npm install googleapis#39 --save
index.js
const express = require("express")
//import passport_setup.js
const passportSetup = require('./passport_setup')
const cookieSeesion = require('cookie-session');
const passport = require("passport");
//import google api
const { google } = require('googleapis');
//read credentials file you obtained from google developer console
const fs = require("fs");
const path = require('path');
var pathToJson_1 = path.resolve(__dirname, './credentials.json');
const credentials = JSON.parse(fs.readFileSync(pathToJson_1));
//get Express functionalities to app
const app = express();
// **Middleware Operations**//
//cookie encryption
app.use(cookieSeesion({
name:'Reserve It',
maxAge: 1*60*60*1000,
keys: ['ranmalc6h12o6dewage']
}))
//initialize passort session handling
app.use(passport.initialize())
app.use(passport.session())
app.use(express.json());
//**API urls**//
//route to authenticate users using google by calling google stratergy in passport_setup.js
//mention access levels of API you want in the scope
app.get("/google", passport.authenticate('google', {
scope: ['profile',
'email',
'https://mail.google.com/'
],
accessType: 'offline',
prompt: 'consent'
}))
//redirected route after obtaining 'code' from user authentication with API scopes
app.get("/google/redirect", passport.authenticate('google'), (req, res) => {
try {
//read token file you saved earlier in passport_setup.js
var pathToJson_2 = path.resolve(__dirname, './tokens.json');
//get tokens to details to object
const tokens = JSON.parse(fs.readFileSync(pathToJson_2));
//extract credential details
const { client_secret, client_id, redirect_uris } = credentials.installed
//make OAuth2 object
const oAuth2Client = new google.auth.OAuth2(client_id,
client_secret,
redirect_uris[0])
// set token details to OAuth2 object
oAuth2Client.setCredentials(tokens)
//create gmail object to call APIs
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client })
//call gmail APIs message send method
gmail.users.messages.send({
userId: 'me',//'me' indicate current logged in user id
resource: {
raw: //<email content>
}
}, (err, res) => {
if (err) {
console.log('The API returned an error: ' + err)
throw err
}
console.log('Email Status : ' + res.status)
console.log('Email Status Text : ' + res.statusText)
})
res.status(200).json({ status:true })
} catch (err) {
res.status(500).json(err)
}
})
app.listen(3000, () => { console.log('Server Satrted at port 3000') })
You can separate the routes in the index.js file to different files for clarity using express.Router()
If you want to call another Google API service just change this code segment and code below that;
const gmail = google.gmail({ version: 'v1', auth: oAuth2Client })
gmail.users.messages.send(....Send Method internal implementation given above....)
For Google Drive:
const drive = google.drive({version: 'v3', auth: oAuth2Client});
drive.files.list(...Refer "Google Drive API" documentation for more details....)
I believe you can't use passport.js for three-legged oauth for APIs like Sheets or Drive.
Have a look at the Using OAuth for web servers documentation instead.
user835611 has the correct answer, as that page explains everything quite nicely. However, if you still need more, the below link really helped me to understand how this works.
https://github.com/googleapis/google-auth-library-nodejs#oauth2