Skip to content

Instantly share code, notes, and snippets.

@Fyko
Forked from appellation/OAuth2.js
Created Mar 28, 2019
Embed
What would you like to do?
async (req, res) => {
const authorized = await oauth.token(req, res); // oauth is an instance of the OAuth2 class
const me = await rest(authorized.access_token, { tokenType: 'Bearer' }).get('/users/@me'); // rest just fetches the url
const [user] = await app.db.model('user').findCreateFind({
where: {},
include: [{
model: app.db.model('profile'),
where: {
id: me.id,
provider: 'DISCORD',
},
}],
});
await app.db.model('profile').upsert({
id: me.id,
userId: user.id,
provider: 'DISCORD',
accessToken: authorized.access_token,
refreshToken: authorized.refresh_token,
accessExpiresAt: Date.now() + (authorized.expires_in * 1000),
});
const token = await app.jwt.sign(user.id);
const redirect = req.cookies.get('redirect_to', { signed: true });
if (redirect) {
res.cookies.set('redirect_to');
res.cookies.set('authorization', token, { httpOnly: false });
res.statusCode = 301;
res.setHeader('Location', redirect);
} else {
res.setHeader('Content-Type', 'application/json');
res.write(JSON.stringify({ token }));
}
res.end();
};
const { randomBytes } = require('crypto');
const fetch = require('node-fetch');
const FormData = require('form-data');
const { parse } = require('url');
module.exports = class OAuth2 {
constructor({
endpoint,
clientID,
clientSecret,
responseType = 'code',
scopes = [],
redirectURI,
}) {
this.endpoint = endpoint;
this.clientID = clientID;
this.clientSecret = clientSecret;
this.responseType = responseType;
this.scopes = scopes;
this.redirectURI = redirectURI;
}
get authorizeURL() {
return `${this.endpoint}/authorize`;
}
get tokenURL() {
return `${this.endpoint}/token`;
}
get revokeURL() {
return `${this.endpoint}/token/revoke`;
}
clone() {
return new this.constructor({
endpoint: this.endpoint,
clientID: this.clientID,
clientSecret: this.clientSecret,
responseType: this.responseType,
scopes: this.scopes.slice(),
redirectURI: this.redirectURI,
});
}
generateState(cookies) {
const id = randomBytes(20).toString('hex');
cookies.set('state', id, { signed: true });
return id;
}
authorize() {
return (req, res) => {
/* eslint-disable camelcase */
const params = new URLSearchParams({
response_type: this.responseType,
scope: this.scopes.join(' '),
redirect_uri: this.redirectURI,
client_id: this.clientID,
state: this.generateState(res.cookies),
});
/* eslint-enable camelcase */
res.setHeader('Location', `${this.authorizeURL}?${params}`);
res.statusCode = 302;
res.end();
};
}
token(req, res, grantType = 'authorization_code') {
const url = parse(req.url, true);
const state = req.cookies.get('state', { signed: true });
if (!state || url.query.state !== state) {
res.statusCode = 401;
res.end('no or invalid state provided');
return;
}
const form = new FormData();
form.append('client_id', this.clientID);
form.append('client_secret', this.clientSecret);
form.append('grant_type', grantType);
form.append('code', url.query.code);
form.append('redirect_uri', this.redirectURI);
form.append('scope', this.scopes.join(' '));
return fetch(this.tokenURL, {
method: 'POST',
body: form,
}).then(r => r.json());
}
refresh(token) {
const form = new FormData();
form.append('client_id', this.clientID);
form.append('client_secret', this.clientSecret);
form.append('grant_type', 'refresh_token');
form.append('refresh_token', token);
form.append('redirect_uri', this.redirectURI);
form.append('scope', this.scopes.join(' '));
return fetch(this.tokenURL, {
method: 'POST',
body: form,
}).then(r => r.json());
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment