Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.*
!.gitignore
flask_session/
node_modules
6 changes: 6 additions & 0 deletions nodejs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## How to run
```
npm install
node app.js
```
App runs on port 3000 by default.
68 changes: 68 additions & 0 deletions nodejs/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const cookieParser = require("cookie-parser")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use semicolons at ends of statements for consistency?

const fetch = require("node-fetch")
const express = require("express")
const session = require("express-session")
const OAuth2Strategy = require("passport-oauth2")
const passport = require("passport")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can/should we use http://www.passportjs.org/docs/openid/ instead?

Copy link
Author

@rongxin-liu rongxin-liu Sep 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The passport-openid library requires the user to enter their OpenID identifier for authentication, not sure if this is the expected user interaction like other examples? I also don't see where it can set client_id/client_secret etc.

I tried to make this NodeJS implementation similar to Django and Flask examples. In those examples, it appeared that they were using the OAuth library, which I was also using OAuth in this NodeJS example.

And I forgot the exact reasons why I ended up not using passport-openid, but I did spend a fair amount of time experimenting passport-openid and decided not to use it.


// Check for environment variables
const variables = ["CLIENT_ID", "CLIENT_SECRET", "SERVER_METADATA_URL"]
variables.forEach((variable) => {
if (!Object.keys(process.env).includes(variable)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use 4 spaces throughout?

console.log(`Missing ${variable}`)
process.exit(1)
}
})

// Configure application
const app = express()
const port = 3000
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use 8080 for IDE's sake for now?

app.set("view engine", "ejs")
app.use(cookieParser());
app.use(session({
secret: "example-secret",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can Express use file storage instead of signed cookies, to avoid secrets?

resave: false,
saveUninitialized: false
}))
app.use(passport.initialize())
app.use(passport.session())

// Configure OAuth
fetch(process.env["SERVER_METADATA_URL"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect we can avoid fetch by using the OpenID library?

.then(res => res.json())
.then(config => {
passport.use(new OAuth2Strategy({
authorizationURL: config["authorization_endpoint"],
tokenURL: config["token_endpoint"],
clientID: process.env["CLIENT_ID"],
clientSecret: process.env["CLIENT_SECRET"],
callbackURL: `http://localhost:${port}/callback`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to avoid hardcoding the host and port, instead letting passport figure that out so that we need only provide the /callback path?

}, (accessToken, refreshToken, profile, cb) => {
fetch(`${config["userinfo_endpoint"]}?access_token=${accessToken}`)
.then(res => res.json())
.then(json => {return cb(null, json)})
}))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try using callbacks instead of promises, assuming OpenID library simplifies this too?

passport.serializeUser((user, done) => {done(null, user);});
passport.deserializeUser((user, done) => {done(null, user);})
})

app.get("/", (req, res) => {
res.render("index", {userinfo: req.user})
})

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

app.get("/login", passport.authenticate("oauth2", {
session: true,
scope: ["openid", "profile", "email"]
}))

app.get("/logout", (req, res) => {
req.logout();
res.redirect("/")
})

app.listen(port)
21 changes: 21 additions & 0 deletions nodejs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "cs50-id",
"version": "1.0.0",
"description": "Sample code for using CS50 ID to authenticate users via HarvardKey, Princeton CAS, or Yale CAS.",
"main": "app.js",
"dependencies": {
"cookie-parser": "^1.4.5",
"ejs": "^3.1.6",
"express": "^4.17.1",
"express-session": "^1.17.2",
"node-fetch": "^2.6.1",
"passport": "^0.4.1",
"passport-oauth2": "^1.6.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update these versions and packages as needed, since it's been a while?

},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's omit scripts as unneeded? And devDependencies and any other keys that npm doesn't require?

"author": "CS50",
"license": "ISC"
}
31 changes: 31 additions & 0 deletions nodejs/views/index.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>

<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, width=device-width">
<title>nodejs</title>
</head>
<body>
<%if (userinfo) {%>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does EJS not recommend spaces after/before % stylistically?

<p>
You are logged in.
</p>
<dl>
<dt><code>sub</code></dt>
<dt><code><%=userinfo.sub%></code></dt>
<dt><code>name</code></dt>
<dt><code><%=userinfo.name%></code></dt>
<dt><code>email</code></dt>
<dt><code><%=userinfo.email%></code></dt>
</dl>
<p>
<a href="/logout">Log out</a>.
</p>
<%} else {%>
<p>
You are not logged in. <a href="/login">Log in</a>.
</p>
<%}%>
</body>
</html>