Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
lipp committed Feb 2, 2017
1 parent f9bd015 commit 75eb875
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 120 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ On successfull login two cookies will be created:
- `token` - A "JSON Web Token" (JWT) containing profile information and the respective access tokens (Twitter/etc). http-only!
- `profile` - A JSON string which containing non-sensitive information (accessible from browser JS):
- `displayName` - The account specific user alias (e.g. Twitter name)
- `photo` - Optional, the account specific user image link
- `name` - The "real" name containing `givenName` and `familyName`
- `username` - string / mandatory, the account specific user alias (e.g. Twitter name)
- `photo` - string / pptional, the account specific user image link
- `name` - string / optional, the "real" name
The cookies will be available for your toplevel domain and all subdomains. In addition, the cookie's `secure` flag is set, which means
that your other websites/webservices must run over `https`.
Expand All @@ -33,7 +33,7 @@ that your other websites/webservices must run over `https`.

The configuration is done by means of environment variables.

## Required environment variables
## Mandatory environment variables

- `LW_SESSION_SECRET` - The session secret used by the microservice
- `LW_JWT_SECRET` - The secret to sign the JSON Web Token (JWT)
Expand Down Expand Up @@ -86,7 +86,7 @@ Don't forget to `encodeURIComponent` on them.
# Example
Checkout [login-example.now.sh](https://login-example.now.sh). The source code is [here](https://github.com/lipp/login-with/tree/master/example/nextjs).
Visit [login-with.now.sh](https://login-with.now.sh). The source code is [here](https://github.com/lipp/login-with/tree/master/example/nextjs).
# Deployment with now
Expand Down
2 changes: 1 addition & 1 deletion example/nextjs/components/KeyValueTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const KeyValueTable = ({flatObject}) => (
))}
</tbody>
<style jsx>{`
.table {
table td + td {
word-break: break-all;
}
`}</style>
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"express": "^4.14.0",
"express-session": "^1.14.2",
"jsonwebtoken": "^7.2.1",
"now-logs": "0.0.7",
"passport": "^0.3.2",
"passport-github2": "^0.1.10",
"passport-reddit": "^0.2.4",
Expand Down
60 changes: 23 additions & 37 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@

require('now-logs')('foofoo')
const express = require('express')
const passport = require('passport')
const cookieParser = require('cookie-parser')
const expressSession = require('express-session')
const MemoryStore = require('session-memory-store')(expressSession)
const routes = require('./src/routes')

const port = parseInt(process.argv[2], 10) || 3000

const profileCookieName = process.env.LW_PROFILE_COOKIENAME || 'profile'
const tokenCookieName = process.env.LW_JWT_COOKIENAME || 'jwt'
const tokenSecret = process.env.LW_JWT_SECRET
const sessionSecret = process.env.LW_SESSION_SECRET
const subDomain = process.env.LW_SUBDOMAIN || `localhost:${port}`
const cookieDomain = process.env.LW_SUBDOMAIN ? '.' + subDomain.split('.').slice(1).join('.') : null
const protocol = process.env.LW_SUBDOMAIN ? 'https:/' : 'http:/'
const tenDays = 1000 * 60 * 60 * 24 * 10
const maxAge = process.env.LW_COOKIE_MAXAGE || tenDays
const opts = require('./src/opts')(process.argv, process.env)

