Skip to content

Commit

Permalink
Merge pull request #17 from veracity/feature/rc1
Browse files Browse the repository at this point in the history
Updated samples, docs and readme
  • Loading branch information
rudfoss committed Oct 1, 2019
2 parents ebef582 + 60ac089 commit 894eaf7
Show file tree
Hide file tree
Showing 16 changed files with 551 additions and 4 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Version `1.0.0` is the first officially released and supported implementation of

- [Quick Start](#quick-start)
* [onVerify / Verifier](#onverify--verifier)
- [Encrypting session](#encrypting-session)
- [Passing state](#passing-state)
- [Error handling](#error-handling)
- [Authentication process](#authentication-process)
Expand Down Expand Up @@ -97,6 +98,39 @@ const verifier = async (tokenData, req, done) => {
passport.use("veracity", new VIDPOpenIDCStrategy(strategySettings, verifier))
```

## Encrypting session
When configuring authentication you need to provide a place to store session data. This is done through the `store` configuration for `express-session`. In the samples we use a MemoryStore instance that keeps the data in memory, but this is not suitable to for production as it does not scale. For such systems you would probably go with a database or cache of some kind such as MySQL or Redis.

Once you set up such a session storage mechanism, however there are some considerations you need to take into account. Since the access tokens for individual users are stored as session data it means that anyone with access to the session storage database can extract any token for a currently logged in user and use it themselves. Since the token is the only key needed to perform actions on behalf of the user it is considered sensitive information and must therefore be protected accordingly.

This library comes with a helper function to deal with just this scenario called `createEncryptedSessionStore`. This function uses the **AES-256-CBC** algorithm to encrypt and decrypt a subset of session data on-the-fly preventing someone with access to the store from seeing the plain access tokens. They will only see an encrypted blob of text.

The way `createEncryptedSessionStore` works is that it replaces the read and write functions of an `express-session` compatible store with augmented versions that decrypt and encrypt a set of specified properties (if present on the session object) respectively. This means that you can still use any of the compatible store connectors and simply pass it through the helper function to get a version that provides encryption.

Using the Redis connector you can configure an encrypted session like this:
```javascript
const session = require("express-session")
const { createEncryptedSessionStore } = require("@veracity/node-auth")
const redisStore = require("connect-redis")(session)

// You should NOT hard-code the encryption key. It should be served from a secure store such as Azure KeyVault or similar
const encryptedRedisStore = createEncryptedSessionStore("encryption key")(redisStore)

// We can now use the encryptedRedisStore in place of a regular store to configure authentication
setupWebAppAuth({
app,
strategy: {
clientId: "",
clientSecret: "",
replyUrl: ""
},
session: {
secret: "ce4dd9d9-cac3-4728-a7d7-d3e6157a06d9",
store: encryptedRedisStore // Use encrypted version of redis store
}
})
```

## Passing state
Sometimes it is useful to be able to pass data from before the login begins all the way through the authentication process until control is returned back to your code. This library supports this in two ways for web and native applications (the bearer token validation strategy does not support this):

Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@veracity/node-auth",
"version": "1.0.0",
"version": "1.0.1",
"description": "A library for authenticating with Veracity and retrieving one or more access tokens for communicating with APIs.",
"scripts": {
"build:copy-files": "ts-node -T scripts/copy-files.ts",
Expand Down
2 changes: 1 addition & 1 deletion samples/101-JS-Auth/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# JS Helper example
This example implements Veracity authentication using the `setupWebAppAuth` helper function. For details see the `start.js` file.

You need to fill in application credentials on line 14-16 in `start.js` before this sample will run. Visit the [Veracity for Developers project portal](https://developer.veracity.com/projects) to create them.
You need to fill in application credentials on line 23-25 in `start.js` before this sample will run. Visit the [Veracity for Developers project portal](https://developer.veracity.com/projects) to create them.

To run the sample:
```javascript
Expand Down
1 change: 1 addition & 0 deletions samples/101-JS-Auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
},
"homepage": "https://developer.veracity.com",
"dependencies": {
"@veracity/node-auth": "^1.0.0",
"body-parser": "^1.19.0",
"express": "^4.17.1",
"express-session": "^1.16.2",
Expand Down
20 changes: 20 additions & 0 deletions samples/102-TS-Auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# TypeScript Helper example
This example implements Veracity authentication using the `setupWebAppAuth` helper function and TypeScript. For details see the `start.ts` file.

You need to fill in application credentials on line 23-25 in `start.ts` before this sample will run. Visit the [Veracity for Developers project portal](https://developer.veracity.com/projects) to create them.

To run the sample:
```javascript
npm i
npm start
```

## HTTPS
This sample uses `node-forge` along with the `generateCertificate` utility to create a self-signed certificate for local development. This is **not** suitable for production and should be replaced with a more secure certificate signed by a trusted third-party. For example: [https://letsencrypt.org/](https://letsencrypt.org/)

## Dependencies
- `@veracity/node-auth`
- `express`
- `express-session`
- `passport`
- `node-forge`
3 changes: 2 additions & 1 deletion samples/102-TS-Auth/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@veracity/node-auth-js-helper-example",
"name": "@veracity/node-auth-ts-helper-example",
"private": true,
"version": "1.0.0",
"description": "",
Expand Down Expand Up @@ -29,6 +29,7 @@
"typescript": "^3.6.3"
},
"dependencies": {
"@veracity/node-auth": "^1.0.0",
"body-parser": "^1.19.0",
"express": "^4.17.1",
"express-session": "^1.16.2",
Expand Down
20 changes: 20 additions & 0 deletions samples/103-JS-Passport/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# JS Passport example
This example implements Veracity authentication using the `VIDPWebAppStrategy` passport strategy directly. This is intended for more advanced scenarios where your code or structure makes it hard or impossible to use the simpler helper function. This sample ends up with the same features as the ones using the helper, but with more code as we have to implement everything ourselves.

You need to fill in application credentials on line 33-35 in `start.js` before this sample will run. Visit the [Veracity for Developers project portal](https://developer.veracity.com/projects) to create them.

To run the sample:
```javascript
npm i
npm start
```

## HTTPS
This sample uses `node-forge` along with the `generateCertificate` utility to create a self-signed certificate for local development. This is **not** suitable for production and should be replaced with a more secure certificate signed by a trusted third-party. For example: [https://letsencrypt.org/](https://letsencrypt.org/)

## Dependencies
- `@veracity/node-auth`
- `express`
- `express-session`
- `passport`
- `node-forge`
33 changes: 33 additions & 0 deletions samples/103-JS-Passport/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@veracity/node-strategy-js-helper-example",
"private": true,
"version": "1.0.0",
"description": "",
"scripts": {
"start": "node start.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/veracity/node-auth.git"
},
"keywords": [
"veracity",
"authentication",
"openid",
"typescript"
],
"author": "Veracity",
"license": "MIT",
"bugs": {
"url": "https://github.com/veracity/node-auth/issues"
},
"homepage": "https://developer.veracity.com",
"dependencies": {
"@veracity/node-auth": "^1.0.0",
"body-parser": "^1.19.0",
"express": "^4.17.1",
"express-session": "^1.16.2",
"node-forge": "^0.9.1",
"passport": "^0.4.0"
}
}
30 changes: 30 additions & 0 deletions samples/103-JS-Passport/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Veracity node-auth - sample app</title>
<style>
nav a{
display: inline-block;
padding: 3px 10px;
}
nav a:first-child{
padding-left: 0;
}
</style>
</head>
<body>
<h1>Veracity Authentication with node</h1>
<p>
This example demonstrates how to set up basic authentication with Veracity and viewing the response.
</p>
<nav>
<a href="/login?returnTo=/user">Login now</a>
<a href="/user">View user data</a>
<a href="/refresh">Refresh token</a>
<a href="/logout">Logout</a>
</nav>
</body>
</html>
125 changes: 125 additions & 0 deletions samples/103-JS-Passport/start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Import the dependencies we need
const express = require("express")
const https = require("https")
const session = require("express-session")
const passport = require("passport")
const bodyParser = require("body-parser")

const {
VIDPWebAppStrategy,
makeSessionConfigObject,
generateCertificate,
createEncryptedSessionStore,
createRefreshTokenMiddleware,
VERACITY_API_SCOPES,
VERACITY_LOGOUT_URL,
VERACITY_METADATA_ENDPOINT
} = require("@veracity/node-auth")

// Create our express instance
const app = express()

// Create an encrypted version of the memory store to ensure tokens are encrypted at rest.
// This is an optional, but recommeded step.
const encryptedSessionStorage = createEncryptedSessionStore("encryptionKey")(new session.MemoryStore())

// Set up session and passport
app.use(session(makeSessionConfigObject({
secret: "ce4dd9d9-cac3-4728-a7d7-d3e6157a06d9", // Replace this with your own secret
store: encryptedSessionStorage
})))

// Initialize and configure passport
app.use(passport.initialize())
app.use(passport.session())
const strategySettings = { // Equivalent to the strategy property
clientId: "",
clientSecret: "",
replyUrl: "",
apiScopes: [VERACITY_API_SCOPES.services]
}
const strategy = new VIDPWebAppStrategy(
strategySettings,
(data, req, done) => { // Our verifier function (equivalent to onVerify)
done(null, data)
}
)
const strategyName = "veracity_oidc" // The name of our strategy
passport.use(strategyName, strategy)
passport.serializeUser((user, done) => { done(null, user) })
passport.deserializeUser((id, done) => { done(null, id) })

// Create our login endpoint
app.get("/login",
(req, res, next) => { // This is equivalent to onBeforeLogin and can be removed if not used
next()
},
passport.authenticate(strategyName), // Begin authenticating with passport
(req, res, next) => { // This handler will never be called in normal operation, but we log an error if it does
next(new Error("Login handler reached, this should not happen."))
}
)

// Create our return endpoint when the Veracity IDP has completed authentication
app.post("/auth/oidc/loginreturn",
bodyParser.urlencoded({extended: true}), // Decode body of request
passport.authenticate(strategyName), // Continue authentication by exchanging auth code for access token and optionally redirecting to another login
(req, res, next) => { // Equivalent to onLoginComplete. This is called once all tokens have been retrieved
res.redirect(req.query.returnTo || "/") // Redirect back to root or to the returnTo param sent to "/login"
}
)

// Set up our logout path
app.get("/logout",
(req, res) => {
req.logout() // Destroy our internal session
res.redirect(VERACITY_LOGOUT_URL) // Redirect to central logout for all Veracity Services
}
)

// The last feature we need to configure is the refresh middleware.
const refreshTokenMiddleware = createRefreshTokenMiddleware(
strategySettings,
(tokenData, req) => {
Object.assign(req.user.accessTokens, {
[tokenData.scope]: tokenData
})
},
VERACITY_METADATA_ENDPOINT
)

// Now we can continue with our normal handlers as in the other samples

// This endpoint will return our user data so we can inspect it.
app.get("/user", (req, res) => {
if (req.isAuthenticated()) {
res.send(req.user)
return
}
res.status(401).send("Unauthorized")
})

// Create an endpoint where we can refresh the services token.
// By default this will refresh it when it has less than 5 minutes until it expires.
app.get("/refresh", refreshTokenMiddleware(VERACITY_API_SCOPES.services), (req, res) => {
res.send({
updated: Date.now(),
user: req.user
})
})


// Serve static content from the public folder so we can display the index.html page
app.use(express.static("public"))

// Set up the HTTPS development server
const server = https.createServer({
...generateCertificate() // Generate self-signed certificates for development
}, app)
server.on("error", (error) => { // If an error occurs halt the application
console.error(error)
process.exit(1)
})
server.listen(3000, () => { // Begin listening for connections
console.log("Listening for connections on port 3000")
})
20 changes: 20 additions & 0 deletions samples/104-TS-Passport/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# TS Passport example
This example implements Veracity authentication using the `VIDPWebAppStrategy` passport strategy directly in TypeScript. This is intended for more advanced scenarios where your code or structure makes it hard or impossible to use the simpler helper function. This sample ends up with the same features as the ones using the helper, but with more code as we have to implement everything ourselves.

You need to fill in application credentials on line 38-40 in `start.ts` before this sample will run. Visit the [Veracity for Developers project portal](https://developer.veracity.com/projects) to create them.

To run the sample:
```javascript
npm i
npm start
```

## HTTPS
This sample uses `node-forge` along with the `generateCertificate` utility to create a self-signed certificate for local development. This is **not** suitable for production and should be replaced with a more secure certificate signed by a trusted third-party. For example: [https://letsencrypt.org/](https://letsencrypt.org/)

## Dependencies
- `@veracity/node-auth`
- `express`
- `express-session`
- `passport`
- `node-forge`
40 changes: 40 additions & 0 deletions samples/104-TS-Passport/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@veracity/node-strategy-ts-helper-example",
"private": true,
"version": "1.0.0",
"description": "",
"scripts": {
"start": "ts-node -P tsconfig.json start.ts"
},
"repository": {
"type": "git",
"url": "git+https://github.com/veracity/node-auth.git"
},
"keywords": [
"veracity",
"authentication",
"openid",
"typescript"
],
"author": "Veracity",
"license": "MIT",
"bugs": {
"url": "https://github.com/veracity/node-auth/issues"
},
"devDependencies": {
"@types/body-parser": "^1.17.1",
"@types/express": "^4.17.1",
"@types/express-session": "^1.15.14",
"ts-node": "^8.4.1",
"typescript": "^3.6.3"
},
"homepage": "https://developer.veracity.com",
"dependencies": {
"@veracity/node-auth": "^1.0.0",
"body-parser": "^1.19.0",
"express": "^4.17.1",
"express-session": "^1.16.2",
"node-forge": "^0.9.1",
"passport": "^0.4.0"
}
}

0 comments on commit 894eaf7

Please sign in to comment.