Skip to content

Commit 43316fe

Browse files
authored
docs: example for passing state during twitter auth flow (#49)
* state usage * apply review comments
1 parent 885ed09 commit 43316fe

File tree

4 files changed

+129
-0
lines changed

4 files changed

+129
-0
lines changed

examples/state-usage/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
BASE_URL=http://localhost:3000
2+
TWITTER_CLIENT_ID="OAuth 2.0 Client ID from Twitter Developer portal"
3+
TWITTER_CLIENT_SECRET="OAuth 2.0 Client Secret from Twitter Developer portal"

examples/state-usage/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Plain JavaScript example for passing state during authentication flow with Passport Twitter OAuth 2.0 Strategy
2+
3+
This is an example of passing state during the authentication flow with Passport and `@superfaceai/passport-twitter-oauth2` packages on Express server. After a successful login, the application shows user profile information, the state that was passed during the authentication request and logs the access token to a console.
4+
5+
Check [`@superfaceai/passport-twitter-oauth2`](https://github.com/superfaceai/passport-twitter-oauth2) for more info about the package and [step-by-step tutorial](https://superface.ai/blog/twitter-oauth2-passport) on setting up the Twitter application.
6+
7+
## Setup
8+
9+
1. Install dependencies
10+
```shell
11+
npm i
12+
```
13+
1. Copy `.env.example` to `.env`
14+
```shell
15+
cp .env.example .env
16+
```
17+
1. Paste your Client ID and Client Secret from Twitter developer portal to `.env` file
18+
19+
## Usage
20+
21+
1. Start the server with
22+
```shell
23+
npm start
24+
```
25+
1. Visit `http://localhost:3000/auth/twitter?state=my-very-long-state-12234567890`
26+
27+
## Troubleshooting
28+
29+
If you run into any issues with the example, please don't hesitate to [open an issue](https://github.com/superfaceai/passport-twitter-oauth2/issues/new).

examples/state-usage/package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "state-usage",
3+
"version": "1.0.0",
4+
"description": "Plain JavaScript example for passing state during the authentication flow with Passport Twitter OAuth 2.0 Strategy",
5+
"main": "server.js",
6+
"private": true,
7+
"scripts": {
8+
"test": "echo \"Error: no test specified\" && exit 1",
9+
"start": "node server.js"
10+
},
11+
"keywords": [],
12+
"author": "",
13+
"license": "MIT",
14+
"dependencies": {
15+
"@superfaceai/passport-twitter-oauth2": "file:../..",
16+
"dotenv": "^16.0.3",
17+
"express": "^4.18.2",
18+
"express-session": "^1.17.3",
19+
"passport": "^0.6.0"
20+
}
21+
}

examples/state-usage/server.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
const express = require('express');
2+
const passport = require('passport');
3+
const { Strategy } = require('@superfaceai/passport-twitter-oauth2');
4+
const session = require('express-session');
5+
require('dotenv').config();
6+
7+
passport.serializeUser(function (user, done) {
8+
done(null, user);
9+
});
10+
passport.deserializeUser(function (obj, done) {
11+
done(null, obj);
12+
});
13+
14+
// Use the Twitter OAuth2 strategy within Passport
15+
passport.use(
16+
new Strategy(
17+
{
18+
clientID: process.env.TWITTER_CLIENT_ID,
19+
clientSecret: process.env.TWITTER_CLIENT_SECRET,
20+
clientType: 'confidential',
21+
callbackURL: `${process.env.BASE_URL}/auth/twitter/callback`,
22+
},
23+
(accessToken, refreshToken, profile, done) => {
24+
console.log('Success!', { accessToken, refreshToken });
25+
return done(null, profile);
26+
}
27+
)
28+
);
29+
30+
const app = express();
31+
32+
app.use(passport.initialize());
33+
app.use(
34+
session({ secret: 'keyboard cat', resave: false, saveUninitialized: true })
35+
);
36+
37+
app.get(
38+
'/auth/twitter',
39+
(req, res, next) => {
40+
const stateObject = {
41+
key: req.query.state
42+
};
43+
44+
passport.authenticate('twitter', {
45+
scope: ['tweet.read', 'users.read', 'offline.access'],
46+
state: stateObject // Passing the state as an object is required by the Passport strategy
47+
})(req, res, next);
48+
}
49+
);
50+
51+
52+
app.get(
53+
'/auth/twitter/callback',
54+
passport.authenticate('twitter'),
55+
function (req, res) {
56+
// Regenerate the session to prevent session fixation attacks
57+
req.session.regenerate(function (err) {
58+
if (err) {
59+
return res.status(500).json({ error: 'Failed to regenerate session' });
60+
}
61+
62+
const state = JSON.stringify(req.session.req.authInfo.state, undefined, 2);
63+
const userData = JSON.stringify(req.user, undefined, 2);
64+
res.end(
65+
`<h1>Authentication succeeded</h1> User data: <pre>${userData}</pre>
66+
State:
67+
<pre>${state}</pre>
68+
`
69+
);
70+
});
71+
}
72+
);
73+
74+
app.listen(3000, () => {
75+
console.log(`Listening on ${process.env.BASE_URL}`);
76+
});

0 commit comments

Comments
 (0)