Home > Software engineering >  How do I serve static files with authentication?
How do I serve static files with authentication?

Time:11-09

This is my folder structure:

/app
  server.js

  /public
    index.html
    script-a.js

    /scripts
      script-b.js

These are the relevant contents of my index.html:

<!-- index.html -->
.....
...
<script scr="/script-a.js"></script>
<script scr="/script-b.js"></script>

These are the relevant contents of server.js:

import express                          from 'express'
import session                          from 'express-session'
import NedbStore                        from 'nedb'
import passport                         from 'passport'
import { Strategy as FacebookStrategy } from 'passport-facebook'
import { ensureLoggedIn }               from 'connect-ensure-login'
....
..
const db  = new NedbStore({ filename: `${__dirname}/db/nedb.db`, autoload: true })
const DB  = dbAsyncWrapper(db)

app.use(cors({
   origin: '*',
   credentials: true,
   optionSuccessStatus: 200
}))

app.use(function(req, res, next) {
  res.header('Access-Control-Allow-Origin', 'http://localhost:4200')
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization')
  res.header('Access-Control-Allow-Methods', 'POST, GET')
  next()
})

app.use(cookieParser())
app.use(express.json({ limit: '50mb' }))
app.use(express.urlencoded({ extended: true, limit: '50mb' }))

app.use(session({
  secret: 'googoogaga',
  resave: false,
  saveUninitialized: false
}))
app.use(passport.initialize())
app.use(passport.session())

passport.use(new FacebookStrategy({
  clientID      : FACEBOOK_APP_ID,
  clientSecret  : FACEBOOK_APP_SECRET,
  callbackURL   : "http://localhost:4200/facebook/callback",
  profileFields : ['id']
}, async(accessToken, refreshToken, profile, done) => {
  let facebookId = profile.id
  let userInDb =  await DB.getUser()
  if (userInDb && userInDb.facebookId === facebookId) {
    await DB.updateUser({ accessToken })
    done(null, userInDb)
  } else {
    let newUser = await DB.updateUser({ facebookId, accessToken })
    done(null, newUser)
  }
}))

passport.serializeUser(function(user, done) {
  done(null, user)
})

passport.deserializeUser(function(user, done) {
  done(null, user)
})

app.use('/', ensureLoggedIn('/auth/facebook'), express.static(__dirname   '/public'))

app.get('/auth/facebook', passport.authenticate('facebook', { scope:'email' }))

app.get('/facebook/callback', passport.authenticate('facebook', {
  successRedirect : '/',
  failureRedirect : '/auth/facebook'
}))

With the above code, I'm expecting to:

  1. go to /
  2. be redirected to facebook for login
  3. go back to /
  4. have index.html served and loaded
  5. have script-a.js and script-b.js load correctly

What happens instead is:

I go to / and I'm redirected to /auth/facebook with a 302 status code.

If I remove:

app.use('/', ensureLoggedIn('/auth/facebook'), express.static(__dirname   '/public'))

and instead declare every single route handler manually, everything works:

....
..
app.get('/', ensureLoggedIn('/auth/facebook'), (req,res) => {
  res.sendFile(__dirname   '/public/index.html')
})
app.get('/script-a.js', ensureLoggedIn('/auth/facebook'), (req,res) => {
  res.sendFile(__dirname   '/public/script-a.js')
})
app.get('/script-b.js', ensureLoggedIn('/auth/facebook'), (req,res) => {
  res.sendFile(__dirname   '/public/scripts/script-b.js')
})

CodePudding user response:

If your auth is implemented by this:

app.use('/public', ensureLoggedIn('/auth/facebook'));

Then, you either run that before your express.static() route like this:

app.use(ensureLoggedIn('/auth/facebook'));
app.use(express.static(__dirname   '/public'));

Or, you incorporate it specifically into the static route:

app.use(ensureLoggedIn('/auth/facebook'), express.static(__dirname   '/public'));

When I also include the first 2 lines, not even the index.html gets served. I only get a failed facebook redirect registered on the network panel.

Then, your auth middleware is either correctly denying access to an unauthorized client or your auth middleware is not working properly. There's nothing in your question that indicates which that might be or could allow us to diagnose that for you.

But all other static files fail to be served.

For us to help you with that, we'd have to see the specifics of a static file that is not serving correctly (URL in the web page, location of the resource in your file system, etc...). There are 4-5 common mistakes people make to get static files to work properly. The most common is just using the wrong URL in the web page for static resources such as images, CSS files, script files, etc...

CodePudding user response:

Unfortunately, the only solution I found was to turn my /public folder into a flat structure and do:

app.get('/', ensureLoggedIn('/auth/facebook'), (req,res) => {
  res.sendFile(__dirname   '/public/index.html')
})

app.get('/:filename', ensureLoggedIn('/auth/facebook'), (req,res) => {
  res.sendFile(__dirname   `/public/${req.params.filename}`)
})
  • Related