-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: example for passing state during twitter auth flow (#49)
* state usage * apply review comments
- Loading branch information
1 parent
885ed09
commit 43316fe
Showing
4 changed files
with
129 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
BASE_URL=http://localhost:3000 | ||
TWITTER_CLIENT_ID="OAuth 2.0 Client ID from Twitter Developer portal" | ||
TWITTER_CLIENT_SECRET="OAuth 2.0 Client Secret from Twitter Developer portal" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Plain JavaScript example for passing state during authentication flow with Passport Twitter OAuth 2.0 Strategy | ||
|
||
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. | ||
|
||
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. | ||
|
||
## Setup | ||
|
||
1. Install dependencies | ||
```shell | ||
npm i | ||
``` | ||
1. Copy `.env.example` to `.env` | ||
```shell | ||
cp .env.example .env | ||
``` | ||
1. Paste your Client ID and Client Secret from Twitter developer portal to `.env` file | ||
|
||
## Usage | ||
|
||
1. Start the server with | ||
```shell | ||
npm start | ||
``` | ||
1. Visit `http://localhost:3000/auth/twitter?state=my-very-long-state-12234567890` | ||
|
||
## Troubleshooting | ||
|
||
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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"name": "state-usage", | ||
"version": "1.0.0", | ||
"description": "Plain JavaScript example for passing state during the authentication flow with Passport Twitter OAuth 2.0 Strategy", | ||
"main": "server.js", | ||
"private": true, | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1", | ||
"start": "node server.js" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@superfaceai/passport-twitter-oauth2": "file:../..", | ||
"dotenv": "^16.0.3", | ||
"express": "^4.18.2", | ||
"express-session": "^1.17.3", | ||
"passport": "^0.6.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
const express = require('express'); | ||
const passport = require('passport'); | ||
const { Strategy } = require('@superfaceai/passport-twitter-oauth2'); | ||
const session = require('express-session'); | ||
require('dotenv').config(); | ||
|
||
passport.serializeUser(function (user, done) { | ||
done(null, user); | ||
}); | ||
passport.deserializeUser(function (obj, done) { | ||
done(null, obj); | ||
}); | ||
|
||
// Use the Twitter OAuth2 strategy within Passport | ||
passport.use( | ||
new Strategy( | ||
{ | ||
clientID: process.env.TWITTER_CLIENT_ID, | ||
clientSecret: process.env.TWITTER_CLIENT_SECRET, | ||
clientType: 'confidential', | ||
callbackURL: `${process.env.BASE_URL}/auth/twitter/callback`, | ||
}, | ||
(accessToken, refreshToken, profile, done) => { | ||
console.log('Success!', { accessToken, refreshToken }); | ||
return done(null, profile); | ||
} | ||
) | ||
); | ||
|
||
const app = express(); | ||
|
||
app.use(passport.initialize()); | ||
app.use( | ||
session({ secret: 'keyboard cat', resave: false, saveUninitialized: true }) | ||
); | ||
|
||
app.get( | ||
'/auth/twitter', | ||
(req, res, next) => { | ||
const stateObject = { | ||
key: req.query.state | ||
}; | ||
|
||
passport.authenticate('twitter', { | ||
scope: ['tweet.read', 'users.read', 'offline.access'], | ||
state: stateObject // Passing the state as an object is required by the Passport strategy | ||
})(req, res, next); | ||
} | ||
); | ||
|
||
|
||
app.get( | ||
'/auth/twitter/callback', | ||
passport.authenticate('twitter'), | ||
function (req, res) { | ||
// Regenerate the session to prevent session fixation attacks | ||
req.session.regenerate(function (err) { | ||
if (err) { | ||
return res.status(500).json({ error: 'Failed to regenerate session' }); | ||
} | ||
|
||
const state = JSON.stringify(req.session.req.authInfo.state, undefined, 2); | ||
const userData = JSON.stringify(req.user, undefined, 2); | ||
res.end( | ||
`<h1>Authentication succeeded</h1> User data: <pre>${userData}</pre> | ||
State: | ||
<pre>${state}</pre> | ||
` | ||
); | ||
}); | ||
} | ||
); | ||
|
||
app.listen(3000, () => { | ||
console.log(`Listening on ${process.env.BASE_URL}`); | ||
}); |