├── .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 | 
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 |
47 |
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 |
--------------------------------------------------------------------------------