if (!tokenSecret) {
if (!opts.tokenSecret) {
console.error('no LW_TOKEN_SECRET env variable specified')
process.exit(1)
}

if (!sessionSecret) {
if (!opts.sessionSecret) {
console.error('no LW_SESSION_SECRET env variable specified')
process.exit(1)
}

const rootUrl = protocol + '/' + subDomain
const rootUrl = opts.protocol + '/' + opts.subDomain

console.log(`Using subdomain "${rootUrl}" for callback urls`)

Expand All @@ -45,42 +36,37 @@ passport.deserializeUser((user, done) => done(null, user))
const app = express()
app.use(cookieParser())
app.use(expressSession({
secret: sessionSecret,
secret: opts.sessionSecret,
resave: false,
saveUninitialized: false,
store: new MemoryStore()
}))
app.use(passport.initialize())

if (strategies.length > 0) {
opts.strategies = strategies
opts.passport = passport
const routes = require('./src/routes')(opts)

app.get(
strategies.map(strategy => `/${strategy.type}`),
routes.onAuthenticationRequest({
strategies,
passport,
tokenSecret,
tokenCookieName,
profileCookieName,
cookieDomain,
maxAge
})
routes.onAuthenticationRequest
)

app.get(
strategies.map(strategy => `/${strategy.type}/callback`),
routes.onAuthenticationCallback({
strategies,
passport,
tokenSecret,
tokenCookieName,
profileCookieName,
cookieDomain,
maxAge
})
routes.onAuthenticationCallback
)

app.get(
'/logout',
routes.onLogout
)

app.get('/logout', routes.onLogout({tokenCookieName, profileCookieName, cookieDomain}))
app.get('/', routes.onIndex({tokenCookieName, profileCookieName}))
app.get(
'/',
routes.onIndex
)
}

app.listen(port)
app.listen(opts.port)
125 changes: 66 additions & 59 deletions src/routes.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
const jwt = require('jsonwebtoken')

module.exports.onAuthenticationRequest = ({strategies, passport}) => (req, res, next) => {
const type = req.path.split('/')[1]
const strategy = strategies.find(strategy => strategy.type === type)
const opts = {}
req.session.success = req.query.success
req.session.failure = req.query.failure
if (strategy.preHook) {
strategy.preHook(req, opts)
}
passport.authenticate(type, opts)(req, res, next)
}

const cookieOpts = ({httpOnly, reset = false, domain, maxAge = false}) => ({
secure: true,
httpOnly,
Expand All @@ -20,59 +8,78 @@ const cookieOpts = ({httpOnly, reset = false, domain, maxAge = false}) => ({
maxAge: !reset ? maxAge : maxAge
})

module.exports.onAuthenticationCallback = ({passport, tokenCookieName, tokenSecret, profileCookieName, cookieDomain, maxAge = false}) => (req, res, next) => {
const type = req.path.split('/')[1]
passport.authenticate(type, (error, user) => {
if (error) {
module.exports = ({
strategies,
passport,
tokenCookieName,
tokenSecret,
profileCookieName,
cookieDomain,
maxAge = false
}) => ({
onAuthenticationRequest: (req, res, next) => {
const type = req.path.split('/')[1]
const strategy = strategies.find(strategy => strategy.type === type)
const opts = {}
req.session.success = req.query.success
req.session.failure = req.query.failure
if (strategy.preHook) {
strategy.preHook(req, opts)
}
passport.authenticate(type, opts)(req, res, next)
},
onAuthenticationCallback: (req, res, next) => {
const type = req.path.split('/')[1]
passport.authenticate(type, (error, user) => {
if (error) {
res.cookie(tokenCookieName, '', cookieOpts({
reset: true,
httpOnly: true,
domain: cookieDomain
}))
res.cookie(profileCookieName, JSON.stringify({error}), cookieOpts({
httpOnly: false,
domain: cookieDomain,
maxAge
}))
if (req.session.failure) {
return res.redirect(decodeURIComponent(req.session.failure))
}
} else if (user) {
res.cookie(tokenCookieName, jwt.sign(user, tokenSecret), cookieOpts({
httpOnly: true,
domain: cookieDomain,
maxAge
}))
res.cookie(profileCookieName, JSON.stringify(user.profile), cookieOpts({
httpOnly: false,
domain: cookieDomain,
maxAge
}))
if (req.session.success) {
return res.redirect(decodeURIComponent(req.session.success))
}
}
return res.json({error, user})
})(req, res)
},
onLogout: (req, res) => {
res.cookie(tokenCookieName, '', cookieOpts({
reset: true,
httpOnly: true,
domain: cookieDomain
}))
res.cookie(profileCookieName, JSON.stringify({error}), cookieOpts({
httpOnly: false,
domain: cookieDomain,
maxAge
}))
if (req.session.failure) {
return res.redirect(decodeURIComponent(req.session.failure))
}
} else if (user) {
res.cookie(tokenCookieName, jwt.sign(user, tokenSecret), cookieOpts({
httpOnly: true,
domain: cookieDomain,
maxAge
}))
res.cookie(profileCookieName, JSON.stringify(user.profile), cookieOpts({
res.cookie(profileCookieName, '', cookieOpts({
reset: true,
httpOnly: false,
domain: cookieDomain,
maxAge
domain: cookieDomain
}))
if (req.session.success) {
return res.redirect(decodeURIComponent(req.session.success))
if (req.query.success) {
return res.redirect(decodeURIComponent(req.query.success))
}
return res.json({status: 'logged out'})
},
onIndex: (req, res) => {
return res.json({token: req.cookies[tokenCookieName], profile: req.cookies[profileCookieName]})
}
return res.json({error, user})
})(req, res)
}

module.exports.onLogout = ({tokenCookieName, profileCookieName, cookieDomain}) => (req, res) => {
res.cookie(tokenCookieName, '', cookieOpts({
reset: true,
httpOnly: true,
domain: cookieDomain
}))
res.cookie(profileCookieName, '', cookieOpts({
reset: true,
httpOnly: false,
domain: cookieDomain
}))
if (req.query.success) {
return res.redirect(decodeURIComponent(req.query.success))
}
return res.json({status: 'logged out'})
}

module.exports.onIndex = ({tokenCookieName, profileCookieName}) => (req, res) => {
return res.json({token: req.cookies[tokenCookieName], profile: req.cookies[profileCookieName]})
}
})
3 changes: 1 addition & 2 deletions src/strategies/reddit.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ module.exports = {
refreshToken,
profile: {
username: profile.name,
provider: 'reddit',
photos: []
provider: 'reddit'
}
})
},
Expand Down
2 changes: 1 addition & 1 deletion src/strategies/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class TestStrategy extends Strategy {
}
authenticate (req, options) {
if (req.session._test_once) {
const profile = {displayName: 'foo', id: 2138716238765, provider: 'test'}
const profile = {username: 'foo', provider: 'test'}
this._verify(profile, (error, user) => {
req.session._test_once = false
if (error) {
Expand Down

0 comments on commit 75eb875

Please sign in to comment.