├── .babelrc ├── .eslintrc ├── .gitignore ├── README.MD ├── api └── discord.js ├── index.html ├── package.json ├── server.js ├── src └── js │ ├── components │ └── App │ │ ├── App.js │ │ └── styles.css │ └── entry.js ├── static ├── css │ └── styles.css └── js │ └── bundle.js ├── utils.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "react", 4 | ["env", { 5 | "targets": { 6 | "browsers": ["last 2 versions", "safari >= 7"], 7 | "node": 7.9 8 | } 9 | }] 10 | ] 11 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "airbnb", 4 | "rules": { 5 | "react/jsx-filename-extension": 0 6 | } 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | design 2 | node_modules 3 | .git 4 | .idea 5 | .atom 6 | *.log -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | ![s](https://cdn-images-1.medium.com/max/800/1*64GSosmMFaKyBM5Dn_3aeQ.png) 2 | 3 | # Discord Token Generator 4 | >An example app for my discord oauth2 tutorial on my medium blog. [Don't forget to check it out!](https://medium.com/@orels1/using-discord-oauth2-a-simple-guide-and-an-example-nodejs-app-71a9e032770) 5 | 6 | ## Run 7 | 8 | ``` 9 | npm install 10 | ``` 11 | 12 | Create a new app at your [developer dashboard](https://discordapp.com/developers/applications/me/create) 13 | 14 | Set the `CLIENT_ID` and `CLIENT_SECRET` env vars and run 15 | 16 | ``` 17 | node server.js 18 | ``` 19 | 20 | ## Develop 21 | 22 | ``` 23 | npm install 24 | ``` 25 | 26 | ``` 27 | npm run watch 28 | ``` -------------------------------------------------------------------------------- /api/discord.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by orel- on 15/May/17. 3 | */ 4 | 5 | const express = require('express'); 6 | const fetch = require('node-fetch'); 7 | const btoa = require('btoa'); 8 | const { catchAsync } = require('../utils'); 9 | 10 | const router = express.Router(); 11 | 12 | const CLIENT_ID = process.env.CLIENT_ID; 13 | const CLIENT_SECRET = process.env.CLIENT_SECRET; 14 | const redirect = encodeURIComponent('http://localhost:50451/api/discord/callback'); 15 | 16 | router.get('/login', (req, res) => { 17 | res.redirect(`https://discordapp.com/api/oauth2/authorize?client_id=${CLIENT_ID}&scope=identify&response_type=code&redirect_uri=${redirect}`); 18 | }); 19 | 20 | router.get('/callback', catchAsync(async (req, res) => { 21 | if (!req.query.code) throw new Error('NoCodeProvided'); 22 | const code = req.query.code; 23 | const creds = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`); 24 | const response = await fetch(`https://discordapp.com/api/oauth2/token?grant_type=authorization_code&code=${code}&redirect_uri=${redirect}`, 25 | { 26 | method: 'POST', 27 | headers: { 28 | Authorization: `Basic ${creds}`, 29 | }, 30 | }); 31 | const json = await response.json(); 32 | res.redirect(`/?token=${json.access_token}`); 33 | })); 34 | 35 | module.exports = router; 36 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Discord Token Generator 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-token-generator", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "watch": "webpack --progress --watch" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/orels1/discord-token-generator.git" 13 | }, 14 | "keywords": [ 15 | "nodejs", 16 | "react", 17 | "discord" 18 | ], 19 | "author": "orels1", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/orels1/discord-token-generator/issues" 23 | }, 24 | "devDependencies": { 25 | "babel-core": "^6.24.1", 26 | "babel-eslint": "^7.2.2", 27 | "babel-loader": "^7.0.0", 28 | "babel-preset-env": "^1.4.0", 29 | "babel-preset-react": "^6.24.1", 30 | "css-loader": "^0.28.1", 31 | "eslint": "^3.19.0", 32 | "eslint-config-airbnb": "^14.1.0", 33 | "eslint-plugin-import": "^2.2.0", 34 | "eslint-plugin-jsx-a11y": "^4.0.0", 35 | "eslint-plugin-react": "^6.10.3", 36 | "extract-text-webpack-plugin": "^2.1.0", 37 | "webpack": "^2.5.1" 38 | }, 39 | "homepage": "https://github.com/orels1/discord-token-generator#readme", 40 | "dependencies": { 41 | "btoa": "^1.1.2", 42 | "express": "^4.15.2", 43 | "node-fetch": "^2.6.1", 44 | "react": "^15.5.4", 45 | "react-dom": "^15.5.4" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by orel- on 15/May/17. 3 | */ 4 | const express = require('express'); 5 | const path = require('path'); 6 | 7 | const app = express(); 8 | 9 | app.use('/static', express.static(path.join(__dirname, 'static'))); 10 | 11 | app.get('/', (req, res) => { 12 | res.status(200).sendFile(path.join(__dirname, 'index.html')); 13 | }); 14 | 15 | app.listen(50451, () => { 16 | console.info('Running on port 50451'); 17 | }); 18 | 19 | // Routes 20 | app.use('/api/discord', require('./api/discord')); 21 | 22 | app.use((err, req, res, next) => { 23 | switch (err.message) { 24 | case 'NoCodeProvided': 25 | return res.status(400).send({ 26 | status: 'ERROR', 27 | error: err.message, 28 | }); 29 | default: 30 | return res.status(500).send({ 31 | status: 'ERROR', 32 | error: err.message, 33 | }); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /src/js/components/App/App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by orel- on 15/May/17. 3 | */ 4 | import React from 'react'; 5 | import styles from './styles.css'; 6 | 7 | const qs = (key) => { 8 | key = key.replace(/[*+?^$.[\]{}()|\\/]/g, '\\$&'); // escape RegEx meta chars 9 | const match = window.location.search.match(new RegExp(`[?&]${key}=([^&]+)(&|$)`)); 10 | return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); 11 | }; 12 | 13 | const App = () => ( 14 |
15 |
16 | Get Discord Token 17 |
18 |
19 | {(qs('token') && 20 |
21 |
22 | Your token 23 |
24 |
25 | {qs('token')} 26 |
27 |
28 | Scope 29 |
30 |
31 | identify 32 |
33 |
34 | ) 35 | || 36 | 40 | Login through Discord 41 | 42 | } 43 |
44 |
45 | This website is not affiliated with discord inc. 46 |
47 |
48 | I do not save anything. See sources 49 |
50 |
51 | ); 52 | 53 | export default App; 54 | -------------------------------------------------------------------------------- /src/js/components/App/styles.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Lato:400,700'); 2 | 3 | @value black: #23272A; 4 | @value dark: #2C2F33; 5 | @value greyple: #99AAB5; 6 | @value white: #FFFFFF; 7 | @value notsowhite: rgba(255,255,255,0.4); 8 | @value blurple: #7289DA; 9 | @value blurple-hover: #697ec4; 10 | 11 | html, body{ 12 | min-height: 100vh; 13 | margin: 0; 14 | background: black; 15 | font-family: 'Lato', sans-serif; 16 | } 17 | 18 | a { 19 | color: white; 20 | text-decoration: none; 21 | } 22 | 23 | .block { 24 | display: flex; 25 | flex-direction: column; 26 | justify-content: center; 27 | align-items: center; 28 | box-shadow: 0 1px 10px rgba(0,0,0,0.1); 29 | } 30 | 31 | .block__header { 32 | padding: 24px; 33 | border-radius: 5px 5px 0 0; 34 | background: blurple; 35 | color: notsowhite; 36 | font-size: 16px; 37 | text-transform: uppercase; 38 | font-weight: 700; 39 | width: 100%; 40 | box-sizing: border-box; 41 | } 42 | 43 | .block__body { 44 | padding: 30px; 45 | background: dark; 46 | border-radius: 0 0 5px 5px; 47 | } 48 | 49 | .block__footer { 50 | margin-top: 10px; 51 | font-size: 12px; 52 | color: notsowhite; 53 | } 54 | 55 | .login_button { 56 | text-decoration: none; 57 | display: block; 58 | line-height: 35px; 59 | padding: 0 21px; 60 | background: blurple; 61 | -webkit-border-radius: 3px; 62 | -moz-border-radius: 3px; 63 | border-radius: 3px; 64 | color: white; 65 | font-weight: 700; 66 | font-size: 16px; 67 | } 68 | 69 | .login_button:hover { 70 | background: blurple-hover; 71 | } 72 | 73 | .success { 74 | display: flex; 75 | flex-direction: column; 76 | width: 100%; 77 | max-width: 350px; 78 | box-sizing: border-box; 79 | } 80 | 81 | .success__header { 82 | color: notsowhite; 83 | font-size: 16px; 84 | text-transform: uppercase; 85 | font-weight: 700; 86 | padding-bottom: 10px; 87 | } 88 | 89 | .success__data { 90 | color: white; 91 | font-weight: 700; 92 | font-size: 16px; 93 | padding-bottom: 10px; 94 | } -------------------------------------------------------------------------------- /src/js/entry.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by orel- on 15/May/17. 3 | */ 4 | import React from 'react'; 5 | import { render } from 'react-dom'; 6 | import App from './components/App/App'; 7 | 8 | render((), document.getElementById('app')); -------------------------------------------------------------------------------- /static/css/styles.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700);html, body{ 2 | min-height: 100vh; 3 | margin: 0; 4 | background: #23272A; 5 | font-family: 'Lato', sans-serif; 6 | } 7 | 8 | a { 9 | color: #FFFFFF; 10 | text-decoration: none; 11 | } 12 | 13 | .styles__block___25HZ2 { 14 | display: flex; 15 | flex-direction: column; 16 | justify-content: center; 17 | align-items: center; 18 | box-shadow: 0 1px 10px rgba(0,0,0,0.1); 19 | } 20 | 21 | .styles__block__header___1oFGD { 22 | padding: 24px; 23 | border-radius: 5px 5px 0 0; 24 | background: #7289DA; 25 | color: rgba(255,255,255,0.4); 26 | font-size: 16px; 27 | text-transform: uppercase; 28 | font-weight: 700; 29 | width: 100%; 30 | box-sizing: border-box; 31 | } 32 | 33 | .styles__block__body___2rMc0 { 34 | padding: 30px; 35 | background: #2C2F33; 36 | border-radius: 0 0 5px 5px; 37 | } 38 | 39 | .styles__block__footer___3SlT- { 40 | margin-top: 10px; 41 | font-size: 12px; 42 | color: rgba(255,255,255,0.4); 43 | } 44 | 45 | .styles__login_button___1-2XJ { 46 | text-decoration: none; 47 | display: block; 48 | line-height: 35px; 49 | padding: 0 21px; 50 | background: #7289DA; 51 | -webkit-border-radius: 3px; 52 | -moz-border-radius: 3px; 53 | border-radius: 3px; 54 | color: #FFFFFF; 55 | font-weight: 700; 56 | font-size: 16px; 57 | } 58 | 59 | .styles__login_button___1-2XJ:hover { 60 | background: #697ec4; 61 | } 62 | 63 | .styles__success___2Z82d { 64 | display: flex; 65 | flex-direction: column; 66 | width: 100%; 67 | max-width: 350px; 68 | box-sizing: border-box; 69 | } 70 | 71 | .styles__success__header___2denR { 72 | color: rgba(255,255,255,0.4); 73 | font-size: 16px; 74 | text-transform: uppercase; 75 | font-weight: 700; 76 | padding-bottom: 10px; 77 | } 78 | 79 | .styles__success__data___2U49A { 80 | color: #FFFFFF; 81 | font-weight: 700; 82 | font-size: 16px; 83 | padding-bottom: 10px; 84 | } -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by orel- on 15/May/17. 3 | */ 4 | // async/await error catcher 5 | const catchAsyncErrors = fn => ( 6 | (req, res, next) => { 7 | const routePromise = fn(req, res, next); 8 | if (routePromise.catch) { 9 | routePromise.catch(err => next(err)); 10 | } 11 | } 12 | ); 13 | 14 | exports.catchAsync = catchAsyncErrors; 15 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by orel- on 15/May/17. 3 | */ 4 | const path = require('path'); 5 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 | 7 | const extractStyles = new ExtractTextPlugin({ 8 | filename: './css/styles.css', 9 | disable: process.env.NODE_ENV === 'development', 10 | }); 11 | 12 | module.exports = { 13 | entry: { 14 | bundle: './src/js/entry.js', 15 | }, 16 | output: { 17 | path: path.resolve(__dirname, 'static/'), 18 | filename: './js/[name].js', 19 | }, 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.css$/, 24 | exclude: /node_modules/, 25 | use: extractStyles.extract('css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'), 26 | }, 27 | { 28 | test: /\.css$/, 29 | use: ExtractTextPlugin.extract({ 30 | fallback: 'style-loader', 31 | use: 'css-loader', 32 | filename: './css/styles.css', 33 | }), 34 | include: /react-select/, 35 | }, 36 | { 37 | test: /\.js$/, 38 | exclude: /node_modules/, 39 | use: { 40 | loader: 'babel-loader', 41 | }, 42 | }, 43 | ], 44 | }, 45 | plugins: [ 46 | extractStyles, 47 | ], 48 | }; 49 | --------------------------------------------------------------------------------